1
0
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:
Mert 2024-06-16 11:45:58 -04:00 committed by GitHub
parent 0b08af7082
commit e77e87b936
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 17 additions and 27 deletions

View File

@ -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[];

View File

@ -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,
}, },
}, },
); );

View File

@ -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;

View File

@ -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',