mirror of
https://github.com/immich-app/immich.git
synced 2024-11-28 09:33:27 +02:00
fix(server): orientation handling for person thumbnails (#10382)
fix orientation handling
This commit is contained in:
parent
0b08af7082
commit
e77e87b936
@ -47,6 +47,10 @@ export interface ImageDimensions {
|
|||||||
height: number;
|
height: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface InputDimensions extends ImageDimensions {
|
||||||
|
inputPath: string;
|
||||||
|
}
|
||||||
|
|
||||||
export interface VideoInfo {
|
export interface VideoInfo {
|
||||||
format: VideoFormat;
|
format: VideoFormat;
|
||||||
videoStreams: VideoStreamInfo[];
|
videoStreams: VideoStreamInfo[];
|
||||||
|
@ -917,9 +917,9 @@ describe(PersonService.name, () => {
|
|||||||
colorspace: Colorspace.P3,
|
colorspace: Colorspace.P3,
|
||||||
crop: {
|
crop: {
|
||||||
left: 0,
|
left: 0,
|
||||||
top: 428,
|
top: 85,
|
||||||
width: 1102,
|
width: 510,
|
||||||
height: 1102,
|
height: 510,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
@ -41,13 +41,12 @@ import {
|
|||||||
} from 'src/interfaces/job.interface';
|
} from 'src/interfaces/job.interface';
|
||||||
import { ILoggerRepository } from 'src/interfaces/logger.interface';
|
import { ILoggerRepository } from 'src/interfaces/logger.interface';
|
||||||
import { BoundingBox, IMachineLearningRepository } from 'src/interfaces/machine-learning.interface';
|
import { BoundingBox, IMachineLearningRepository } from 'src/interfaces/machine-learning.interface';
|
||||||
import { CropOptions, IMediaRepository, ImageDimensions } from 'src/interfaces/media.interface';
|
import { CropOptions, IMediaRepository, ImageDimensions, InputDimensions } from 'src/interfaces/media.interface';
|
||||||
import { IMoveRepository } from 'src/interfaces/move.interface';
|
import { IMoveRepository } from 'src/interfaces/move.interface';
|
||||||
import { IPersonRepository, UpdateFacesData } from 'src/interfaces/person.interface';
|
import { IPersonRepository, UpdateFacesData } from 'src/interfaces/person.interface';
|
||||||
import { ISearchRepository } from 'src/interfaces/search.interface';
|
import { ISearchRepository } from 'src/interfaces/search.interface';
|
||||||
import { IStorageRepository } from 'src/interfaces/storage.interface';
|
import { IStorageRepository } from 'src/interfaces/storage.interface';
|
||||||
import { ISystemMetadataRepository } from 'src/interfaces/system-metadata.interface';
|
import { ISystemMetadataRepository } from 'src/interfaces/system-metadata.interface';
|
||||||
import { Orientation } from 'src/services/metadata.service';
|
|
||||||
import { CacheControl, ImmichFileResponse } from 'src/utils/file';
|
import { CacheControl, ImmichFileResponse } from 'src/utils/file';
|
||||||
import { mimeTypes } from 'src/utils/mime-types';
|
import { mimeTypes } from 'src/utils/mime-types';
|
||||||
import { isFacialRecognitionEnabled } from 'src/utils/misc';
|
import { isFacialRecognitionEnabled } from 'src/utils/misc';
|
||||||
@ -520,7 +519,7 @@ export class PersonService {
|
|||||||
return JobStatus.FAILED;
|
return JobStatus.FAILED;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { width, height, inputPath } = await this.getInputDimensions(asset);
|
const { width, height, inputPath } = await this.getInputDimensions(asset, { width: oldWidth, height: oldHeight });
|
||||||
|
|
||||||
const thumbnailPath = StorageCore.getPersonThumbnailPath(person);
|
const thumbnailPath = StorageCore.getPersonThumbnailPath(person);
|
||||||
this.storageCore.ensureFolders(thumbnailPath);
|
this.storageCore.ensureFolders(thumbnailPath);
|
||||||
@ -601,7 +600,7 @@ export class PersonService {
|
|||||||
return person;
|
return person;
|
||||||
}
|
}
|
||||||
|
|
||||||
private async getInputDimensions(asset: AssetEntity): Promise<ImageDimensions & { inputPath: string }> {
|
private async getInputDimensions(asset: AssetEntity, oldDims: ImageDimensions): Promise<InputDimensions> {
|
||||||
if (!asset.exifInfo?.exifImageHeight || !asset.exifInfo.exifImageWidth) {
|
if (!asset.exifInfo?.exifImageHeight || !asset.exifInfo.exifImageWidth) {
|
||||||
throw new Error(`Asset ${asset.id} dimensions are unknown`);
|
throw new Error(`Asset ${asset.id} dimensions are unknown`);
|
||||||
}
|
}
|
||||||
@ -611,10 +610,11 @@ export class PersonService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (asset.type === AssetType.IMAGE) {
|
if (asset.type === AssetType.IMAGE) {
|
||||||
const { width, height } = this.withOrientation(asset.exifInfo.orientation as Orientation, {
|
let { exifImageWidth: width, exifImageHeight: height } = asset.exifInfo;
|
||||||
width: asset.exifInfo.exifImageWidth,
|
if (oldDims.height > oldDims.width !== height > width) {
|
||||||
height: asset.exifInfo.exifImageHeight,
|
[width, height] = [height, width];
|
||||||
});
|
}
|
||||||
|
|
||||||
return { width, height, inputPath: asset.originalPath };
|
return { width, height, inputPath: asset.originalPath };
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -622,20 +622,6 @@ export class PersonService {
|
|||||||
return { width, height, inputPath: asset.previewPath };
|
return { width, height, inputPath: asset.previewPath };
|
||||||
}
|
}
|
||||||
|
|
||||||
private withOrientation(orientation: Orientation, { width, height }: ImageDimensions): ImageDimensions {
|
|
||||||
switch (orientation) {
|
|
||||||
case Orientation.MirrorHorizontalRotate270CW:
|
|
||||||
case Orientation.Rotate90CW:
|
|
||||||
case Orientation.MirrorHorizontalRotate90CW:
|
|
||||||
case Orientation.Rotate270CW: {
|
|
||||||
return { width: height, height: width };
|
|
||||||
}
|
|
||||||
default: {
|
|
||||||
return { width, height };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private getCrop(dims: { old: ImageDimensions; new: ImageDimensions }, { x1, y1, x2, y2 }: BoundingBox): CropOptions {
|
private getCrop(dims: { old: ImageDimensions; new: ImageDimensions }, { x1, y1, x2, y2 }: BoundingBox): CropOptions {
|
||||||
const widthScale = dims.new.width / dims.old.width;
|
const widthScale = dims.new.width / dims.old.width;
|
||||||
const heightScale = dims.new.height / dims.old.height;
|
const heightScale = dims.new.height / dims.old.height;
|
||||||
|
4
server/test/fixtures/face.stub.ts
vendored
4
server/test/fixtures/face.stub.ts
vendored
@ -72,8 +72,8 @@ export const faceStub = {
|
|||||||
boundingBoxY1: 5,
|
boundingBoxY1: 5,
|
||||||
boundingBoxX2: 505,
|
boundingBoxX2: 505,
|
||||||
boundingBoxY2: 505,
|
boundingBoxY2: 505,
|
||||||
imageHeight: 1000,
|
imageHeight: 2880,
|
||||||
imageWidth: 1000,
|
imageWidth: 2160,
|
||||||
}),
|
}),
|
||||||
middle: Object.freeze<NonNullableProperty<AssetFaceEntity>>({
|
middle: Object.freeze<NonNullableProperty<AssetFaceEntity>>({
|
||||||
id: 'assetFaceId6',
|
id: 'assetFaceId6',
|
||||||
|
Loading…
Reference in New Issue
Block a user