raw: decode the preview already oriented and drop the second turn
Skia's Image.MakeImageFromEncoded applies the embedded JPEG's EXIF orientation itself. A Panasonic RW2 preview stored 1920x1280 with orientation 8 therefore decodes as 1280x1920 already — the frame the camera meant. previewGrid turned it a second time by its own per-orientation canvas rotation, so a portrait frame's reference grid met the develop a quarter turn out, previewMatch rejected the fit as folded and the RW2 opened with no tone curve at all (~9-13 levels off across the shadows). Drop the rotation and read the crop rectangle and aspect straight from bmp.width()/bmp.height(), the display dimensions Skia hands back. Panasonic RW2 now fits a real curve (dRGB 2.3,-0.0,0.4, was -9.1,-10.3, -13.2). The five bodies that were already stable — ORF, NEF, DNG, both RAF — measure exactly as before. getJpegOrientation had no other caller and goes with it; raw-orf-check drops its own byte-scanning preview finder, which crashed on DNG and RAF, and reads the reference through the shipped extractEmbeddedJpeg.
This commit is contained in:
@@ -100,7 +100,7 @@ writeFileSync(
|
||||
);
|
||||
writeFileSync(join(dir, 'librawNode.mjs'), LIBRAW_NODE_SRC);
|
||||
|
||||
const { developRaw } = await import(pathToFileURL(join(dir, 'rawDevelop.mjs')).href);
|
||||
const { developRaw, extractEmbeddedJpeg } = await import(pathToFileURL(join(dir, 'rawDevelop.mjs')).href);
|
||||
// `Skia` is a live binding filled in by initSkia, so read it after the call.
|
||||
const Skia = await (await import(pathToFileURL(join(dir, 'skiaShim.mjs')).href)).initSkia();
|
||||
|
||||
@@ -109,20 +109,11 @@ const file = process.argv[2] ?? '/home/locpham/RecipesCam/docker/_3279791.ORF';
|
||||
assert.ok(existsSync(file), `no such file: ${file}`);
|
||||
const bytes = new Uint8Array(readFileSync(file));
|
||||
|
||||
// The largest embedded JPEG in the file: the camera's own preview, which is both
|
||||
// the reference the curve is fitted to and what the eye calls correct here.
|
||||
function embeddedPreview(buf) {
|
||||
let best = null;
|
||||
for (let i = 0; i < buf.length - 1; i++) {
|
||||
if (buf[i] !== 0xff || buf[i + 1] !== 0xd8) continue;
|
||||
let j = i + 2;
|
||||
while (j < buf.length - 1 && !(buf[j] === 0xff && buf[j + 1] === 0xd9)) j++;
|
||||
if (j < buf.length - 1 && (!best || j - i > best[1] - best[0])) best = [i, j + 2];
|
||||
i = j + 2;
|
||||
}
|
||||
return best ? buf.subarray(best[0], best[1]) : null;
|
||||
}
|
||||
|
||||
// The preview the curve is fitted to is the one the develop itself reads —
|
||||
// `extractEmbeddedJpeg`, the same call the app makes. The byte-scanning copy that
|
||||
// stood here found a JPEG in an ORF and nothing in a DNG or a RAF (their previews
|
||||
// live behind a container this scanner's marker walk does not follow), so the
|
||||
// reference came back undecodable and the check died on those two bodies.
|
||||
const jpeg = await developRaw(bytes);
|
||||
const out = file.replace(/\.\w+$/, '') + '-develop-check.jpg';
|
||||
writeFileSync(out, jpeg);
|
||||
@@ -153,7 +144,7 @@ function readPixels(jpegBytes, w, h) {
|
||||
|
||||
const N = 256;
|
||||
const dev = readPixels(jpeg, N, N);
|
||||
const ref = readPixels(embeddedPreview(bytes), N, N);
|
||||
const ref = readPixels(extractEmbeddedJpeg(bytes), N, N);
|
||||
|
||||
// Bands by how bright the *camera's own* rendering says the block is: a neutral
|
||||
// render has both ratios at 1 there, a green/yellow veil pulls them apart.
|
||||
|
||||
Reference in New Issue
Block a user