Spaces:
Running
Running
File size: 2,712 Bytes
31c7d49 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 | import * as exifr from 'exifr'
import { DEFAULT_FOCAL_MM, FILM_35MM_DIAGONAL_MM } from './sharpConstants'
export type FocalSource = 'exif-35mm' | 'exif-mm-approx' | 'default-30mm'
export interface FocalEstimate {
focalPx: number
focalMmUsed: number
source: FocalSource
exif35mm?: number
exifFocalMm?: number
}
export function convertFocalMmToPx(width: number, height: number, focalMm: number): number {
return (focalMm * Math.hypot(width, height)) / FILM_35MM_DIAGONAL_MM
}
function parseFiniteNumber(value: unknown): number | undefined {
if (typeof value === 'number' && Number.isFinite(value)) {
return value
}
if (typeof value === 'string' && value.trim().length > 0) {
const parsed = Number(value)
if (Number.isFinite(parsed)) {
return parsed
}
}
if (value && typeof value === 'object') {
const maybeRational = value as { numerator?: unknown; denominator?: unknown }
const numerator = typeof maybeRational.numerator === 'number' ? maybeRational.numerator : undefined
const denominator = typeof maybeRational.denominator === 'number' ? maybeRational.denominator : undefined
if (
numerator !== undefined &&
denominator !== undefined &&
Number.isFinite(numerator) &&
Number.isFinite(denominator) &&
denominator !== 0
) {
return numerator / denominator
}
}
return undefined
}
export async function estimateFocalLengthFromFile(
file: File,
width: number,
height: number,
): Promise<FocalEstimate> {
let exifData: Record<string, unknown> | undefined
try {
exifData = (await exifr.parse(file, [
'FocalLengthIn35mmFilm',
'FocalLenIn35mmFilm',
'FocalLength',
])) as Record<string, unknown> | undefined
} catch {
exifData = undefined
}
const focal35mm =
parseFiniteNumber(exifData?.FocalLengthIn35mmFilm) ??
parseFiniteNumber(exifData?.FocalLenIn35mmFilm)
const focalMmExif = parseFiniteNumber(exifData?.FocalLength)
if (focal35mm !== undefined && focal35mm >= 1) {
return {
focalPx: convertFocalMmToPx(width, height, focal35mm),
focalMmUsed: focal35mm,
source: 'exif-35mm',
exif35mm: focal35mm,
exifFocalMm: focalMmExif,
}
}
if (focalMmExif !== undefined && focalMmExif > 0) {
const normalizedFocalMm = focalMmExif < 10 ? focalMmExif * 8.4 : focalMmExif
return {
focalPx: convertFocalMmToPx(width, height, normalizedFocalMm),
focalMmUsed: normalizedFocalMm,
source: 'exif-mm-approx',
exifFocalMm: focalMmExif,
}
}
return {
focalPx: convertFocalMmToPx(width, height, DEFAULT_FOCAL_MM),
focalMmUsed: DEFAULT_FOCAL_MM,
source: 'default-30mm',
}
}
|