/** @file GImage_bayer.cpp @author Morgan McGuire, http://graphics.cs.williams.edu @created 2002-05-27 @edited 2006-05-10 */ #include "G3D/platform.h" #include "G3D/GImage.h" namespace G3D { void GImage::BAYER_G8B8_R8G8_to_Quarter_R8G8B8(int width, int height, const uint8* in, uint8* out) { debugAssert(in != out); int halfHeight = height / 2; int halfWidth = width / 2; int dst_off = 0; for (int y = 0; y < halfHeight; ++y) { for (int x = 0; x < halfWidth; ++x) { // GBRG int src_off = x*2 + y*2*width; out[dst_off] = in[src_off+width]; // red out[dst_off+1] = ((int)in[src_off] + (int)in[src_off+width+1])/2; // green out[dst_off+2] = in[src_off+1]; // blue dst_off = dst_off + 3; } } } void GImage::Quarter_R8G8B8_to_BAYER_G8B8_R8G8(int inWidth, int inHeight, const uint8* in, uint8* out) { // Undo quarter-size Bayer as best we can. This code isn't very efficient, but it // also isn't used very frequently. debugAssert(out != in); int outWidth = 2 * inWidth; int outHeight = 2 * inHeight; for (int y = 0; y < outHeight; ++y) { for (int x = 0; x < outWidth; ++x) { const Color3uint8* inp = ((const Color3uint8*)in) + ((x/2) + (y/2)* inWidth); uint8* outp = out + x + y * outWidth; if (isEven(y)) { // GB row if (isEven(x)) { // Green *outp = inp->g; } else { // Blue *outp = inp->b; } } else { // RG row if (isEven(x)) { // Red *outp = inp->r; } else { // Green *outp = inp->g; } } } } } /** Applies a 5x5 filter to monochrome image I (wrapping at the boundaries) */ static uint8 applyFilter( const uint8* I, int x, int y, int w, int h, const float filter[5][5]) { debugAssert(isEven(w)); debugAssert(isEven(h)); float sum = 0.0f; float denom = 0.0f; for (int dy = 0; dy < 5; ++dy) { int offset = ((y + dy + h - 2) % h) * w; for (int dx = 0; dx < 5; ++dx) { float f = filter[dy][dx]; sum += f * I[((x + dx + w - 2) % w) + offset]; denom += f; } } return (uint8)iClamp(iRound(sum / denom), 0, 255); } //////////////////////////////////////////////////////////////////////////////////////////////// // // Bayer conversions // // There are two kinds of rows (GR and BG). // In each row, there are two kinds of pixels (G/R, B/G). // We express the four kinds of INPUT pixels as: // GRG, GRG, BGB, BGG // // There are three kinds of OUTPUT pixels: R, G, B. // Thus there are nominally 12 different I/O combinations, // but several are impulses because needed output at that // location *is* the input (e.g., G_GRG and G_BGG). // // The following 5x5 row-major filters are named as output_input. // Green static const float G_GRR[5][5] = {{ 0.0f, 0.0f, -1.0f, 0.0f, 0.0f}, { 0.0f, 0.0f, 2.0f, 0.0f, 0.0f}, { -1.0f, 2.0f, 4.0f, 2.0f, -1.0f}, { 0.0f, 0.0f, 2.0f, 0.0f, 0.0f}, { 0.0f, 0.0f, -1.0f, 0.0f, 0.0f}}; static const float G_BGB[5][5] = {{ 0.0f, 0.0f, -1.0f, 0.0f, 0.0f}, { 0.0f, 0.0f, 2.0f, 0.0f, 0.0f}, { -1.0f, 2.0f, 4.0f, 2.0f, -1.0f}, { 0.0f, 0.0f, 2.0f, 0.0f, 0.0f}, { 0.0f, 0.0f, -1.0f, 0.0f, 0.0f}}; // Red //(the caption in the paper is wrong for this case: // "R row B column really means R row G column" static const float R_GRG[5][5] = {{ 0.0f, 0.0f, 0.5f, 0.0f, 0.0f}, { 0.0f, -1.0f, 0.0f, -1.0f, 0.0f}, { -1.0f, 4.0f, 5.0f, 4.0f, -1.0f}, { 0.0f, -1.0f, 0.0f, -1.0f, 0.0f}, { 0.0f, 0.0f, 0.5f, 0.0f, 0.0f}}; static const float R_BGG[5][5] = {{ 0.0f, 0.0f, -1.0f, 0.0f, 0.0f}, { 0.0f, -1.0f, 4.0f, -1.0f, 0.0f}, { 0.5f, 0.0f, 5.0f, 0.0f, 0.5f}, { 0.0f, -1.0f, 4.0f, -1.0f, 0.0f}, { 0.0f, 0.0f, -1.0f, 0.0f, 0.0f}}; static const float R_BGB[5][5] = {{ 0.0f, 0.0f, -3.0f/2.0f, 0.0f, 0.0f}, { 0.0f, 2.0f, 0.0f, 2.0f, 0.0f}, {-3.0f/2.0f, 0.0f, 6.0f, 0.0f, -3.0f/2.0f}, { 0.0f, 2.0f, 0.0f, 2.0f, 0.0f}, { 0.0f, 0.0f, -3.0f/2.0f, 0.0f, 0.0f}}; // Blue //(the caption in the paper is wrong for this case: // "B row R column really means B row G column") #define B_BGG R_GRG #define B_GRG R_BGG #define B_GRR R_BGB void GImage::BAYER_R8G8_G8B8_to_R8G8B8_MHC(int w, int h, const uint8* in, uint8* _out) { debugAssert(in != _out); Color3uint8* out = (Color3uint8*)_out; for (int y = 0; y < h; ++y) { // Row beginning in the input array. int offset = y * w; // RG row for (int x = 0; x < w; ++x, ++out) { // R pixel { out->r = in[x + offset]; out->g = applyFilter(in, x, y, w, h, G_GRR); out->b = applyFilter(in, x, y, w, h, B_GRR); } ++x; ++out; // G pixel { out->r = applyFilter(in, x, y, w, h, R_GRG); out->g = in[x + offset]; out->b = applyFilter(in, x, y, w, h, B_GRG); } } ++y; offset += w; // GB row for (int x = 0; x < w; ++x, ++out) { // G pixel { out->r = applyFilter(in, x, y, w, h, R_BGG); out->g = in[x + offset]; out->b = applyFilter(in, x, y, w, h, B_BGG); } ++x; ++out; // B pixel { out->r = applyFilter(in, x, y, w, h, R_BGB); out->g = applyFilter(in, x, y, w, h, G_BGB); out->b = in[x + offset]; } } } } static void swapRedAndBlue(int N, Color3uint8* out) { for (int i = N - 1; i >= 0; --i) { uint8 tmp = out[i].r; out[i].r = out[i].b; out[i].b = tmp; } } void GImage::BAYER_G8R8_B8G8_to_R8G8B8_MHC(int w, int h, const uint8* in, uint8* _out) { // Run the equivalent function for red BAYER_G8B8_R8G8_to_R8G8B8_MHC(w, h, in, _out); // Now swap red and blue swapRedAndBlue(w * h, (Color3uint8*)_out); } void GImage::BAYER_B8G8_G8R8_to_R8G8B8_MHC(int w, int h, const uint8* in, uint8* _out) { // Run the equivalent function for red BAYER_R8G8_G8B8_to_R8G8B8_MHC(w, h, in, _out); // Now swap red and blue swapRedAndBlue(w * h, (Color3uint8*)_out); } void GImage::BAYER_G8B8_R8G8_to_R8G8B8_MHC(int w, int h, const uint8* in, uint8* _out) { debugAssert(in != _out); Color3uint8* out = (Color3uint8*)_out; for (int y = 0; y < h; ++y) { // Row beginning in the input array. int offset = y * w; // GB row for (int x = 0; x < w; ++x, ++out) { // G pixel { out->r = applyFilter(in, x, y, w, h, R_BGG); out->g = in[x + offset]; out->b = applyFilter(in, x, y, w, h, B_BGG); } ++x; ++out; // B pixel { out->r = applyFilter(in, x, y, w, h, R_BGB); out->g = applyFilter(in, x, y, w, h, G_BGB); out->b = in[x + offset]; } } ++y; offset += w; // RG row for (int x = 0; x < w; ++x, ++out) { // R pixel { out->r = in[x + offset]; out->g = applyFilter(in, x, y, w, h, G_GRR); out->b = applyFilter(in, x, y, w, h, B_GRR); } ++x; ++out; // G pixel { out->r = applyFilter(in, x, y, w, h, R_GRG); out->g = in[x + offset]; out->b = applyFilter(in, x, y, w, h, B_GRG); } } } } #undef B_BGG #undef B_GRG #undef B_GRR }