Last active
February 17, 2020 14:04
-
-
Save stengoes/3076aea7ff24a183402764ec3984784b to your computer and use it in GitHub Desktop.
Demosaic images with Uint8ClampedArray
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
function demosaic(raw) { | |
// Demosaics a Color Filter Array (CFA) into a RGBA color array. | |
// For implementation details see: http://research.microsoft.com/pubs/102068/demosaicing_icassp04.pdf | |
// Validate inputs | |
const raw_size = raw.height * raw.width * raw.depth; | |
if (raw.data.length != raw_size) { | |
console.log("Error: raw image dimensions do not match raw buffer size!"); | |
return null; | |
} | |
if (raw.height <= 4 || raw.width <= 4) { | |
console.log("Error: raw image dimensions are too small!"); | |
return null; | |
} | |
if (raw.depth != 1) { | |
console.log("Error: raw image should have a depth of 1!"); | |
return null; | |
} | |
if (raw.data.constructor !== Uint8ClampedArray) { | |
console.log("Error: raw image data should be of type Uint8ClampedArray!"); | |
return null; | |
} | |
// Init rgb image | |
const rgb = { | |
height: (raw.height - 4), | |
width: (raw.width - 4), | |
depth: 4, | |
data: new Uint8ClampedArray((raw.height - 4) * (raw.width - 4) * 4) | |
} | |
// Helper functions | |
function raw_idx(y, x) { | |
return 2 * raw.width + 2 + y * raw.width + x; | |
} | |
// 4 Different spatial filters | |
const F1 = function (y, x) { | |
let p1 = raw_idx(y - 2, x); | |
let p2 = raw_idx(y, x - 2); | |
let p3 = raw_idx(y, x + 2) | |
let p4 = raw_idx(y + 2, x); | |
let p5 = raw_idx(y - 1, x); | |
let p6 = raw_idx(y, x - 1); | |
let p7 = raw_idx(y, x + 1); | |
let p8 = raw_idx(y + 1, x); | |
let p9 = raw_idx(y, x); | |
return () => { | |
const val = ( | |
-2 * (raw.data[p1] + raw.data[p2] + raw.data[p3] + raw.data[p4]) | |
+ 4 * (raw.data[p5] + raw.data[p6] + raw.data[p7] + raw.data[p8]) | |
+ 8 * raw.data[p9] | |
) / 16; | |
p1 += 2; | |
p2 += 2; | |
p3 += 2; | |
p4 += 2; | |
p5 += 2; | |
p6 += 2; | |
p7 += 2; | |
p8 += 2; | |
p9 += 2; | |
return val; | |
}; | |
} | |
const F2 = function (y, x) { | |
let p1 = raw_idx(y - 2, x); | |
let p2 = raw_idx(y + 2, x); | |
let p3 = raw_idx(y - 1, x - 1); | |
let p4 = raw_idx(y - 1, x + 1); | |
let p5 = raw_idx(y, x - 2); | |
let p6 = raw_idx(y, x + 2); | |
let p7 = raw_idx(y + 1, x - 1); | |
let p8 = raw_idx(y + 1, x + 1); | |
let p9 = raw_idx(y, x - 1); | |
let p10 = raw_idx(y, x + 1); | |
let p11 = raw_idx(y, x); | |
return () => { | |
const val = ( | |
1 * (raw.data[p1] + raw.data[p2]) | |
- 2 * (raw.data[p3] + raw.data[p4] + raw.data[p5] + raw.data[p6] + raw.data[p7] + raw.data[p8]) | |
+ 8 * (raw.data[p9] + raw.data[p10]) | |
+ 10 * raw.data[p11] | |
) / 16; | |
p1 += 2; | |
p2 += 2; | |
p3 += 2; | |
p4 += 2; | |
p5 += 2; | |
p6 += 2; | |
p7 += 2; | |
p8 += 2; | |
p9 += 2; | |
p10 += 2; | |
p11 += 2; | |
return val; | |
}; | |
} | |
const F3 = function (y, x) { | |
let p1 = raw_idx(y, x - 2); | |
let p2 = raw_idx(y, x + 2); | |
let p3 = raw_idx(y - 2, x); | |
let p4 = raw_idx(y - 1, x - 1); | |
let p5 = raw_idx(y - 1, x + 1); | |
let p6 = raw_idx(y + 1, x - 1); | |
let p7 = raw_idx(y + 1, x + 1); | |
let p8 = raw_idx(y + 2, x); | |
let p9 = raw_idx(y - 1, x); | |
let p10 = raw_idx(y + 1, x); | |
let p11 = raw_idx(y, x); | |
return () => { | |
const val = ( | |
1 * (raw.data[p1] + raw.data[p2]) | |
- 2 * (raw.data[p3] + raw.data[p4] + raw.data[p5] + raw.data[p6] + raw.data[p7] + raw.data[p8]) | |
+ 8 * (raw.data[p9] + raw.data[p10]) | |
+ 10 * raw.data[p11] | |
) / 16; | |
p1 += 2; | |
p2 += 2; | |
p3 += 2; | |
p4 += 2; | |
p5 += 2; | |
p6 += 2; | |
p7 += 2; | |
p8 += 2; | |
p9 += 2; | |
p10 += 2; | |
p11 += 2; | |
return val; | |
}; | |
} | |
const F4 = function (y, x) { | |
let p1 = raw_idx(y - 2, x); | |
let p2 = raw_idx(y, x - 2); | |
let p3 = raw_idx(y, x + 2); | |
let p4 = raw_idx(y + 2, x); | |
let p5 = raw_idx(y - 1, x - 1); | |
let p6 = raw_idx(y - 1, x + 1); | |
let p7 = raw_idx(y + 1, x - 1) | |
let p8 = raw_idx(y + 1, x + 1); | |
let p9 = raw_idx(y, x); | |
return () => { | |
const val = ( | |
-3 * (raw.data[p1] + raw.data[p2] + raw.data[p3] + raw.data[p4]) | |
+ 4 * (raw.data[p5] + raw.data[p6] + raw.data[p7] + raw.data[p8]) | |
+ 12 * raw.data[p9] | |
) / 16; | |
p1 += 2; | |
p2 += 2; | |
p3 += 2; | |
p4 += 2; | |
p5 += 2; | |
p6 += 2; | |
p7 += 2; | |
p8 += 2; | |
p9 += 2; | |
return val; | |
}; | |
} | |
// Loop over the rgb image | |
let idx_rgb = 0; | |
let idx_raw = 2 * raw.width; | |
// Keep track of row location in color filter | |
let R_row = true; | |
for (let y = 0; y < rgb.height; y++) { | |
// Update raw index | |
idx_raw += 2; | |
// Keep track of col location in color filter | |
let B_col = false; | |
// Color filter | |
// [ | |
// [R, G], | |
// [G, B] | |
// ] | |
// Handle each location in the color filter differently | |
if (R_row) | |
{ | |
// Initilize filters at start of the row | |
const GatR = F1(y, 0); | |
const BatR = F4(y, 0); | |
const RatGinRrow = F2(y, 1); | |
const BatGinRrow = F3(y, 1); | |
for (let x = 0; x < rgb.width; x++) | |
{ | |
if(!B_col) | |
{ | |
// R | |
rgb.data[idx_rgb++] = raw.data[idx_raw++]; // R at R | |
rgb.data[idx_rgb++] = GatR(); // G at R | |
rgb.data[idx_rgb++] = BatR(); // B at R | |
rgb.data[idx_rgb++] = 255; // A | |
} | |
else | |
{ | |
// G in R row | |
rgb.data[idx_rgb++] = RatGinRrow(); // R at G in R row | |
rgb.data[idx_rgb++] = raw.data[idx_raw++]; // G at G | |
rgb.data[idx_rgb++] = BatGinRrow(); // B at G in R row | |
rgb.data[idx_rgb++] = 255; // A | |
} | |
// Update col location | |
B_col = !B_col; | |
} | |
} | |
else | |
{ | |
// Initilize filters at start of the row | |
const BatGinBrow = F2(y, 0); | |
const RatGinBrow = F3(y, 0); | |
const RatB = F4(y, 1); | |
const GatB = F1(y, 1); | |
for (let x = 0; x < rgb.width; x++) | |
{ | |
if(!B_col) | |
{ | |
// G in B row | |
rgb.data[idx_rgb++] = RatGinBrow(); // R at G in B row | |
rgb.data[idx_rgb++] = raw.data[idx_raw++]; // G at G | |
rgb.data[idx_rgb++] = BatGinBrow(); // B at G in B row | |
rgb.data[idx_rgb++] = 255; // A | |
} | |
else | |
{ | |
// B | |
rgb.data[idx_rgb++] = RatB(); // R at B | |
rgb.data[idx_rgb++] = GatB(); // G at B | |
rgb.data[idx_rgb++] = raw.data[idx_raw++]; // B at B | |
rgb.data[idx_rgb++] = 255; // A | |
} | |
// Update col location | |
B_col = !B_col; | |
} | |
} | |
// Update row location | |
R_row = !R_row; | |
// Update raw index | |
idx_raw += 2; | |
} | |
return rgb; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment