From 7cf1c591d61f7bdba4e99cbeb4c5d8f26f3dd9d0 Mon Sep 17 00:00:00 2001 From: Carsten Otto Date: Fri, 11 Oct 2024 17:06:47 +0200 Subject: [PATCH] fix(server): prefer ImageSize over ImageWidth/Height metadata For some RAW formats/cameras, the ImageWidth/ImageHeight information represents the dimensions of the preview image, i.e. much smaller than the actual image. This also causes issues with (face/people) thumbnail generation. The information in ImageSize seems to be correct, so we use this instead. fixes #13049 --- e2e/src/api/specs/asset.e2e-spec.ts | 24 ++++++++++++++++++++ e2e/test-assets | 2 +- server/src/services/metadata.service.ts | 29 +++++++++++++++++++++++-- 3 files changed, 52 insertions(+), 3 deletions(-) diff --git a/e2e/src/api/specs/asset.e2e-spec.ts b/e2e/src/api/specs/asset.e2e-spec.ts index 4dd02ec69fc3dc..bfc174ff391ad7 100644 --- a/e2e/src/api/specs/asset.e2e-spec.ts +++ b/e2e/src/api/specs/asset.e2e-spec.ts @@ -1148,6 +1148,30 @@ describe('/asset', () => { }, }, }, + { + input: 'formats/raw/Canon/PowerShot_G12.CR2', + expected: { + type: AssetTypeEnum.Image, + originalFileName: 'PowerShot_G12.CR2', + fileCreatedAt: '2015-12-27T09:55:40.000Z', + exifInfo: { + make: 'Canon', + model: 'Canon PowerShot G12', + exifImageHeight: 2736, + exifImageWidth: 3648, + exposureTime: '1/1000', + fNumber: 4, + focalLength: 18.098, + iso: 80, + lensModel: null, + fileSizeInByte: 11113617, + dateTimeOriginal: '2015-12-27T09:55:40.000Z', + latitude: null, + longitude: null, + orientation: '1', + }, + }, + } ]; it(`should upload and generate a thumbnail for different file types`, async () => { diff --git a/e2e/test-assets b/e2e/test-assets index 3e057d2f58750a..ee86eb77a4473d 160000 --- a/e2e/test-assets +++ b/e2e/test-assets @@ -1 +1 @@ -Subproject commit 3e057d2f58750acdf7ff281a3938e34a86cfef4d +Subproject commit ee86eb77a4473de32c89e6ba03aa648a9d676cbf diff --git a/server/src/services/metadata.service.ts b/server/src/services/metadata.service.ts index a81d1b4904c275..fee4f36418746d 100644 --- a/server/src/services/metadata.service.ts +++ b/server/src/services/metadata.service.ts @@ -192,6 +192,8 @@ export class MetadataService extends BaseService { const { dateTimeOriginal, localDateTime, timeZone, modifyDate } = this.getDates(asset, exifTags); const { latitude, longitude, country, state, city } = await this.getGeo(exifTags, reverseGeocoding); + let imageDimensions = this.getImageDimensions(exifTags); + const exifData: Partial = { assetId: asset.id, @@ -209,8 +211,8 @@ export class MetadataService extends BaseService { // image/file fileSizeInByte: stats.size, - exifImageHeight: validate(exifTags.ImageHeight), - exifImageWidth: validate(exifTags.ImageWidth), + exifImageHeight: validate(imageDimensions.height), + exifImageWidth: validate(imageDimensions.width), orientation: validate(exifTags.Orientation)?.toString() ?? null, projectionType: exifTags.ProjectionType ? String(exifTags.ProjectionType).toUpperCase() : null, bitsPerSample: this.getBitsPerSample(exifTags), @@ -334,6 +336,29 @@ export class MetadataService extends BaseService { return JobStatus.SUCCESS; } + private getImageDimensions(exifTags: ImmichTags): {width: number, height: number} { + /* + * The "true" values for width and height are a bit hidden, depending on the camera model and file format. + * For RAW images in the CR2 or RAF format, the "ImageSize" value seems to be correct, + * but ImageWidth and ImageHeight are not correct (they contain the dimensions of the preview image). + */ + + let width = NaN; + let height = NaN; + let imageSize = exifTags.ImageSize; + if (imageSize && imageSize.indexOf('x') > 0) { + // width x height (e.g. 100x200) + let split = imageSize.split('x'); + width = parseInt(split[0]); + height = parseInt(split[1]); + } + if (!width || !height) { + width = exifTags.ImageWidth ?? NaN; + height = exifTags.ImageHeight ?? NaN; + } + return {width, height}; + } + private async getExifTags(asset: AssetEntity): Promise { const mediaTags = await this.metadataRepository.readTags(asset.originalPath); const sidecarTags = asset.sidecarPath ? await this.metadataRepository.readTags(asset.sidecarPath) : {};