mirror of
https://github.com/KevinMidboe/immich.git
synced 2026-01-13 20:55:54 +00:00
fix(server): use srgb pipeline for srgb images (#4101)
* added color-related exif fields * remove metadata check, conditional pipe colorspace * check exif metadata for srgb * added migration * updated e2e fixture * uncased srgb check, search substrings * extracted exif logic into separate function * handle images with no bit depth or color metadata * added unit tests
This commit is contained in:
@@ -81,6 +81,15 @@ export class ExifEntity {
|
||||
@Column({ type: 'varchar', nullable: true })
|
||||
exposureTime!: string | null;
|
||||
|
||||
@Column({ type: 'varchar', nullable: true })
|
||||
profileDescription!: string | null;
|
||||
|
||||
@Column({ type: 'varchar', nullable: true })
|
||||
colorspace!: string | null;
|
||||
|
||||
@Column({ type: 'integer', nullable: true })
|
||||
bitsPerSample!: number | null;
|
||||
|
||||
/* Video info */
|
||||
@Column({ type: 'float8', nullable: true })
|
||||
fps?: number | null;
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
import { MigrationInterface, QueryRunner } from "typeorm";
|
||||
|
||||
export class AddExifColorSpace1694750975773 implements MigrationInterface {
|
||||
name = 'AddExifColorSpace1694750975773'
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`ALTER TABLE "exif" ADD "profileDescription" character varying`);
|
||||
await queryRunner.query(`ALTER TABLE "exif" ADD "colorspace" character varying`);
|
||||
await queryRunner.query(`ALTER TABLE "exif" ADD "bitsPerSample" integer`);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`ALTER TABLE "exif" DROP COLUMN "bitsPerSample"`);
|
||||
await queryRunner.query(`ALTER TABLE "exif" DROP COLUMN "colorspace"`);
|
||||
await queryRunner.query(`ALTER TABLE "exif" DROP COLUMN "profileDescription"`);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -26,24 +26,12 @@ export class MediaRepository implements IMediaRepository {
|
||||
}
|
||||
|
||||
async resize(input: string | Buffer, output: string, options: ResizeOptions): Promise<void> {
|
||||
let colorProfile = options.colorspace;
|
||||
if (options.colorspace !== Colorspace.SRGB) {
|
||||
try {
|
||||
const { space } = await sharp(input).metadata();
|
||||
// if the image is already in srgb, keep it that way
|
||||
if (space === 'srgb') {
|
||||
colorProfile = Colorspace.SRGB;
|
||||
}
|
||||
} catch (err) {
|
||||
this.logger.warn(`Could not determine colorspace of image, defaulting to ${colorProfile} profile`);
|
||||
}
|
||||
}
|
||||
const chromaSubsampling = options.quality >= 80 ? '4:4:4' : '4:2:0'; // this is default in libvips (except the threshold is 90), but we need to set it manually in sharp
|
||||
await sharp(input, { failOn: 'none' })
|
||||
.pipelineColorspace('rgb16')
|
||||
.pipelineColorspace(options.colorspace === Colorspace.SRGB ? 'srgb' : 'rgb16')
|
||||
.resize(options.size, options.size, { fit: 'outside', withoutEnlargement: true })
|
||||
.rotate()
|
||||
.withMetadata({ icc: colorProfile })
|
||||
.withMetadata({ icc: options.colorspace })
|
||||
.toFormat(options.format, { quality: options.quality, chromaSubsampling })
|
||||
.toFile(output);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user