Skip to content

Commit

Permalink
Merge pull request #1011 from jboolean/image-levels
Browse files Browse the repository at this point in the history
Apply normalization when converting images
  • Loading branch information
jboolean authored Jun 29, 2024
2 parents ff7bfed + 2eaf7b5 commit 1296709
Show file tree
Hide file tree
Showing 3 changed files with 62 additions and 1 deletion.
6 changes: 5 additions & 1 deletion backend/convertImage.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import sharp from 'sharp';
import AWS from 'aws-sdk';
import sharp from 'sharp';

import * as ImageAdjustUtils from './src/image-processing/ImageAdjustUtils';
import * as LaserdiscUtils from './src/image-processing/LaserdiscUtils';

const s3 = new AWS.S3();
Expand Down Expand Up @@ -51,6 +52,9 @@ export const handler = async (event): Promise<unknown> => {
inputBuffer = await LaserdiscUtils.cropVideoFrame(inputBuffer);
}

// Perform level normalization
inputBuffer = await ImageAdjustUtils.adjustLevels(inputBuffer);

return Promise.all([
// webp doesn't look that good, sorry webp
// sharp(inputBuffer)
Expand Down
2 changes: 2 additions & 0 deletions backend/serverless.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ functions:
stages:
- production
handler: convertImage.handler
memorySize: 4096
timeout: 30
events:
- s3:
bucket: ${self:custom.bucket}
Expand Down
55 changes: 55 additions & 0 deletions backend/src/image-processing/ImageAdjustUtils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import sharp from 'sharp';

type SharpInput = Parameters<typeof sharp>[0];

const ROI_PCT = 0.9;

const SAMPLE_WIDTH = 560;

const BLACK_PERCENTILE = 1;
const WHITE_PERCENTILE = 99;

const percentile = (values: Uint8ClampedArray, p: number): number => {
const index = (p / 100) * (values.length - 1);
const lower = Math.floor(index);
const upper = Math.ceil(index);
return values[lower] + (values[upper] - values[lower]) * (index - lower);
};

// Normalize levels based on center of image, to ignore junk on the sides
export async function adjustLevels(input: SharpInput): Promise<Buffer> {
const meta = await sharp(input).metadata();

const { width, height } = meta;

if (!width || !height) {
console.warn('Image has no dimensions');
return sharp(input).toBuffer();
}

const roi = {
left: Math.floor((width * (1 - ROI_PCT)) / 2),
top: Math.floor((height * (1 - ROI_PCT)) / 2),
width: Math.floor(width * ROI_PCT),
height: Math.floor(height * ROI_PCT),
};

const roiBuffer = await sharp(input)
.extract(roi)
.resize(SAMPLE_WIDTH)
.raw()
.toBuffer();

// Calculate black and white points based on percentiles

const pixelValues = new Uint8ClampedArray(roiBuffer);
pixelValues.sort((a, b) => a - b);

const blackPoint = percentile(pixelValues, BLACK_PERCENTILE);
const whitePoint = percentile(pixelValues, WHITE_PERCENTILE);

const scale = 255 / (whitePoint - blackPoint);
const offset = -blackPoint * scale;

return sharp(input).linear(scale, offset).toBuffer();
}

0 comments on commit 1296709

Please sign in to comment.