299 lines
8.4 KiB
C++
299 lines
8.4 KiB
C++
/**
|
|
@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
|
|
|
|
}
|