diff --git a/CMakeLists.txt b/CMakeLists.txt index 790bf0c..66f18fc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,5 +15,5 @@ LINK_DIRECTORIES(${GTK3_LIBRARY_DIRS}) # Add other flags to the compiler ADD_DEFINITIONS(${GTK3_CFLAGS_OTHER}) -add_executable(GTKCamera main.c ini.c ini.h) -target_link_libraries(GTKCamera ${GTK3_LIBRARIES}) \ No newline at end of file +add_executable(GTKCamera main.c ini.c ini.h bayer.c bayer.h) +target_link_libraries(GTKCamera ${GTK3_LIBRARIES}) diff --git a/bayer.c b/bayer.c new file mode 100644 index 0000000..9f889e4 --- /dev/null +++ b/bayer.c @@ -0,0 +1,2187 @@ +/* + * 1394-Based Digital Camera Control Library + * + * Bayer pattern decoding functions + * + * Written by Damien Douxchamps and Frederic Devernay + * The original VNG and AHD Bayer decoding are from Dave Coffin's DCRAW. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +//#include "conversions.h" +#include "bayer.h" + + +#define CLIP(in, out)\ + in = in < 0 ? 0 : in;\ + in = in > 255 ? 255 : in;\ + out=in; + +#define CLIP16(in, out, bits)\ + in = in < 0 ? 0 : in;\ + in = in > ((1<= 0) { + rgb[i--] = 0; + rgb[j--] = 0; + } + + int low = sx * (w - 1) * 3 - 1 + w * 3; + i = low + sx * (sy - w * 2 + 1) * 3; + while (i > low) { + j = 6 * w; + while (j > 0) { + rgb[i--] = 0; + j--; + } + i -= (sx - 2 * w) * 3; + } +} + +void +ClearBorders_uint16(uint16_t * rgb, int sx, int sy, int w) +{ + int i, j; + + // black edges: + i = 3 * sx * w - 1; + j = 3 * sx * sy - 1; + while (i >= 0) { + rgb[i--] = 0; + rgb[j--] = 0; + } + + int low = sx * (w - 1) * 3 - 1 + w * 3; + i = low + sx * (sy - w * 2 + 1) * 3; + while (i > low) { + j = 6 * w; + while (j > 0) { + rgb[i--] = 0; + j--; + } + i -= (sx - 2 * w) * 3; + } + +} + +/************************************************************** + * Color conversion functions for cameras that can * + * output raw-Bayer pattern images, such as some Basler and * + * Point Grey camera. Most of the algos presented here come * + * from http://www-ise.stanford.edu/~tingchen/ and have been * + * converted from Matlab to C and extended to all elementary * + * patterns. * + **************************************************************/ + +/* 8-bits versions */ +/* insprired by OpenCV's Bayer decoding */ + +dc1394error_t +dc1394_bayer_NearestNeighbor(const uint8_t *restrict bayer, uint8_t *restrict rgb, int sx, int sy, int tile) +{ + const int bayerStep = sx; + const int rgbStep = 3 * sx; + int width = sx; + int height = sy; + int blue = tile == DC1394_COLOR_FILTER_BGGR + || tile == DC1394_COLOR_FILTER_GBRG ? -1 : 1; + int start_with_green = tile == DC1394_COLOR_FILTER_GBRG + || tile == DC1394_COLOR_FILTER_GRBG; + int i, imax, iinc; + + if ((tile>DC1394_COLOR_FILTER_MAX)||(tile 0) { + for (; bayer <= bayerEnd - 2; bayer += 2, rgb += 6) { + rgb[-1] = bayer[0]; + rgb[0] = bayer[1]; + rgb[1] = bayer[bayerStep + 1]; + + rgb[2] = bayer[2]; + rgb[3] = bayer[bayerStep + 2]; + rgb[4] = bayer[bayerStep + 1]; + } + } else { + for (; bayer <= bayerEnd - 2; bayer += 2, rgb += 6) { + rgb[1] = bayer[0]; + rgb[0] = bayer[1]; + rgb[-1] = bayer[bayerStep + 1]; + + rgb[4] = bayer[2]; + rgb[3] = bayer[bayerStep + 2]; + rgb[2] = bayer[bayerStep + 1]; + } + } + + if (bayer < bayerEnd) { + rgb[-blue] = bayer[0]; + rgb[0] = bayer[1]; + rgb[blue] = bayer[bayerStep + 1]; + bayer++; + rgb += 3; + } + + bayer -= width; + rgb -= width * 3; + + blue = -blue; + start_with_green = !start_with_green; + } + + return DC1394_SUCCESS; +} + +/* OpenCV's Bayer decoding */ +dc1394error_t +dc1394_bayer_Bilinear(const uint8_t *restrict bayer, uint8_t *restrict rgb, int sx, int sy, int tile) +{ + const int bayerStep = sx; + const int rgbStep = 3 * sx; + int width = sx; + int height = sy; + /* + the two letters of the OpenCV name are respectively + the 4th and 3rd letters from the blinky name, + and we also have to switch R and B (OpenCV is BGR) + + CV_BayerBG2BGR <-> DC1394_COLOR_FILTER_BGGR + CV_BayerGB2BGR <-> DC1394_COLOR_FILTER_GBRG + CV_BayerGR2BGR <-> DC1394_COLOR_FILTER_GRBG + + int blue = tile == CV_BayerBG2BGR || tile == CV_BayerGB2BGR ? -1 : 1; + int start_with_green = tile == CV_BayerGB2BGR || tile == CV_BayerGR2BGR; + */ + int blue = tile == DC1394_COLOR_FILTER_BGGR + || tile == DC1394_COLOR_FILTER_GBRG ? -1 : 1; + int start_with_green = tile == DC1394_COLOR_FILTER_GBRG + || tile == DC1394_COLOR_FILTER_GRBG; + + if ((tile>DC1394_COLOR_FILTER_MAX)||(tile> 1; */ + t0 = (bayer[1] + bayer[bayerStep * 2 + 1] + 1) >> 1; + t1 = (bayer[bayerStep] + bayer[bayerStep + 2] + 1) >> 1; + rgb[-blue] = (uint8_t) t0; + rgb[0] = bayer[bayerStep + 1]; + rgb[blue] = (uint8_t) t1; + bayer++; + rgb += 3; + } + + if (blue > 0) { + for (; bayer <= bayerEnd - 2; bayer += 2, rgb += 6) { + t0 = (bayer[0] + bayer[2] + bayer[bayerStep * 2] + + bayer[bayerStep * 2 + 2] + 2) >> 2; + t1 = (bayer[1] + bayer[bayerStep] + + bayer[bayerStep + 2] + bayer[bayerStep * 2 + 1] + + 2) >> 2; + rgb[-1] = (uint8_t) t0; + rgb[0] = (uint8_t) t1; + rgb[1] = bayer[bayerStep + 1]; + + t0 = (bayer[2] + bayer[bayerStep * 2 + 2] + 1) >> 1; + t1 = (bayer[bayerStep + 1] + bayer[bayerStep + 3] + + 1) >> 1; + rgb[2] = (uint8_t) t0; + rgb[3] = bayer[bayerStep + 2]; + rgb[4] = (uint8_t) t1; + } + } else { + for (; bayer <= bayerEnd - 2; bayer += 2, rgb += 6) { + t0 = (bayer[0] + bayer[2] + bayer[bayerStep * 2] + + bayer[bayerStep * 2 + 2] + 2) >> 2; + t1 = (bayer[1] + bayer[bayerStep] + + bayer[bayerStep + 2] + bayer[bayerStep * 2 + 1] + + 2) >> 2; + rgb[1] = (uint8_t) t0; + rgb[0] = (uint8_t) t1; + rgb[-1] = bayer[bayerStep + 1]; + + t0 = (bayer[2] + bayer[bayerStep * 2 + 2] + 1) >> 1; + t1 = (bayer[bayerStep + 1] + bayer[bayerStep + 3] + + 1) >> 1; + rgb[4] = (uint8_t) t0; + rgb[3] = bayer[bayerStep + 2]; + rgb[2] = (uint8_t) t1; + } + } + + if (bayer < bayerEnd) { + t0 = (bayer[0] + bayer[2] + bayer[bayerStep * 2] + + bayer[bayerStep * 2 + 2] + 2) >> 2; + t1 = (bayer[1] + bayer[bayerStep] + + bayer[bayerStep + 2] + bayer[bayerStep * 2 + 1] + + 2) >> 2; + rgb[-blue] = (uint8_t) t0; + rgb[0] = (uint8_t) t1; + rgb[blue] = bayer[bayerStep + 1]; + bayer++; + rgb += 3; + } + + bayer -= width; + rgb -= width * 3; + + blue = -blue; + start_with_green = !start_with_green; + } + return DC1394_SUCCESS; +} + +/* High-Quality Linear Interpolation For Demosaicing Of + Bayer-Patterned Color Images, by Henrique S. Malvar, Li-wei He, and + Ross Cutler, in ICASSP'04 */ +dc1394error_t +dc1394_bayer_HQLinear(const uint8_t *restrict bayer, uint8_t *restrict rgb, int sx, int sy, int tile) +{ + const int bayerStep = sx; + const int rgbStep = 3 * sx; + int width = sx; + int height = sy; + int blue = tile == DC1394_COLOR_FILTER_BGGR + || tile == DC1394_COLOR_FILTER_GBRG ? -1 : 1; + int start_with_green = tile == DC1394_COLOR_FILTER_GBRG + || tile == DC1394_COLOR_FILTER_GRBG; + + if ((tile>DC1394_COLOR_FILTER_MAX)||(tile> 1); + t1 = rgb[0] * 5 + + ((bayer[bayerStep2 + 1] + bayer[bayerStep2 + 3]) << 2) + - bayer[bayerStep2] + - bayer[bayerStep + 1] + - bayer[bayerStep + 3] + - bayer[bayerStep3 + 1] + - bayer[bayerStep3 + 3] + - bayer[bayerStep2 + 4] + + ((bayer[2] + bayer[bayerStep4 + 2] + 1) >> 1); + t0 = (t0 + 4) >> 3; + CLIP(t0, rgb[-blue]); + t1 = (t1 + 4) >> 3; + CLIP(t1, rgb[blue]); + bayer++; + rgb += 3; + } + + if (blue > 0) { + for (; bayer <= bayerEnd - 2; bayer += 2, rgb += 6) { + /* B at B */ + rgb[1] = bayer[bayerStep2 + 2]; + /* R at B */ + t0 = ((bayer[bayerStep + 1] + bayer[bayerStep + 3] + + bayer[bayerStep3 + 1] + bayer[bayerStep3 + 3]) << 1) + - + (((bayer[2] + bayer[bayerStep2] + + bayer[bayerStep2 + 4] + bayer[bayerStep4 + + 2]) * 3 + 1) >> 1) + + rgb[1] * 6; + /* G at B */ + t1 = ((bayer[bayerStep + 2] + bayer[bayerStep2 + 1] + + bayer[bayerStep2 + 3] + bayer[bayerStep3 + 2]) << 1) + - (bayer[2] + bayer[bayerStep2] + + bayer[bayerStep2 + 4] + bayer[bayerStep4 + 2]) + + (rgb[1] << 2); + t0 = (t0 + 4) >> 3; + CLIP(t0, rgb[-1]); + t1 = (t1 + 4) >> 3; + CLIP(t1, rgb[0]); + /* at green pixel */ + rgb[3] = bayer[bayerStep2 + 3]; + t0 = rgb[3] * 5 + + ((bayer[bayerStep + 3] + bayer[bayerStep3 + 3]) << 2) + - bayer[3] + - bayer[bayerStep + 2] + - bayer[bayerStep + 4] + - bayer[bayerStep3 + 2] + - bayer[bayerStep3 + 4] + - bayer[bayerStep4 + 3] + + + ((bayer[bayerStep2 + 1] + bayer[bayerStep2 + 5] + + 1) >> 1); + t1 = rgb[3] * 5 + + ((bayer[bayerStep2 + 2] + bayer[bayerStep2 + 4]) << 2) + - bayer[bayerStep2 + 1] + - bayer[bayerStep + 2] + - bayer[bayerStep + 4] + - bayer[bayerStep3 + 2] + - bayer[bayerStep3 + 4] + - bayer[bayerStep2 + 5] + + ((bayer[3] + bayer[bayerStep4 + 3] + 1) >> 1); + t0 = (t0 + 4) >> 3; + CLIP(t0, rgb[2]); + t1 = (t1 + 4) >> 3; + CLIP(t1, rgb[4]); + } + } else { + for (; bayer <= bayerEnd - 2; bayer += 2, rgb += 6) { + /* R at R */ + rgb[-1] = bayer[bayerStep2 + 2]; + /* B at R */ + t0 = ((bayer[bayerStep + 1] + bayer[bayerStep + 3] + + bayer[bayerStep3 + 1] + bayer[bayerStep3 + 3]) << 1) + - + (((bayer[2] + bayer[bayerStep2] + + bayer[bayerStep2 + 4] + bayer[bayerStep4 + + 2]) * 3 + 1) >> 1) + + rgb[-1] * 6; + /* G at R */ + t1 = ((bayer[bayerStep + 2] + bayer[bayerStep2 + 1] + + bayer[bayerStep2 + 3] + bayer[bayerStep * 3 + + 2]) << 1) + - (bayer[2] + bayer[bayerStep2] + + bayer[bayerStep2 + 4] + bayer[bayerStep4 + 2]) + + (rgb[-1] << 2); + t0 = (t0 + 4) >> 3; + CLIP(t0, rgb[1]); + t1 = (t1 + 4) >> 3; + CLIP(t1, rgb[0]); + + /* at green pixel */ + rgb[3] = bayer[bayerStep2 + 3]; + t0 = rgb[3] * 5 + + ((bayer[bayerStep + 3] + bayer[bayerStep3 + 3]) << 2) + - bayer[3] + - bayer[bayerStep + 2] + - bayer[bayerStep + 4] + - bayer[bayerStep3 + 2] + - bayer[bayerStep3 + 4] + - bayer[bayerStep4 + 3] + + + ((bayer[bayerStep2 + 1] + bayer[bayerStep2 + 5] + + 1) >> 1); + t1 = rgb[3] * 5 + + ((bayer[bayerStep2 + 2] + bayer[bayerStep2 + 4]) << 2) + - bayer[bayerStep2 + 1] + - bayer[bayerStep + 2] + - bayer[bayerStep + 4] + - bayer[bayerStep3 + 2] + - bayer[bayerStep3 + 4] + - bayer[bayerStep2 + 5] + + ((bayer[3] + bayer[bayerStep4 + 3] + 1) >> 1); + t0 = (t0 + 4) >> 3; + CLIP(t0, rgb[4]); + t1 = (t1 + 4) >> 3; + CLIP(t1, rgb[2]); + } + } + + if (bayer < bayerEnd) { + /* B at B */ + rgb[blue] = bayer[bayerStep2 + 2]; + /* R at B */ + t0 = ((bayer[bayerStep + 1] + bayer[bayerStep + 3] + + bayer[bayerStep3 + 1] + bayer[bayerStep3 + 3]) << 1) + - + (((bayer[2] + bayer[bayerStep2] + + bayer[bayerStep2 + 4] + bayer[bayerStep4 + + 2]) * 3 + 1) >> 1) + + rgb[blue] * 6; + /* G at B */ + t1 = (((bayer[bayerStep + 2] + bayer[bayerStep2 + 1] + + bayer[bayerStep2 + 3] + bayer[bayerStep3 + 2])) << 1) + - (bayer[2] + bayer[bayerStep2] + + bayer[bayerStep2 + 4] + bayer[bayerStep4 + 2]) + + (rgb[blue] << 2); + t0 = (t0 + 4) >> 3; + CLIP(t0, rgb[-blue]); + t1 = (t1 + 4) >> 3; + CLIP(t1, rgb[0]); + bayer++; + rgb += 3; + } + + bayer -= width; + rgb -= width * 3; + + blue = -blue; + start_with_green = !start_with_green; + } + + return DC1394_SUCCESS; + +} + +/* coriander's Bayer decoding */ +/* Edge Sensing Interpolation II from http://www-ise.stanford.edu/~tingchen/ */ +/* (Laroche,Claude A. "Apparatus and method for adaptively + interpolating a full color image utilizing chrominance gradients" + U.S. Patent 5,373,322) */ +dc1394error_t +dc1394_bayer_EdgeSense(const uint8_t *restrict bayer, uint8_t *restrict rgb, int sx, int sy, int tile) +{ + /* Removed due to patent concerns */ + return DC1394_FUNCTION_NOT_SUPPORTED; +} + +/* coriander's Bayer decoding */ +dc1394error_t +dc1394_bayer_Downsample(const uint8_t *restrict bayer, uint8_t *restrict rgb, int sx, int sy, int tile) +{ + uint8_t *outR, *outG, *outB; + register int i, j; + int tmp; + + switch (tile) { + case DC1394_COLOR_FILTER_GRBG: + case DC1394_COLOR_FILTER_BGGR: + outR = &rgb[0]; + outG = &rgb[1]; + outB = &rgb[2]; + break; + case DC1394_COLOR_FILTER_GBRG: + case DC1394_COLOR_FILTER_RGGB: + outR = &rgb[2]; + outG = &rgb[1]; + outB = &rgb[0]; + break; + default: + return DC1394_INVALID_COLOR_FILTER; + } + + switch (tile) { + case DC1394_COLOR_FILTER_GRBG: //--------------------------------------------------------- + case DC1394_COLOR_FILTER_GBRG: + for (i = 0; i < sy*sx; i += (sx<<1)) { + for (j = 0; j < sx; j += 2) { + tmp = ((bayer[i + j] + bayer[i + sx + j + 1]) >> 1); + CLIP(tmp, outG[((i >> 2) + (j >> 1)) * 3]); + tmp = bayer[i + sx + j + 1]; + CLIP(tmp, outR[((i >> 2) + (j >> 1)) * 3]); + tmp = bayer[i + sx + j]; + CLIP(tmp, outB[((i >> 2) + (j >> 1)) * 3]); + } + } + break; + case DC1394_COLOR_FILTER_BGGR: //--------------------------------------------------------- + case DC1394_COLOR_FILTER_RGGB: + for (i = 0; i < sy*sx; i += (sx<<1)) { + for (j = 0; j < sx; j += 2) { + tmp = ((bayer[i + sx + j] + bayer[i + j + 1]) >> 1); + CLIP(tmp, outG[((i >> 2) + (j >> 1)) * 3]); + tmp = bayer[i + sx + j + 1]; + CLIP(tmp, outR[((i >> 2) + (j >> 1)) * 3]); + tmp = bayer[i + j]; + CLIP(tmp, outB[((i >> 2) + (j >> 1)) * 3]); + } + } + break; + } + + return DC1394_SUCCESS; + +} + +/* this is the method used inside AVT cameras. See AVT docs. */ +dc1394error_t +dc1394_bayer_Simple(const uint8_t *restrict bayer, uint8_t *restrict rgb, int sx, int sy, int tile) +{ + const int bayerStep = sx; + const int rgbStep = 3 * sx; + int width = sx; + int height = sy; + int blue = tile == DC1394_COLOR_FILTER_BGGR + || tile == DC1394_COLOR_FILTER_GBRG ? -1 : 1; + int start_with_green = tile == DC1394_COLOR_FILTER_GBRG + || tile == DC1394_COLOR_FILTER_GRBG; + int i, imax, iinc; + + if ((tile>DC1394_COLOR_FILTER_MAX)||(tile> 1; + rgb[blue] = bayer[bayerStep]; + bayer++; + rgb += 3; + } + + if (blue > 0) { + for (; bayer <= bayerEnd - 2; bayer += 2, rgb += 6) { + rgb[-1] = bayer[0]; + rgb[0] = (bayer[1] + bayer[bayerStep] + 1) >> 1; + rgb[1] = bayer[bayerStep + 1]; + + rgb[2] = bayer[2]; + rgb[3] = (bayer[1] + bayer[bayerStep + 2] + 1) >> 1; + rgb[4] = bayer[bayerStep + 1]; + } + } else { + for (; bayer <= bayerEnd - 2; bayer += 2, rgb += 6) { + rgb[1] = bayer[0]; + rgb[0] = (bayer[1] + bayer[bayerStep] + 1) >> 1; + rgb[-1] = bayer[bayerStep + 1]; + + rgb[4] = bayer[2]; + rgb[3] = (bayer[1] + bayer[bayerStep + 2] + 1) >> 1; + rgb[2] = bayer[bayerStep + 1]; + } + } + + if (bayer < bayerEnd) { + rgb[-blue] = bayer[0]; + rgb[0] = (bayer[1] + bayer[bayerStep] + 1) >> 1; + rgb[blue] = bayer[bayerStep + 1]; + bayer++; + rgb += 3; + } + + bayer -= width; + rgb -= width * 3; + + blue = -blue; + start_with_green = !start_with_green; + } + + return DC1394_SUCCESS; + +} + +/* 16-bits versions */ + +/* insprired by OpenCV's Bayer decoding */ +dc1394error_t +dc1394_bayer_NearestNeighbor_uint16(const uint16_t *restrict bayer, uint16_t *restrict rgb, int sx, int sy, int tile, int bits) +{ + const int bayerStep = sx; + const int rgbStep = 3 * sx; + int width = sx; + int height = sy; + int blue = tile == DC1394_COLOR_FILTER_BGGR + || tile == DC1394_COLOR_FILTER_GBRG ? -1 : 1; + int start_with_green = tile == DC1394_COLOR_FILTER_GBRG + || tile == DC1394_COLOR_FILTER_GRBG; + int i, iinc, imax; + + if ((tile>DC1394_COLOR_FILTER_MAX)||(tile 0) { + for (; bayer <= bayerEnd - 2; bayer += 2, rgb += 6) { + rgb[-1] = bayer[0]; + rgb[0] = bayer[1]; + rgb[1] = bayer[bayerStep + 1]; + + rgb[2] = bayer[2]; + rgb[3] = bayer[bayerStep + 2]; + rgb[4] = bayer[bayerStep + 1]; + } + } else { + for (; bayer <= bayerEnd - 2; bayer += 2, rgb += 6) { + rgb[1] = bayer[0]; + rgb[0] = bayer[1]; + rgb[-1] = bayer[bayerStep + 1]; + + rgb[4] = bayer[2]; + rgb[3] = bayer[bayerStep + 2]; + rgb[2] = bayer[bayerStep + 1]; + } + } + + if (bayer < bayerEnd) { + rgb[-blue] = bayer[0]; + rgb[0] = bayer[1]; + rgb[blue] = bayer[bayerStep + 1]; + bayer++; + rgb += 3; + } + + bayer -= width; + rgb -= width * 3; + + blue = -blue; + start_with_green = !start_with_green; + } + + return DC1394_SUCCESS; + +} +/* OpenCV's Bayer decoding */ +dc1394error_t +dc1394_bayer_Bilinear_uint16(const uint16_t *restrict bayer, uint16_t *restrict rgb, int sx, int sy, int tile, int bits) +{ + const int bayerStep = sx; + const int rgbStep = 3 * sx; + int width = sx; + int height = sy; + int blue = tile == DC1394_COLOR_FILTER_BGGR + || tile == DC1394_COLOR_FILTER_GBRG ? -1 : 1; + int start_with_green = tile == DC1394_COLOR_FILTER_GBRG + || tile == DC1394_COLOR_FILTER_GRBG; + + if ((tile>DC1394_COLOR_FILTER_MAX)||(tile> 1; */ + t0 = (bayer[1] + bayer[bayerStep * 2 + 1] + 1) >> 1; + t1 = (bayer[bayerStep] + bayer[bayerStep + 2] + 1) >> 1; + rgb[-blue] = (uint16_t) t0; + rgb[0] = bayer[bayerStep + 1]; + rgb[blue] = (uint16_t) t1; + bayer++; + rgb += 3; + } + + if (blue > 0) { + for (; bayer <= bayerEnd - 2; bayer += 2, rgb += 6) { + t0 = (bayer[0] + bayer[2] + bayer[bayerStep * 2] + + bayer[bayerStep * 2 + 2] + 2) >> 2; + t1 = (bayer[1] + bayer[bayerStep] + + bayer[bayerStep + 2] + bayer[bayerStep * 2 + 1] + + 2) >> 2; + rgb[-1] = (uint16_t) t0; + rgb[0] = (uint16_t) t1; + rgb[1] = bayer[bayerStep + 1]; + + t0 = (bayer[2] + bayer[bayerStep * 2 + 2] + 1) >> 1; + t1 = (bayer[bayerStep + 1] + bayer[bayerStep + 3] + + 1) >> 1; + rgb[2] = (uint16_t) t0; + rgb[3] = bayer[bayerStep + 2]; + rgb[4] = (uint16_t) t1; + } + } else { + for (; bayer <= bayerEnd - 2; bayer += 2, rgb += 6) { + t0 = (bayer[0] + bayer[2] + bayer[bayerStep * 2] + + bayer[bayerStep * 2 + 2] + 2) >> 2; + t1 = (bayer[1] + bayer[bayerStep] + + bayer[bayerStep + 2] + bayer[bayerStep * 2 + 1] + + 2) >> 2; + rgb[1] = (uint16_t) t0; + rgb[0] = (uint16_t) t1; + rgb[-1] = bayer[bayerStep + 1]; + + t0 = (bayer[2] + bayer[bayerStep * 2 + 2] + 1) >> 1; + t1 = (bayer[bayerStep + 1] + bayer[bayerStep + 3] + + 1) >> 1; + rgb[4] = (uint16_t) t0; + rgb[3] = bayer[bayerStep + 2]; + rgb[2] = (uint16_t) t1; + } + } + + if (bayer < bayerEnd) { + t0 = (bayer[0] + bayer[2] + bayer[bayerStep * 2] + + bayer[bayerStep * 2 + 2] + 2) >> 2; + t1 = (bayer[1] + bayer[bayerStep] + + bayer[bayerStep + 2] + bayer[bayerStep * 2 + 1] + + 2) >> 2; + rgb[-blue] = (uint16_t) t0; + rgb[0] = (uint16_t) t1; + rgb[blue] = bayer[bayerStep + 1]; + bayer++; + rgb += 3; + } + + bayer -= width; + rgb -= width * 3; + + blue = -blue; + start_with_green = !start_with_green; + } + + return DC1394_SUCCESS; + +} + +/* High-Quality Linear Interpolation For Demosaicing Of + Bayer-Patterned Color Images, by Henrique S. Malvar, Li-wei He, and + Ross Cutler, in ICASSP'04 */ +dc1394error_t +dc1394_bayer_HQLinear_uint16(const uint16_t *restrict bayer, uint16_t *restrict rgb, int sx, int sy, int tile, int bits) +{ + const int bayerStep = sx; + const int rgbStep = 3 * sx; + int width = sx; + int height = sy; + /* + the two letters of the OpenCV name are respectively + the 4th and 3rd letters from the blinky name, + and we also have to switch R and B (OpenCV is BGR) + + CV_BayerBG2BGR <-> DC1394_COLOR_FILTER_BGGR + CV_BayerGB2BGR <-> DC1394_COLOR_FILTER_GBRG + CV_BayerGR2BGR <-> DC1394_COLOR_FILTER_GRBG + + int blue = tile == CV_BayerBG2BGR || tile == CV_BayerGB2BGR ? -1 : 1; + int start_with_green = tile == CV_BayerGB2BGR || tile == CV_BayerGR2BGR; + */ + int blue = tile == DC1394_COLOR_FILTER_BGGR + || tile == DC1394_COLOR_FILTER_GBRG ? -1 : 1; + int start_with_green = tile == DC1394_COLOR_FILTER_GBRG + || tile == DC1394_COLOR_FILTER_GRBG; + + if ((tile>DC1394_COLOR_FILTER_MAX)||(tile> 1); + t1 = rgb[0] * 5 + + ((bayer[bayerStep2 + 1] + bayer[bayerStep2 + 3]) << 2) + - bayer[bayerStep2] + - bayer[bayerStep + 1] + - bayer[bayerStep + 3] + - bayer[bayerStep3 + 1] + - bayer[bayerStep3 + 3] + - bayer[bayerStep2 + 4] + + ((bayer[2] + bayer[bayerStep4 + 2] + 1) >> 1); + t0 = (t0 + 4) >> 3; + CLIP16(t0, rgb[-blue], bits); + t1 = (t1 + 4) >> 3; + CLIP16(t1, rgb[blue], bits); + bayer++; + rgb += 3; + } + + if (blue > 0) { + for (; bayer <= bayerEnd - 2; bayer += 2, rgb += 6) { + /* B at B */ + rgb[1] = bayer[bayerStep2 + 2]; + /* R at B */ + t0 = ((bayer[bayerStep + 1] + bayer[bayerStep + 3] + + bayer[bayerStep3 + 1] + bayer[bayerStep3 + 3]) << 1) + - + (((bayer[2] + bayer[bayerStep2] + + bayer[bayerStep2 + 4] + bayer[bayerStep4 + + 2]) * 3 + 1) >> 1) + + rgb[1] * 6; + /* G at B */ + t1 = ((bayer[bayerStep + 2] + bayer[bayerStep2 + 1] + + bayer[bayerStep2 + 3] + bayer[bayerStep * 3 + + 2]) << 1) + - (bayer[2] + bayer[bayerStep2] + + bayer[bayerStep2 + 4] + bayer[bayerStep4 + 2]) + + (rgb[1] << 2); + t0 = (t0 + 4) >> 3; + CLIP16(t0, rgb[-1], bits); + t1 = (t1 + 4) >> 3; + CLIP16(t1, rgb[0], bits); + /* at green pixel */ + rgb[3] = bayer[bayerStep2 + 3]; + t0 = rgb[3] * 5 + + ((bayer[bayerStep + 3] + bayer[bayerStep3 + 3]) << 2) + - bayer[3] + - bayer[bayerStep + 2] + - bayer[bayerStep + 4] + - bayer[bayerStep3 + 2] + - bayer[bayerStep3 + 4] + - bayer[bayerStep4 + 3] + + + ((bayer[bayerStep2 + 1] + bayer[bayerStep2 + 5] + + 1) >> 1); + t1 = rgb[3] * 5 + + ((bayer[bayerStep2 + 2] + bayer[bayerStep2 + 4]) << 2) + - bayer[bayerStep2 + 1] + - bayer[bayerStep + 2] + - bayer[bayerStep + 4] + - bayer[bayerStep3 + 2] + - bayer[bayerStep3 + 4] + - bayer[bayerStep2 + 5] + + ((bayer[3] + bayer[bayerStep4 + 3] + 1) >> 1); + t0 = (t0 + 4) >> 3; + CLIP16(t0, rgb[2], bits); + t1 = (t1 + 4) >> 3; + CLIP16(t1, rgb[4], bits); + } + } else { + for (; bayer <= bayerEnd - 2; bayer += 2, rgb += 6) { + /* R at R */ + rgb[-1] = bayer[bayerStep2 + 2]; + /* B at R */ + t0 = ((bayer[bayerStep + 1] + bayer[bayerStep + 3] + + bayer[bayerStep * 3 + 1] + bayer[bayerStep3 + + 3]) << 1) + - + (((bayer[2] + bayer[bayerStep2] + + bayer[bayerStep2 + 4] + bayer[bayerStep4 + + 2]) * 3 + 1) >> 1) + + rgb[-1] * 6; + /* G at R */ + t1 = ((bayer[bayerStep + 2] + bayer[bayerStep2 + 1] + + bayer[bayerStep2 + 3] + bayer[bayerStep3 + 2]) << 1) + - (bayer[2] + bayer[bayerStep2] + + bayer[bayerStep2 + 4] + bayer[bayerStep4 + 2]) + + (rgb[-1] << 2); + t0 = (t0 + 4) >> 3; + CLIP16(t0, rgb[1], bits); + t1 = (t1 + 4) >> 3; + CLIP16(t1, rgb[0], bits); + + /* at green pixel */ + rgb[3] = bayer[bayerStep2 + 3]; + t0 = rgb[3] * 5 + + ((bayer[bayerStep + 3] + bayer[bayerStep3 + 3]) << 2) + - bayer[3] + - bayer[bayerStep + 2] + - bayer[bayerStep + 4] + - bayer[bayerStep3 + 2] + - bayer[bayerStep3 + 4] + - bayer[bayerStep4 + 3] + + + ((bayer[bayerStep2 + 1] + bayer[bayerStep2 + 5] + + 1) >> 1); + t1 = rgb[3] * 5 + + ((bayer[bayerStep2 + 2] + bayer[bayerStep2 + 4]) << 2) + - bayer[bayerStep2 + 1] + - bayer[bayerStep + 2] + - bayer[bayerStep + 4] + - bayer[bayerStep3 + 2] + - bayer[bayerStep3 + 4] + - bayer[bayerStep2 + 5] + + ((bayer[3] + bayer[bayerStep4 + 3] + 1) >> 1); + t0 = (t0 + 4) >> 3; + CLIP16(t0, rgb[4], bits); + t1 = (t1 + 4) >> 3; + CLIP16(t1, rgb[2], bits); + } + } + + if (bayer < bayerEnd) { + /* B at B */ + rgb[blue] = bayer[bayerStep2 + 2]; + /* R at B */ + t0 = ((bayer[bayerStep + 1] + bayer[bayerStep + 3] + + bayer[bayerStep3 + 1] + bayer[bayerStep3 + 3]) << 1) + - + (((bayer[2] + bayer[bayerStep2] + + bayer[bayerStep2 + 4] + bayer[bayerStep4 + + 2]) * 3 + 1) >> 1) + + rgb[blue] * 6; + /* G at B */ + t1 = (((bayer[bayerStep + 2] + bayer[bayerStep2 + 1] + + bayer[bayerStep2 + 3] + bayer[bayerStep3 + 2])) << 1) + - (bayer[2] + bayer[bayerStep2] + + bayer[bayerStep2 + 4] + bayer[bayerStep4 + 2]) + + (rgb[blue] << 2); + t0 = (t0 + 4) >> 3; + CLIP16(t0, rgb[-blue], bits); + t1 = (t1 + 4) >> 3; + CLIP16(t1, rgb[0], bits); + bayer++; + rgb += 3; + } + + bayer -= width; + rgb -= width * 3; + + blue = -blue; + start_with_green = !start_with_green; + } + + return DC1394_SUCCESS; +} + +/* coriander's Bayer decoding */ +dc1394error_t +dc1394_bayer_EdgeSense_uint16(const uint16_t *restrict bayer, uint16_t *restrict rgb, int sx, int sy, int tile, int bits) +{ + /* Removed due to patent concerns */ + return DC1394_FUNCTION_NOT_SUPPORTED; +} + +/* coriander's Bayer decoding */ +dc1394error_t +dc1394_bayer_Downsample_uint16(const uint16_t *restrict bayer, uint16_t *restrict rgb, int sx, int sy, int tile, int bits) +{ + uint16_t *outR, *outG, *outB; + register int i, j; + int tmp; + + switch (tile) { + case DC1394_COLOR_FILTER_GRBG: + case DC1394_COLOR_FILTER_BGGR: + outR = &rgb[0]; + outG = &rgb[1]; + outB = &rgb[2]; + break; + case DC1394_COLOR_FILTER_GBRG: + case DC1394_COLOR_FILTER_RGGB: + outR = &rgb[2]; + outG = &rgb[1]; + outB = &rgb[0]; + break; + default: + return DC1394_INVALID_COLOR_FILTER; + } + + switch (tile) { + case DC1394_COLOR_FILTER_GRBG: //--------------------------------------------------------- + case DC1394_COLOR_FILTER_GBRG: + for (i = 0; i < sy*sx; i += (sx<<1)) { + for (j = 0; j < sx; j += 2) { + tmp = + ((bayer[i + j] + bayer[i + sx + j + 1]) >> 1); + CLIP16(tmp, outG[((i >> 2) + (j >> 1)) * 3], bits); + tmp = bayer[i + sx + j + 1]; + CLIP16(tmp, outR[((i >> 2) + (j >> 1)) * 3], bits); + tmp = bayer[i + sx + j]; + CLIP16(tmp, outB[((i >> 2) + (j >> 1)) * 3], bits); + } + } + break; + case DC1394_COLOR_FILTER_BGGR: //--------------------------------------------------------- + case DC1394_COLOR_FILTER_RGGB: + for (i = 0; i < sy*sx; i += (sx<<1)) { + for (j = 0; j < sx; j += 2) { + tmp = + ((bayer[i + sx + j] + bayer[i + j + 1]) >> 1); + CLIP16(tmp, outG[((i >> 2) + (j >> 1)) * 3], bits); + tmp = bayer[i + sx + j + 1]; + CLIP16(tmp, outR[((i >> 2) + (j >> 1)) * 3], bits); + tmp = bayer[i + j]; + CLIP16(tmp, outB[((i >> 2) + (j >> 1)) * 3], bits); + } + } + break; + } + + return DC1394_SUCCESS; + +} + +/* coriander's Bayer decoding */ +dc1394error_t +dc1394_bayer_Simple_uint16(const uint16_t *restrict bayer, uint16_t *restrict rgb, int sx, int sy, int tile, int bits) +{ + uint16_t *outR, *outG, *outB; + register int i, j; + int tmp, base; + + // sx and sy should be even + switch (tile) { + case DC1394_COLOR_FILTER_GRBG: + case DC1394_COLOR_FILTER_BGGR: + outR = &rgb[0]; + outG = &rgb[1]; + outB = &rgb[2]; + break; + case DC1394_COLOR_FILTER_GBRG: + case DC1394_COLOR_FILTER_RGGB: + outR = &rgb[2]; + outG = &rgb[1]; + outB = &rgb[0]; + break; + default: + return DC1394_INVALID_COLOR_FILTER; + } + + switch (tile) { + case DC1394_COLOR_FILTER_GRBG: + case DC1394_COLOR_FILTER_BGGR: + outR = &rgb[0]; + outG = &rgb[1]; + outB = &rgb[2]; + break; + case DC1394_COLOR_FILTER_GBRG: + case DC1394_COLOR_FILTER_RGGB: + outR = &rgb[2]; + outG = &rgb[1]; + outB = &rgb[0]; + break; + default: + outR = NULL; + outG = NULL; + outB = NULL; + break; + } + + switch (tile) { + case DC1394_COLOR_FILTER_GRBG: //--------------------------------------------------------- + case DC1394_COLOR_FILTER_GBRG: + for (i = 0; i < sy - 1; i += 2) { + for (j = 0; j < sx - 1; j += 2) { + base = i * sx + j; + tmp = ((bayer[base] + bayer[base + sx + 1]) >> 1); + CLIP16(tmp, outG[base * 3], bits); + tmp = bayer[base + 1]; + CLIP16(tmp, outR[base * 3], bits); + tmp = bayer[base + sx]; + CLIP16(tmp, outB[base * 3], bits); + } + } + for (i = 0; i < sy - 1; i += 2) { + for (j = 1; j < sx - 1; j += 2) { + base = i * sx + j; + tmp = ((bayer[base + 1] + bayer[base + sx]) >> 1); + CLIP16(tmp, outG[(base) * 3], bits); + tmp = bayer[base]; + CLIP16(tmp, outR[(base) * 3], bits); + tmp = bayer[base + 1 + sx]; + CLIP16(tmp, outB[(base) * 3], bits); + } + } + for (i = 1; i < sy - 1; i += 2) { + for (j = 0; j < sx - 1; j += 2) { + base = i * sx + j; + tmp = ((bayer[base + sx] + bayer[base + 1]) >> 1); + CLIP16(tmp, outG[base * 3], bits); + tmp = bayer[base + sx + 1]; + CLIP16(tmp, outR[base * 3], bits); + tmp = bayer[base]; + CLIP16(tmp, outB[base * 3], bits); + } + } + for (i = 1; i < sy - 1; i += 2) { + for (j = 1; j < sx - 1; j += 2) { + base = i * sx + j; + tmp = ((bayer[base] + bayer[base + 1 + sx]) >> 1); + CLIP16(tmp, outG[(base) * 3], bits); + tmp = bayer[base + sx]; + CLIP16(tmp, outR[(base) * 3], bits); + tmp = bayer[base + 1]; + CLIP16(tmp, outB[(base) * 3], bits); + } + } + break; + case DC1394_COLOR_FILTER_BGGR: //--------------------------------------------------------- + case DC1394_COLOR_FILTER_RGGB: + for (i = 0; i < sy - 1; i += 2) { + for (j = 0; j < sx - 1; j += 2) { + base = i * sx + j; + tmp = ((bayer[base + sx] + bayer[base + 1]) >> 1); + CLIP16(tmp, outG[base * 3], bits); + tmp = bayer[base + sx + 1]; + CLIP16(tmp, outR[base * 3], bits); + tmp = bayer[base]; + CLIP16(tmp, outB[base * 3], bits); + } + } + for (i = 1; i < sy - 1; i += 2) { + for (j = 0; j < sx - 1; j += 2) { + base = i * sx + j; + tmp = ((bayer[base] + bayer[base + 1 + sx]) >> 1); + CLIP16(tmp, outG[(base) * 3], bits); + tmp = bayer[base + 1]; + CLIP16(tmp, outR[(base) * 3], bits); + tmp = bayer[base + sx]; + CLIP16(tmp, outB[(base) * 3], bits); + } + } + for (i = 0; i < sy - 1; i += 2) { + for (j = 1; j < sx - 1; j += 2) { + base = i * sx + j; + tmp = ((bayer[base] + bayer[base + sx + 1]) >> 1); + CLIP16(tmp, outG[base * 3], bits); + tmp = bayer[base + sx]; + CLIP16(tmp, outR[base * 3], bits); + tmp = bayer[base + 1]; + CLIP16(tmp, outB[base * 3], bits); + } + } + for (i = 1; i < sy - 1; i += 2) { + for (j = 1; j < sx - 1; j += 2) { + base = i * sx + j; + tmp = ((bayer[base + 1] + bayer[base + sx]) >> 1); + CLIP16(tmp, outG[(base) * 3], bits); + tmp = bayer[base]; + CLIP16(tmp, outR[(base) * 3], bits); + tmp = bayer[base + 1 + sx]; + CLIP16(tmp, outB[(base) * 3], bits); + } + } + break; + } + + /* add black border */ + for (i = sx * (sy - 1) * 3; i < sx * sy * 3; i++) { + rgb[i] = 0; + } + for (i = (sx - 1) * 3; i < sx * sy * 3; i += (sx - 1) * 3) { + rgb[i++] = 0; + rgb[i++] = 0; + rgb[i++] = 0; + } + + return DC1394_SUCCESS; + +} + +/* Variable Number of Gradients, from dcraw */ +/* Ported to libdc1394 by Frederic Devernay */ + +#define FORC3 for (c=0; c < 3; c++) + +#define SQR(x) ((x)*(x)) +#define ABS(x) (((int)(x) ^ ((int)(x) >> 31)) - ((int)(x) >> 31)) +#ifndef MIN + #define MIN(a,b) ((a) < (b) ? (a) : (b)) +#endif +#ifndef MAX + #define MAX(a,b) ((a) > (b) ? (a) : (b)) +#endif +#define LIM(x,min,max) MAX(min,MIN(x,max)) +#define ULIM(x,y,z) ((y) < (z) ? LIM(x,y,z) : LIM(x,z,y)) +/* + In order to inline this calculation, I make the risky + assumption that all filter patterns can be described + by a repeating pattern of eight rows and two columns + + Return values are either 0/1/2/3 = G/M/C/Y or 0/1/2/3 = R/G1/B/G2 + */ +#define FC(row,col) \ + (filters >> ((((row) << 1 & 14) + ((col) & 1)) << 1) & 3) + +/* + This algorithm is officially called: + + "Interpolation using a Threshold-based variable number of gradients" + + described in http://www-ise.stanford.edu/~tingchen/algodep/vargra.html + + I've extended the basic idea to work with non-Bayer filter arrays. + Gradients are numbered clockwise from NW=0 to W=7. + */ +static const signed char bayervng_terms[] = { + -2,-2,+0,-1,0,0x01, -2,-2,+0,+0,1,0x01, -2,-1,-1,+0,0,0x01, + -2,-1,+0,-1,0,0x02, -2,-1,+0,+0,0,0x03, -2,-1,+0,+1,1,0x01, + -2,+0,+0,-1,0,0x06, -2,+0,+0,+0,1,0x02, -2,+0,+0,+1,0,0x03, + -2,+1,-1,+0,0,0x04, -2,+1,+0,-1,1,0x04, -2,+1,+0,+0,0,0x06, + -2,+1,+0,+1,0,0x02, -2,+2,+0,+0,1,0x04, -2,+2,+0,+1,0,0x04, + -1,-2,-1,+0,0,0x80, -1,-2,+0,-1,0,0x01, -1,-2,+1,-1,0,0x01, + -1,-2,+1,+0,1,0x01, -1,-1,-1,+1,0,0x88, -1,-1,+1,-2,0,0x40, + -1,-1,+1,-1,0,0x22, -1,-1,+1,+0,0,0x33, -1,-1,+1,+1,1,0x11, + -1,+0,-1,+2,0,0x08, -1,+0,+0,-1,0,0x44, -1,+0,+0,+1,0,0x11, + -1,+0,+1,-2,1,0x40, -1,+0,+1,-1,0,0x66, -1,+0,+1,+0,1,0x22, + -1,+0,+1,+1,0,0x33, -1,+0,+1,+2,1,0x10, -1,+1,+1,-1,1,0x44, + -1,+1,+1,+0,0,0x66, -1,+1,+1,+1,0,0x22, -1,+1,+1,+2,0,0x10, + -1,+2,+0,+1,0,0x04, -1,+2,+1,+0,1,0x04, -1,+2,+1,+1,0,0x04, + +0,-2,+0,+0,1,0x80, +0,-1,+0,+1,1,0x88, +0,-1,+1,-2,0,0x40, + +0,-1,+1,+0,0,0x11, +0,-1,+2,-2,0,0x40, +0,-1,+2,-1,0,0x20, + +0,-1,+2,+0,0,0x30, +0,-1,+2,+1,1,0x10, +0,+0,+0,+2,1,0x08, + +0,+0,+2,-2,1,0x40, +0,+0,+2,-1,0,0x60, +0,+0,+2,+0,1,0x20, + +0,+0,+2,+1,0,0x30, +0,+0,+2,+2,1,0x10, +0,+1,+1,+0,0,0x44, + +0,+1,+1,+2,0,0x10, +0,+1,+2,-1,1,0x40, +0,+1,+2,+0,0,0x60, + +0,+1,+2,+1,0,0x20, +0,+1,+2,+2,0,0x10, +1,-2,+1,+0,0,0x80, + +1,-1,+1,+1,0,0x88, +1,+0,+1,+2,0,0x08, +1,+0,+2,-1,0,0x40, + +1,+0,+2,+1,0,0x10 +}, bayervng_chood[] = { -1,-1, -1,0, -1,+1, 0,+1, +1,+1, +1,0, +1,-1, 0,-1 }; + +dc1394error_t +dc1394_bayer_VNG(const uint8_t *restrict bayer, + uint8_t *restrict dst, int sx, int sy, + dc1394color_filter_t pattern) +{ + const int height = sy, width = sx; + static const signed char *cp; + /* the following has the same type as the image */ + uint8_t (*brow[5])[3], *pix; /* [FD] */ + int code[8][2][320], *ip, gval[8], gmin, gmax, sum[4]; + int row, col, x, y, x1, x2, y1, y2, t, weight, grads, color, diag; + int g, diff, thold, num, c; + uint32_t filters; /* [FD] */ + + /* first, use bilinear bayer decoding */ + dc1394_bayer_Bilinear(bayer, dst, sx, sy, pattern); + + switch(pattern) { + case DC1394_COLOR_FILTER_BGGR: + filters = 0x16161616; + break; + case DC1394_COLOR_FILTER_GRBG: + filters = 0x61616161; + break; + case DC1394_COLOR_FILTER_RGGB: + filters = 0x94949494; + break; + case DC1394_COLOR_FILTER_GBRG: + filters = 0x49494949; + break; + default: + return DC1394_INVALID_COLOR_FILTER; + } + + for (row=0; row < 8; row++) { /* Precalculate for VNG */ + for (col=0; col < 2; col++) { + ip = code[row][col]; + for (cp=bayervng_terms, t=0; t < 64; t++) { + y1 = *cp++; x1 = *cp++; + y2 = *cp++; x2 = *cp++; + weight = *cp++; + grads = *cp++; + color = FC(row+y1,col+x1); + if (FC(row+y2,col+x2) != color) continue; + diag = (FC(row,col+1) == color && FC(row+1,col) == color) ? 2:1; + if (abs(y1-y2) == diag && abs(x1-x2) == diag) continue; + *ip++ = (y1*width + x1)*3 + color; /* [FD] */ + *ip++ = (y2*width + x2)*3 + color; /* [FD] */ + *ip++ = weight; + for (g=0; g < 8; g++) + if (grads & 1< gval[g]) gmin = gval[g]; + if (gmax < gval[g]) gmax = gval[g]; + } + if (gmax == 0) { + memcpy (brow[2][col], pix, 3 * sizeof *dst); /* [FD] */ + continue; + } + thold = gmin + (gmax >> 1); + memset (sum, 0, sizeof sum); + color = FC(row,col); + for (num=g=0; g < 8; g++,ip+=2) { /* Average the neighbors */ + if (gval[g] <= thold) { + for (c=0; c < 3; c++) /* [FD] */ + if (c == color && ip[1]) + sum[c] += (pix[c] + pix[ip[1]]) >> 1; + else + sum[c] += pix[ip[0] + c]; + num++; + } + } + for (c=0; c < 3; c++) { /* [FD] Save to buffer */ + t = pix[color]; + if (c != color) + t += (sum[c] - sum[color]) / num; + CLIP(t,brow[2][col][c]); /* [FD] */ + } + } + if (row > 3) /* Write buffer to image */ + memcpy (dst + 3*((row-2)*width+2), brow[0]+2, (width-4)*3*sizeof *dst); /* [FD] */ + for (g=0; g < 4; g++) + brow[(g-1) & 3] = brow[g]; + } + memcpy (dst + 3*((row-2)*width+2), brow[0]+2, (width-4)*3*sizeof *dst); + memcpy (dst + 3*((row-1)*width+2), brow[1]+2, (width-4)*3*sizeof *dst); + free (brow[4]); + + return DC1394_SUCCESS; +} + + +dc1394error_t +dc1394_bayer_VNG_uint16(const uint16_t *restrict bayer, + uint16_t *restrict dst, int sx, int sy, + dc1394color_filter_t pattern, int bits) +{ + const int height = sy, width = sx; + static const signed char *cp; + /* the following has the same type as the image */ + uint16_t (*brow[5])[3], *pix; /* [FD] */ + int code[8][2][320], *ip, gval[8], gmin, gmax, sum[4]; + int row, col, x, y, x1, x2, y1, y2, t, weight, grads, color, diag; + int g, diff, thold, num, c; + uint32_t filters; /* [FD] */ + + /* first, use bilinear bayer decoding */ + + dc1394_bayer_Bilinear_uint16(bayer, dst, sx, sy, pattern, bits); + + switch(pattern) { + case DC1394_COLOR_FILTER_BGGR: + filters = 0x16161616; + break; + case DC1394_COLOR_FILTER_GRBG: + filters = 0x61616161; + break; + case DC1394_COLOR_FILTER_RGGB: + filters = 0x94949494; + break; + case DC1394_COLOR_FILTER_GBRG: + filters = 0x49494949; + break; + default: + return DC1394_INVALID_COLOR_FILTER; + } + + for (row=0; row < 8; row++) { /* Precalculate for VNG */ + for (col=0; col < 2; col++) { + ip = code[row][col]; + for (cp=bayervng_terms, t=0; t < 64; t++) { + y1 = *cp++; x1 = *cp++; + y2 = *cp++; x2 = *cp++; + weight = *cp++; + grads = *cp++; + color = FC(row+y1,col+x1); + if (FC(row+y2,col+x2) != color) continue; + diag = (FC(row,col+1) == color && FC(row+1,col) == color) ? 2:1; + if (abs(y1-y2) == diag && abs(x1-x2) == diag) continue; + *ip++ = (y1*width + x1)*3 + color; /* [FD] */ + *ip++ = (y2*width + x2)*3 + color; /* [FD] */ + *ip++ = weight; + for (g=0; g < 8; g++) + if (grads & 1< gval[g]) gmin = gval[g]; + if (gmax < gval[g]) gmax = gval[g]; + } + if (gmax == 0) { + memcpy (brow[2][col], pix, 3 * sizeof *dst); /* [FD] */ + continue; + } + thold = gmin + (gmax >> 1); + memset (sum, 0, sizeof sum); + color = FC(row,col); + for (num=g=0; g < 8; g++,ip+=2) { /* Average the neighbors */ + if (gval[g] <= thold) { + for (c=0; c < 3; c++) /* [FD] */ + if (c == color && ip[1]) + sum[c] += (pix[c] + pix[ip[1]]) >> 1; + else + sum[c] += pix[ip[0] + c]; + num++; + } + } + for (c=0; c < 3; c++) { /* [FD] Save to buffer */ + t = pix[color]; + if (c != color) + t += (sum[c] - sum[color]) / num; + CLIP16(t,brow[2][col][c],bits); /* [FD] */ + } + } + if (row > 3) /* Write buffer to image */ + memcpy (dst + 3*((row-2)*width+2), brow[0]+2, (width-4)*3*sizeof *dst); /* [FD] */ + for (g=0; g < 4; g++) + brow[(g-1) & 3] = brow[g]; + } + memcpy (dst + 3*((row-2)*width+2), brow[0]+2, (width-4)*3*sizeof *dst); + memcpy (dst + 3*((row-1)*width+2), brow[1]+2, (width-4)*3*sizeof *dst); + free (brow[4]); + + return DC1394_SUCCESS; +} + + + +/* AHD interpolation ported from dcraw to libdc1394 by Samuel Audet */ +static dc1394bool_t ahd_inited = DC1394_FALSE; /* WARNING: not multi-processor safe */ + +#define CLIPOUT(x) LIM(x,0,255) +#define CLIPOUT16(x,bits) LIM(x,0,((1< 0.008856 ? pow(r,1/3.0) : 7.787*r + 16/116.0; + } + for (i=0; i < 3; i++) + for (j=0; j < 3; j++) /* [SA] */ + xyz_cam[i][j] = xyz_rgb[i][j] / d65_white[i]; /* [SA] */ + } else { + xyz[0] = xyz[1] = xyz[2] = 0.5; + FORC3 { /* [SA] */ + xyz[0] += xyz_cam[0][c] * cam[c]; + xyz[1] += xyz_cam[1][c] * cam[c]; + xyz[2] += xyz_cam[2][c] * cam[c]; + } + xyz[0] = cbrt[CLIPOUT16((int) xyz[0],16)]; /* [SA] */ + xyz[1] = cbrt[CLIPOUT16((int) xyz[1],16)]; /* [SA] */ + xyz[2] = cbrt[CLIPOUT16((int) xyz[2],16)]; /* [SA] */ + lab[0] = 116 * xyz[1] - 16; + lab[1] = 500 * (xyz[0] - xyz[1]); + lab[2] = 200 * (xyz[1] - xyz[2]); + } +} + +/* + Adaptive Homogeneity-Directed interpolation is based on + the work of Keigo Hirakawa, Thomas Parks, and Paul Lee. + */ +#define TS 256 /* Tile Size */ + +dc1394error_t +dc1394_bayer_AHD(const uint8_t *restrict bayer, + uint8_t *restrict dst, int sx, int sy, + dc1394color_filter_t pattern) +{ + int i, j, top, left, row, col, tr, tc, fc, c, d, val, hm[2]; + /* the following has the same type as the image */ + uint8_t (*pix)[3], (*rix)[3]; /* [SA] */ + uint16_t rix16[3]; /* [SA] */ + static const int dir[4] = { -1, 1, -TS, TS }; + unsigned ldiff[2][4], abdiff[2][4], leps, abeps; + float flab[3]; /* [SA] */ + uint8_t (*rgb)[TS][TS][3]; + short (*lab)[TS][TS][3]; + char (*homo)[TS][TS], *buffer; + + /* start - new code for libdc1394 */ + uint32_t filters; + const int height = sy, width = sx; + int x, y; + + if (ahd_inited==DC1394_FALSE) { + /* WARNING: this might not be multi-processor safe */ + cam_to_cielab (NULL,NULL); + ahd_inited = DC1394_TRUE; + } + + switch(pattern) { + case DC1394_COLOR_FILTER_BGGR: + filters = 0x16161616; + break; + case DC1394_COLOR_FILTER_GRBG: + filters = 0x61616161; + break; + case DC1394_COLOR_FILTER_RGGB: + filters = 0x94949494; + break; + case DC1394_COLOR_FILTER_GBRG: + filters = 0x49494949; + break; + default: + return DC1394_INVALID_COLOR_FILTER; + } + + /* fill-in destination with known exact values */ + for (y = 0; y < height; y++) { + for (x = 0; x < width; x++) { + int channel = FC(y,x); + dst[(y*width+x)*3 + channel] = bayer[y*width+x]; + } + } + /* end - new code for libdc1394 */ + + /* start - code from border_interpolate (int border) */ + { + int border = 3; + unsigned row, col, y, x, f, c, sum[8]; + + for (row=0; row < height; row++) + for (col=0; col < width; col++) { + if (col==border && row >= border && row < height-border) + col = width-border; + memset (sum, 0, sizeof sum); + for (y=row-1; y != row+2; y++) + for (x=col-1; x != col+2; x++) + if (y < height && x < width) { + f = FC(y,x); + sum[f] += dst[(y*width+x)*3 + f]; /* [SA] */ + sum[f+4]++; + } + f = FC(row,col); + FORC3 if (c != f && sum[c+4]) /* [SA] */ + dst[(row*width+col)*3 + c] = sum[c] / sum[c+4]; /* [SA] */ + } + } + /* end - code from border_interpolate (int border) */ + + + buffer = (char *) malloc (26*TS*TS); /* 1664 kB */ + /* merror (buffer, "ahd_interpolate()"); */ + rgb = (uint8_t(*)[TS][TS][3]) buffer; /* [SA] */ + lab = (short (*)[TS][TS][3])(buffer + 12*TS*TS); + homo = (char (*)[TS][TS]) (buffer + 24*TS*TS); + + for (top=0; top < height; top += TS-6) + for (left=0; left < width; left += TS-6) { + memset (rgb, 0, 12*TS*TS); + + /* Interpolate green horizontally and vertically: */ + for (row = top < 2 ? 2:top; row < top+TS && row < height-2; row++) { + col = left + (FC(row,left) == 1); + if (col < 2) col += 2; + for (fc = FC(row,col); col < left+TS && col < width-2; col+=2) { + pix = (uint8_t (*)[3])dst + (row*width+col); /* [SA] */ + val = ((pix[-1][1] + pix[0][fc] + pix[1][1]) * 2 + - pix[-2][fc] - pix[2][fc]) >> 2; + rgb[0][row-top][col-left][1] = ULIM(val,pix[-1][1],pix[1][1]); + val = ((pix[-width][1] + pix[0][fc] + pix[width][1]) * 2 + - pix[-2*width][fc] - pix[2*width][fc]) >> 2; + rgb[1][row-top][col-left][1] = ULIM(val,pix[-width][1],pix[width][1]); + } + } + /* Interpolate red and blue, and convert to CIELab: */ + for (d=0; d < 2; d++) + for (row=top+1; row < top+TS-1 && row < height-1; row++) + for (col=left+1; col < left+TS-1 && col < width-1; col++) { + pix = (uint8_t (*)[3])dst + (row*width+col); /* [SA] */ + rix = &rgb[d][row-top][col-left]; + if ((c = 2 - FC(row,col)) == 1) { + c = FC(row+1,col); + val = pix[0][1] + (( pix[-1][2-c] + pix[1][2-c] + - rix[-1][1] - rix[1][1] ) >> 1); + rix[0][2-c] = CLIPOUT(val); /* [SA] */ + val = pix[0][1] + (( pix[-width][c] + pix[width][c] + - rix[-TS][1] - rix[TS][1] ) >> 1); + } else + val = rix[0][1] + (( pix[-width-1][c] + pix[-width+1][c] + + pix[+width-1][c] + pix[+width+1][c] + - rix[-TS-1][1] - rix[-TS+1][1] + - rix[+TS-1][1] - rix[+TS+1][1] + 1) >> 2); + rix[0][c] = CLIPOUT(val); /* [SA] */ + c = FC(row,col); + rix[0][c] = pix[0][c]; + rix16[0] = rix[0][0]; /* [SA] */ + rix16[1] = rix[0][1]; /* [SA] */ + rix16[2] = rix[0][2]; /* [SA] */ + cam_to_cielab (rix16, flab); /* [SA] */ + FORC3 lab[d][row-top][col-left][c] = 64*flab[c]; + } + /* Build homogeneity maps from the CIELab images: */ + memset (homo, 0, 2*TS*TS); + for (row=top+2; row < top+TS-2 && row < height; row++) { + tr = row-top; + for (col=left+2; col < left+TS-2 && col < width; col++) { + tc = col-left; + for (d=0; d < 2; d++) + for (i=0; i < 4; i++) + ldiff[d][i] = ABS(lab[d][tr][tc][0]-lab[d][tr][tc+dir[i]][0]); + leps = MIN(MAX(ldiff[0][0],ldiff[0][1]), + MAX(ldiff[1][2],ldiff[1][3])); + for (d=0; d < 2; d++) + for (i=0; i < 4; i++) + if (i >> 1 == d || ldiff[d][i] <= leps) + abdiff[d][i] = SQR(lab[d][tr][tc][1]-lab[d][tr][tc+dir[i]][1]) + + SQR(lab[d][tr][tc][2]-lab[d][tr][tc+dir[i]][2]); + abeps = MIN(MAX(abdiff[0][0],abdiff[0][1]), + MAX(abdiff[1][2],abdiff[1][3])); + for (d=0; d < 2; d++) + for (i=0; i < 4; i++) + if (ldiff[d][i] <= leps && abdiff[d][i] <= abeps) + homo[d][tr][tc]++; + } + } + /* Combine the most homogenous pixels for the final result: */ + for (row=top+3; row < top+TS-3 && row < height-3; row++) { + tr = row-top; + for (col=left+3; col < left+TS-3 && col < width-3; col++) { + tc = col-left; + for (d=0; d < 2; d++) + for (hm[d]=0, i=tr-1; i <= tr+1; i++) + for (j=tc-1; j <= tc+1; j++) + hm[d] += homo[d][i][j]; + if (hm[0] != hm[1]) + FORC3 dst[(row*width+col)*3 + c] = CLIPOUT(rgb[hm[1] > hm[0]][tr][tc][c]); /* [SA] */ + else + FORC3 dst[(row*width+col)*3 + c] = + CLIPOUT((rgb[0][tr][tc][c] + rgb[1][tr][tc][c]) >> 1); /* [SA] */ + } + } + } + free (buffer); + + return DC1394_SUCCESS; +} + +dc1394error_t +dc1394_bayer_AHD_uint16(const uint16_t *restrict bayer, + uint16_t *restrict dst, int sx, int sy, + dc1394color_filter_t pattern, int bits) +{ + int i, j, top, left, row, col, tr, tc, fc, c, d, val, hm[2]; + /* the following has the same type as the image */ + uint16_t (*pix)[3], (*rix)[3]; /* [SA] */ + static const int dir[4] = { -1, 1, -TS, TS }; + unsigned ldiff[2][4], abdiff[2][4], leps, abeps; + float flab[3]; + uint16_t (*rgb)[TS][TS][3]; /* [SA] */ + short (*lab)[TS][TS][3]; + char (*homo)[TS][TS], *buffer; + + /* start - new code for libdc1394 */ + uint32_t filters; + const int height = sy, width = sx; + int x, y; + + if (ahd_inited==DC1394_FALSE) { + /* WARNING: this might not be multi-processor safe */ + cam_to_cielab (NULL,NULL); + ahd_inited = DC1394_TRUE; + } + + switch(pattern) { + case DC1394_COLOR_FILTER_BGGR: + filters = 0x16161616; + break; + case DC1394_COLOR_FILTER_GRBG: + filters = 0x61616161; + break; + case DC1394_COLOR_FILTER_RGGB: + filters = 0x94949494; + break; + case DC1394_COLOR_FILTER_GBRG: + filters = 0x49494949; + break; + default: + return DC1394_INVALID_COLOR_FILTER; + } + + /* fill-in destination with known exact values */ + for (y = 0; y < height; y++) { + for (x = 0; x < width; x++) { + int channel = FC(y,x); + dst[(y*width+x)*3 + channel] = bayer[y*width+x]; + } + } + /* end - new code for libdc1394 */ + + /* start - code from border_interpolate(int border) */ + { + int border = 3; + unsigned row, col, y, x, f, c, sum[8]; + + for (row=0; row < height; row++) + for (col=0; col < width; col++) { + if (col==border && row >= border && row < height-border) + col = width-border; + memset (sum, 0, sizeof sum); + for (y=row-1; y != row+2; y++) + for (x=col-1; x != col+2; x++) + if (y < height && x < width) { + f = FC(y,x); + sum[f] += dst[(y*width+x)*3 + f]; /* [SA] */ + sum[f+4]++; + } + f = FC(row,col); + FORC3 if (c != f && sum[c+4]) /* [SA] */ + dst[(row*width+col)*3 + c] = sum[c] / sum[c+4]; /* [SA] */ + } + } + /* end - code from border_interpolate(int border) */ + + + buffer = (char *) malloc (26*TS*TS); /* 1664 kB */ + /* merror (buffer, "ahd_interpolate()"); */ + rgb = (uint16_t(*)[TS][TS][3]) buffer; /* [SA] */ + lab = (short (*)[TS][TS][3])(buffer + 12*TS*TS); + homo = (char (*)[TS][TS]) (buffer + 24*TS*TS); + + for (top=0; top < height; top += TS-6) + for (left=0; left < width; left += TS-6) { + memset (rgb, 0, 12*TS*TS); + + /* Interpolate green horizontally and vertically: */ + for (row = top < 2 ? 2:top; row < top+TS && row < height-2; row++) { + col = left + (FC(row,left) == 1); + if (col < 2) col += 2; + for (fc = FC(row,col); col < left+TS && col < width-2; col+=2) { + pix = (uint16_t (*)[3])dst + (row*width+col); /* [SA] */ + val = ((pix[-1][1] + pix[0][fc] + pix[1][1]) * 2 + - pix[-2][fc] - pix[2][fc]) >> 2; + rgb[0][row-top][col-left][1] = ULIM(val,pix[-1][1],pix[1][1]); + val = ((pix[-width][1] + pix[0][fc] + pix[width][1]) * 2 + - pix[-2*width][fc] - pix[2*width][fc]) >> 2; + rgb[1][row-top][col-left][1] = ULIM(val,pix[-width][1],pix[width][1]); + } + } + /* Interpolate red and blue, and convert to CIELab: */ + for (d=0; d < 2; d++) + for (row=top+1; row < top+TS-1 && row < height-1; row++) + for (col=left+1; col < left+TS-1 && col < width-1; col++) { + pix = (uint16_t (*)[3])dst + (row*width+col); /* [SA] */ + rix = &rgb[d][row-top][col-left]; + if ((c = 2 - FC(row,col)) == 1) { + c = FC(row+1,col); + val = pix[0][1] + (( pix[-1][2-c] + pix[1][2-c] + - rix[-1][1] - rix[1][1] ) >> 1); + rix[0][2-c] = CLIPOUT16(val, bits); /* [SA] */ + val = pix[0][1] + (( pix[-width][c] + pix[width][c] + - rix[-TS][1] - rix[TS][1] ) >> 1); + } else + val = rix[0][1] + (( pix[-width-1][c] + pix[-width+1][c] + + pix[+width-1][c] + pix[+width+1][c] + - rix[-TS-1][1] - rix[-TS+1][1] + - rix[+TS-1][1] - rix[+TS+1][1] + 1) >> 2); + rix[0][c] = CLIPOUT16(val, bits); /* [SA] */ + c = FC(row,col); + rix[0][c] = pix[0][c]; + cam_to_cielab (rix[0], flab); + FORC3 lab[d][row-top][col-left][c] = 64*flab[c]; + } + /* Build homogeneity maps from the CIELab images: */ + memset (homo, 0, 2*TS*TS); + for (row=top+2; row < top+TS-2 && row < height; row++) { + tr = row-top; + for (col=left+2; col < left+TS-2 && col < width; col++) { + tc = col-left; + for (d=0; d < 2; d++) + for (i=0; i < 4; i++) + ldiff[d][i] = ABS(lab[d][tr][tc][0]-lab[d][tr][tc+dir[i]][0]); + leps = MIN(MAX(ldiff[0][0],ldiff[0][1]), + MAX(ldiff[1][2],ldiff[1][3])); + for (d=0; d < 2; d++) + for (i=0; i < 4; i++) + if (i >> 1 == d || ldiff[d][i] <= leps) + abdiff[d][i] = SQR(lab[d][tr][tc][1]-lab[d][tr][tc+dir[i]][1]) + + SQR(lab[d][tr][tc][2]-lab[d][tr][tc+dir[i]][2]); + abeps = MIN(MAX(abdiff[0][0],abdiff[0][1]), + MAX(abdiff[1][2],abdiff[1][3])); + for (d=0; d < 2; d++) + for (i=0; i < 4; i++) + if (ldiff[d][i] <= leps && abdiff[d][i] <= abeps) + homo[d][tr][tc]++; + } + } + /* Combine the most homogenous pixels for the final result: */ + for (row=top+3; row < top+TS-3 && row < height-3; row++) { + tr = row-top; + for (col=left+3; col < left+TS-3 && col < width-3; col++) { + tc = col-left; + for (d=0; d < 2; d++) + for (hm[d]=0, i=tr-1; i <= tr+1; i++) + for (j=tc-1; j <= tc+1; j++) + hm[d] += homo[d][i][j]; + if (hm[0] != hm[1]) + FORC3 dst[(row*width+col)*3 + c] = CLIPOUT16(rgb[hm[1] > hm[0]][tr][tc][c], bits); /* [SA] */ + else + FORC3 dst[(row*width+col)*3 + c] = + CLIPOUT16((rgb[0][tr][tc][c] + rgb[1][tr][tc][c]) >> 1, bits); /* [SA] */ + } + } + } + free (buffer); + + return DC1394_SUCCESS; +} + +dc1394error_t +dc1394_bayer_decoding_8bit(const uint8_t *restrict bayer, uint8_t *restrict rgb, uint32_t sx, uint32_t sy, dc1394color_filter_t tile, dc1394bayer_method_t method) +{ + switch (method) { + case DC1394_BAYER_METHOD_NEAREST: + return dc1394_bayer_NearestNeighbor(bayer, rgb, sx, sy, tile); + case DC1394_BAYER_METHOD_SIMPLE: + return dc1394_bayer_Simple(bayer, rgb, sx, sy, tile); + case DC1394_BAYER_METHOD_BILINEAR: + return dc1394_bayer_Bilinear(bayer, rgb, sx, sy, tile); + case DC1394_BAYER_METHOD_HQLINEAR: + return dc1394_bayer_HQLinear(bayer, rgb, sx, sy, tile); + case DC1394_BAYER_METHOD_DOWNSAMPLE: + return dc1394_bayer_Downsample(bayer, rgb, sx, sy, tile); + case DC1394_BAYER_METHOD_EDGESENSE: + return dc1394_bayer_EdgeSense(bayer, rgb, sx, sy, tile); + case DC1394_BAYER_METHOD_VNG: + return dc1394_bayer_VNG(bayer, rgb, sx, sy, tile); + case DC1394_BAYER_METHOD_AHD: + return dc1394_bayer_AHD(bayer, rgb, sx, sy, tile); + default: + return DC1394_INVALID_BAYER_METHOD; + } + +} + +dc1394error_t +dc1394_bayer_decoding_16bit(const uint16_t *restrict bayer, uint16_t *restrict rgb, uint32_t sx, uint32_t sy, dc1394color_filter_t tile, dc1394bayer_method_t method, uint32_t bits) +{ + switch (method) { + case DC1394_BAYER_METHOD_NEAREST: + return dc1394_bayer_NearestNeighbor_uint16(bayer, rgb, sx, sy, tile, bits); + case DC1394_BAYER_METHOD_SIMPLE: + return dc1394_bayer_Simple_uint16(bayer, rgb, sx, sy, tile, bits); + case DC1394_BAYER_METHOD_BILINEAR: + return dc1394_bayer_Bilinear_uint16(bayer, rgb, sx, sy, tile, bits); + case DC1394_BAYER_METHOD_HQLINEAR: + return dc1394_bayer_HQLinear_uint16(bayer, rgb, sx, sy, tile, bits); + case DC1394_BAYER_METHOD_DOWNSAMPLE: + return dc1394_bayer_Downsample_uint16(bayer, rgb, sx, sy, tile, bits); + case DC1394_BAYER_METHOD_EDGESENSE: + return dc1394_bayer_EdgeSense_uint16(bayer, rgb, sx, sy, tile, bits); + case DC1394_BAYER_METHOD_VNG: + return dc1394_bayer_VNG_uint16(bayer, rgb, sx, sy, tile, bits); + case DC1394_BAYER_METHOD_AHD: + return dc1394_bayer_AHD_uint16(bayer, rgb, sx, sy, tile, bits); + default: + return DC1394_INVALID_BAYER_METHOD; + } + +} + +#if 0 +dc1394error_t +Adapt_buffer_bayer(dc1394video_frame_t *in, dc1394video_frame_t *out, dc1394bayer_method_t method) +{ + uint32_t bpp; + + // conversions will halve the buffer size if the method is DOWNSAMPLE: + out->size[0]=in->size[0]; + out->size[1]=in->size[1]; + if (method == DC1394_BAYER_METHOD_DOWNSAMPLE) { + out->size[0]/=2; // ODD SIZE CASES NOT TAKEN INTO ACCOUNT + out->size[1]/=2; + } + + // as a convention we divide the image position by two in the case of a DOWNSAMPLE: + out->position[0]=in->position[0]; + out->position[1]=in->position[1]; + if (method == DC1394_BAYER_METHOD_DOWNSAMPLE) { + out->position[0]/=2; + out->position[1]/=2; + } + + // the destination color coding is ALWAYS RGB. Set this. + if ( (in->color_coding==DC1394_COLOR_CODING_RAW16) || + (in->color_coding==DC1394_COLOR_CODING_MONO16) ) + out->color_coding=DC1394_COLOR_CODING_RGB16; + else + out->color_coding=DC1394_COLOR_CODING_RGB8; + + // keep the color filter value in all cases. If the format is not raw it will not be further used anyway + out->color_filter=in->color_filter; + + // The output is never YUV, hence nothing to do about YUV byte order + + // bit depth is conserved for 16 bit and set to 8bit for 8bit: + if ( (in->color_coding==DC1394_COLOR_CODING_RAW16) || + (in->color_coding==DC1394_COLOR_CODING_MONO16) ) + out->data_depth=in->data_depth; + else + out->data_depth=8; + + // don't know what to do with stride... >>>> TODO: STRIDE SHOULD BE TAKEN INTO ACCOUNT... <<<< + // out->stride=?? + + // the video mode should not change. Color coding and other stuff can be accessed in specific fields of this struct + out->video_mode = in->video_mode; + + // padding is kept: + out->padding_bytes = in->padding_bytes; + + // image bytes changes: >>>> TODO: STRIDE SHOULD BE TAKEN INTO ACCOUNT... <<<< + dc1394_get_color_coding_bit_size(out->color_coding, &bpp); + out->image_bytes=(out->size[0]*out->size[1]*bpp)/8; + + // total is image_bytes + padding_bytes + out->total_bytes = out->image_bytes + out->padding_bytes; + + // bytes-per-packet and packets_per_frame are internal data that can be kept as is. + out->packet_size = in->packet_size; + out->packets_per_frame = in->packets_per_frame; + + // timestamp, frame_behind, id and camera are copied too: + out->timestamp = in->timestamp; + out->frames_behind = in->frames_behind; + out->camera = in->camera; + out->id = in->id; + + // verify memory allocation: + if (out->total_bytes>out->allocated_image_bytes) { + free(out->image); + out->image=(uint8_t*)malloc(out->total_bytes*sizeof(uint8_t)); + if (out->image) + out->allocated_image_bytes = out->total_bytes*sizeof(uint8_t); + else + out->allocated_image_bytes = 0; + } + + // Copy padding bytes: + if(out->image) + memcpy(&(out->image[out->image_bytes]),&(in->image[in->image_bytes]),out->padding_bytes); + + out->little_endian=0; // not used before 1.32 is out. + out->data_in_padding=0; // not used before 1.32 is out. + + if(out->image) + return DC1394_SUCCESS; + + return DC1394_MEMORY_ALLOCATION_FAILURE; +} + +dc1394error_t +dc1394_debayer_frames(dc1394video_frame_t *in, dc1394video_frame_t *out, dc1394bayer_method_t method) +{ + if ((methodDC1394_BAYER_METHOD_MAX)) + return DC1394_INVALID_BAYER_METHOD; + + switch (in->color_coding) { + case DC1394_COLOR_CODING_RAW8: + case DC1394_COLOR_CODING_MONO8: + + if(DC1394_SUCCESS != Adapt_buffer_bayer(in,out,method)) + return DC1394_MEMORY_ALLOCATION_FAILURE; + + switch (method) { + case DC1394_BAYER_METHOD_NEAREST: + return dc1394_bayer_NearestNeighbor(in->image, out->image, in->size[0], in->size[1], in->color_filter); + case DC1394_BAYER_METHOD_SIMPLE: + return dc1394_bayer_Simple(in->image, out->image, in->size[0], in->size[1], in->color_filter); + case DC1394_BAYER_METHOD_BILINEAR: + return dc1394_bayer_Bilinear(in->image, out->image, in->size[0], in->size[1], in->color_filter); + case DC1394_BAYER_METHOD_HQLINEAR: + return dc1394_bayer_HQLinear(in->image, out->image, in->size[0], in->size[1], in->color_filter); + case DC1394_BAYER_METHOD_DOWNSAMPLE: + return dc1394_bayer_Downsample(in->image, out->image, in->size[0], in->size[1], in->color_filter); + case DC1394_BAYER_METHOD_EDGESENSE: + return dc1394_bayer_EdgeSense(in->image, out->image, in->size[0], in->size[1], in->color_filter); + case DC1394_BAYER_METHOD_VNG: + return dc1394_bayer_VNG(in->image, out->image, in->size[0], in->size[1], in->color_filter); + case DC1394_BAYER_METHOD_AHD: + return dc1394_bayer_AHD(in->image, out->image, in->size[0], in->size[1], in->color_filter); + } + break; + case DC1394_COLOR_CODING_MONO16: + case DC1394_COLOR_CODING_RAW16: + + if(DC1394_SUCCESS != Adapt_buffer_bayer(in,out,method)) + return DC1394_MEMORY_ALLOCATION_FAILURE; + + switch (method) { + case DC1394_BAYER_METHOD_NEAREST: + return dc1394_bayer_NearestNeighbor_uint16((uint16_t*)in->image, (uint16_t*)out->image, in->size[0], in->size[1], in->color_filter, in->data_depth); + case DC1394_BAYER_METHOD_SIMPLE: + return dc1394_bayer_Simple_uint16((uint16_t*)in->image, (uint16_t*)out->image, in->size[0], in->size[1], in->color_filter, in->data_depth); + case DC1394_BAYER_METHOD_BILINEAR: + return dc1394_bayer_Bilinear_uint16((uint16_t*)in->image, (uint16_t*)out->image, in->size[0], in->size[1], in->color_filter, in->data_depth); + case DC1394_BAYER_METHOD_HQLINEAR: + return dc1394_bayer_HQLinear_uint16((uint16_t*)in->image, (uint16_t*)out->image, in->size[0], in->size[1], in->color_filter, in->data_depth); + case DC1394_BAYER_METHOD_DOWNSAMPLE: + return dc1394_bayer_Downsample_uint16((uint16_t*)in->image, (uint16_t*)out->image, in->size[0], in->size[1], in->color_filter, in->data_depth); + case DC1394_BAYER_METHOD_EDGESENSE: + return dc1394_bayer_EdgeSense_uint16((uint16_t*)in->image, (uint16_t*)out->image, in->size[0], in->size[1], in->color_filter, in->data_depth); + case DC1394_BAYER_METHOD_VNG: + return dc1394_bayer_VNG_uint16((uint16_t*)in->image, (uint16_t*)out->image, in->size[0], in->size[1], in->color_filter, in->data_depth); + case DC1394_BAYER_METHOD_AHD: + return dc1394_bayer_AHD_uint16((uint16_t*)in->image, (uint16_t*)out->image, in->size[0], in->size[1], in->color_filter, in->data_depth); + } + break; + default: + return DC1394_FUNCTION_NOT_SUPPORTED; + } + + return DC1394_SUCCESS; +} +#endif diff --git a/bayer.h b/bayer.h new file mode 100644 index 0000000..137908a --- /dev/null +++ b/bayer.h @@ -0,0 +1,87 @@ +#include + +typedef enum { + DC1394_BAYER_METHOD_NEAREST=0, + DC1394_BAYER_METHOD_SIMPLE, + DC1394_BAYER_METHOD_BILINEAR, + DC1394_BAYER_METHOD_HQLINEAR, + DC1394_BAYER_METHOD_DOWNSAMPLE, + DC1394_BAYER_METHOD_EDGESENSE, + DC1394_BAYER_METHOD_VNG, + DC1394_BAYER_METHOD_AHD +} dc1394bayer_method_t; + +typedef enum { + DC1394_COLOR_FILTER_RGGB = 512, + DC1394_COLOR_FILTER_GBRG, + DC1394_COLOR_FILTER_GRBG, + DC1394_COLOR_FILTER_BGGR +} dc1394color_filter_t ; +#define DC1394_COLOR_FILTER_MIN DC1394_COLOR_FILTER_RGGB +#define DC1394_COLOR_FILTER_MAX DC1394_COLOR_FILTER_BGGR +#define DC1394_COLOR_FILTER_NUM (DC1394_COLOR_FILTER_MAX - DC1394_COLOR_FILTER_MIN + 1) + +/** + * Error codes returned by most libdc1394 functions. + * + * General rule: 0 is success, negative denotes a problem. + */ +typedef enum { + DC1394_SUCCESS = 0, + DC1394_FAILURE = -1, + DC1394_NOT_A_CAMERA = -2, + DC1394_FUNCTION_NOT_SUPPORTED = -3, + DC1394_CAMERA_NOT_INITIALIZED = -4, + DC1394_MEMORY_ALLOCATION_FAILURE = -5, + DC1394_TAGGED_REGISTER_NOT_FOUND = -6, + DC1394_NO_ISO_CHANNEL = -7, + DC1394_NO_BANDWIDTH = -8, + DC1394_IOCTL_FAILURE = -9, + DC1394_CAPTURE_IS_NOT_SET = -10, + DC1394_CAPTURE_IS_RUNNING = -11, + DC1394_RAW1394_FAILURE = -12, + DC1394_FORMAT7_ERROR_FLAG_1 = -13, + DC1394_FORMAT7_ERROR_FLAG_2 = -14, + DC1394_INVALID_ARGUMENT_VALUE = -15, + DC1394_REQ_VALUE_OUTSIDE_RANGE = -16, + DC1394_INVALID_FEATURE = -17, + DC1394_INVALID_VIDEO_FORMAT = -18, + DC1394_INVALID_VIDEO_MODE = -19, + DC1394_INVALID_FRAMERATE = -20, + DC1394_INVALID_TRIGGER_MODE = -21, + DC1394_INVALID_TRIGGER_SOURCE = -22, + DC1394_INVALID_ISO_SPEED = -23, + DC1394_INVALID_IIDC_VERSION = -24, + DC1394_INVALID_COLOR_CODING = -25, + DC1394_INVALID_COLOR_FILTER = -26, + DC1394_INVALID_CAPTURE_POLICY = -27, + DC1394_INVALID_ERROR_CODE = -28, + DC1394_INVALID_BAYER_METHOD = -29, + DC1394_INVALID_VIDEO1394_DEVICE = -30, + DC1394_INVALID_OPERATION_MODE = -31, + DC1394_INVALID_TRIGGER_POLARITY = -32, + DC1394_INVALID_FEATURE_MODE = -33, + DC1394_INVALID_LOG_TYPE = -34, + DC1394_INVALID_BYTE_ORDER = -35, + DC1394_INVALID_STEREO_METHOD = -36, + DC1394_BASLER_NO_MORE_SFF_CHUNKS = -37, + DC1394_BASLER_CORRUPTED_SFF_CHUNK = -38, + DC1394_BASLER_UNKNOWN_SFF_CHUNK = -39 +} dc1394error_t; +#define DC1394_ERROR_MIN DC1394_BASLER_UNKNOWN_SFF_CHUNK +#define DC1394_ERROR_MAX DC1394_SUCCESS +#define DC1394_ERROR_NUM (DC1394_ERROR_MAX-DC1394_ERROR_MIN+1) + +typedef enum { + DC1394_FALSE= 0, + DC1394_TRUE +} dc1394bool_t; + + + + +dc1394error_t +dc1394_bayer_decoding_8bit(const uint8_t * bayer, uint8_t * rgb, uint32_t sx, uint32_t sy, dc1394color_filter_t tile, dc1394bayer_method_t method); + +dc1394error_t +dc1394_bayer_decoding_16bit(const uint16_t * bayer, uint16_t * rgb, uint32_t sx, uint32_t sy, dc1394color_filter_t tile, dc1394bayer_method_t method, uint32_t bits); diff --git a/build/GTKCamera b/build/GTKCamera new file mode 100755 index 0000000..cb0cdbc Binary files /dev/null and b/build/GTKCamera differ diff --git a/main.c b/main.c index d3ad5a4..d54c076 100644 --- a/main.c +++ b/main.c @@ -1,11 +1,18 @@ #include #include #include +#include +#include #include #include +#include +#include +#include +#include #include #include #include "ini.h" +#include "bayer.h" enum io_method { IO_METHOD_READ, @@ -21,14 +28,40 @@ struct buffer { struct buffer *buffers; static int *outbuffer; static unsigned int n_buffers; -static char *rear_dev_name; -static char *front_dev_name; -static char *dev_name; static enum io_method io = IO_METHOD_MMAP; -static int preview_width = -1; -static int preview_height = -1; -static int preview_fmt = V4L2_PIX_FMT_RGB24; +// Rear camera +static char *rear_dev_name; +static int *rear_entity_id; +static char *rear_dev[20]; +static int rear_width = -1; +static int rear_height = -1; +static int rear_rotate = 0; +static int rear_fmt = V4L2_PIX_FMT_RGB24; +static int rear_mbus = MEDIA_BUS_FMT_RGB888_1X24; + +// Front camera +static char *front_dev_name; +static int *front_entity_id; +static char *front_dev[20]; +static int front_width = -1; +static int front_height = -1; +static int front_rotate = 0; +static int front_fmt = V4L2_PIX_FMT_RGB24; +static int front_mbus = MEDIA_BUS_FMT_RGB888_1X24; + +// Camera interface +static char *media_drv_name; +static int *interface_entity_id; +static char *dev_name[20]; +static int media_fd; + +// State +static int current_width = -1; +static int current_height = -1; +static int current_fmt = 0; +static int current_rotate = 0; +static int capture = 0; GObject *preview_image; @@ -153,6 +186,32 @@ init_mmap(int fd) } } +static void +init_sensor(char* fn, int width, int height, int mbus) +{ + int fd; + struct v4l2_subdev_format fmt; + fd = open(fn, O_RDWR); + + g_printerr("Setting sensor to %dx%d fmt %d\n", + width, height, mbus); + fmt.pad = 0; + fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE; + fmt.format.code = mbus; + fmt.format.width = width; + fmt.format.height = height; + fmt.format.field = V4L2_FIELD_ANY; + + if (xioctl(fd, VIDIOC_SUBDEV_S_FMT, &fmt) == -1) { + errno_exit("VIDIOC_SUBDEV_S_FMT"); + } + + g_printerr("Driver returned %dx%d fmt %d\n", + fmt.format.width, fmt.format.height, + fmt.format.code); + close(fd); +} + static void init_device(int fd) { @@ -219,12 +278,12 @@ init_device(int fd) struct v4l2_format fmt = { .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, }; - if (preview_width > 0) { + if (current_width > 0) { g_printerr("Setting camera to %dx%d fmt %d\n", - preview_width, preview_height, preview_fmt); - fmt.fmt.pix.width = preview_width; - fmt.fmt.pix.height = preview_height; - fmt.fmt.pix.pixelformat = preview_fmt; + current_width, current_height, current_fmt); + fmt.fmt.pix.width = current_width; + fmt.fmt.pix.height = current_height; + fmt.fmt.pix.pixelformat = current_fmt; fmt.fmt.pix.field = V4L2_FIELD_ANY; if (xioctl(fd, VIDIOC_S_FMT, &fmt) == -1) { @@ -246,10 +305,10 @@ init_device(int fd) g_printerr("Driver returned %dx%d fmt %d\n", fmt.fmt.pix.width, fmt.fmt.pix.height, fmt.fmt.pix.pixelformat); - preview_width = fmt.fmt.pix.width; - preview_height = fmt.fmt.pix.height; + current_width = fmt.fmt.pix.width; + current_height = fmt.fmt.pix.height; } - preview_fmt = fmt.fmt.pix.pixelformat; + current_fmt = fmt.fmt.pix.pixelformat; /* Buggy driver paranoia. */ unsigned int min = fmt.fmt.pix.width * 2; @@ -279,11 +338,54 @@ init_device(int fd) static void process_image(const int *p, int size) { - GdkPixbuf *pixbuf = gdk_pixbuf_new_from_data(p, GDK_COLORSPACE_RGB, - FALSE, 8, 640, 480, 2 * 640, - NULL, NULL); - gtk_image_set_from_pixbuf(preview_image, pixbuf - ); + clock_t t; + time_t rawtime; + uint8_t *pixels; + double time_taken; + char fname[255]; + GdkPixbuf *pixbuf; + GdkPixbuf *pixbufrot; + GError *error = NULL; + t = clock(); + + dc1394bayer_method_t method = DC1394_BAYER_METHOD_DOWNSAMPLE; + dc1394color_filter_t filter = DC1394_COLOR_FILTER_BGGR; + + if(capture){ + method = DC1394_BAYER_METHOD_SIMPLE; + // method = DC1394_BAYER_METHOD_VNG is slightly sharper but takes 10 seconds; + pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, FALSE, 8, current_width, current_height); + }else{ + pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, FALSE, 8, current_width/2, current_height/2); + } + + pixels = gdk_pixbuf_get_pixels(pixbuf); + dc1394_bayer_decoding_8bit((const uint8_t*)p, pixels, current_width, current_height, filter, method); + if (current_rotate == 0) { + pixbufrot = pixbuf; + } else if (current_rotate == 90) { + pixbufrot = gdk_pixbuf_rotate_simple(pixbuf, GDK_PIXBUF_ROTATE_COUNTERCLOCKWISE); + } else if (current_rotate == 180) { + pixbufrot = gdk_pixbuf_rotate_simple(pixbuf, GDK_PIXBUF_ROTATE_UPSIDEDOWN); + } else if (current_rotate == 270) { + pixbufrot = gdk_pixbuf_rotate_simple(pixbuf, GDK_PIXBUF_ROTATE_CLOCKWISE); + } + if (capture){ + time(&rawtime); + sprintf(fname, "%s/Pictures/Photo-%s.jpg", getenv("HOME"), ctime(&rawtime)); + printf("Saving image\n"); + gdk_pixbuf_save(pixbufrot, fname, "jpeg", &error, "quality", "85", NULL); + if(error != NULL) { + g_printerr(error->message); + g_clear_error(&error); + } + } else { + gtk_image_set_from_pixbuf(preview_image, pixbufrot); + } + capture = 0; + t = clock() - t; + time_taken = ((double)t)/CLOCKS_PER_SEC; + printf("%f fps\n", 1.0/time_taken); } static int @@ -405,42 +507,97 @@ static int config_ini_handler(void *user, const char *section, const char *name, const char *value) { - if (strcmp(section, "preview") == 0) { + if (strcmp(section, "rear") == 0) { if (strcmp(name, "width") == 0) { - preview_width = strtol(value, NULL, 10); + rear_width = strtol(value, NULL, 10); } else if (strcmp(name, "height") == 0) { - preview_height = strtol(value, NULL, 10); + rear_height = strtol(value, NULL, 10); + } else if (strcmp(name, "rotate") == 0) { + rear_rotate = strtol(value, NULL, 10); } else if (strcmp(name, "fmt") == 0) { if (strcmp(value, "RGB") == 0) { - preview_fmt = V4L2_PIX_FMT_RGB24; + rear_fmt = V4L2_PIX_FMT_RGB24; } else if (strcmp(value, "UYVY") == 0) { - preview_fmt = V4L2_PIX_FMT_UYVY; + rear_fmt = V4L2_PIX_FMT_UYVY; } else if (strcmp(value, "YUYV") == 0) { - preview_fmt = V4L2_PIX_FMT_YUYV; + rear_fmt = V4L2_PIX_FMT_YUYV; } else if (strcmp(value, "JPEG") == 0) { - preview_fmt = V4L2_PIX_FMT_JPEG; + rear_fmt = V4L2_PIX_FMT_JPEG; } else if (strcmp(value, "NV12") == 0) { - preview_fmt = V4L2_PIX_FMT_NV12; + rear_fmt = V4L2_PIX_FMT_NV12; } else if (strcmp(value, "YUV420") == 0 || strcmp(value, "I420") == 0 || strcmp(value, "YU12") == 0) { - preview_fmt = V4L2_PIX_FMT_YUV420; + rear_fmt = V4L2_PIX_FMT_YUV420; } else if (strcmp(value, "YVU420") == 0 || strcmp(value, "YV12") == 0) { - preview_fmt = V4L2_PIX_FMT_YVU420; + rear_fmt = V4L2_PIX_FMT_YVU420; + } else if (strcmp(value, "RGGB8") == 0) { + rear_fmt = V4L2_PIX_FMT_SRGGB8; + } else if (strcmp(value, "BGGR8") == 0) { + rear_fmt = V4L2_PIX_FMT_SBGGR8; + rear_mbus = MEDIA_BUS_FMT_SBGGR8_1X8; + } else if (strcmp(value, "GRBG8") == 0) { + rear_fmt = V4L2_PIX_FMT_SGRBG8; + } else if (strcmp(value, "GBRG8") == 0) { + rear_fmt = V4L2_PIX_FMT_SGBRG8; } else { g_printerr("Unsupported pixelformat %s\n", value); exit(1); } + } else if (strcmp(name, "driver") == 0){ + rear_dev_name = strdup(value); } else { - g_printerr("Unknown key '%s' in [preview]\n", name); + g_printerr("Unknown key '%s' in [rear]\n", name); + exit(1); + } + } else if (strcmp(section, "front") == 0) { + if (strcmp(name, "width") == 0) { + front_width = strtol(value, NULL, 10); + } else if (strcmp(name, "height") == 0) { + front_height = strtol(value, NULL, 10); + } else if (strcmp(name, "rotate") == 0) { + front_rotate = strtol(value, NULL, 10); + } else if (strcmp(name, "fmt") == 0) { + if (strcmp(value, "RGB") == 0) { + front_fmt = V4L2_PIX_FMT_RGB24; + } else if (strcmp(value, "UYVY") == 0) { + front_fmt = V4L2_PIX_FMT_UYVY; + } else if (strcmp(value, "YUYV") == 0) { + front_fmt = V4L2_PIX_FMT_YUYV; + } else if (strcmp(value, "JPEG") == 0) { + front_fmt = V4L2_PIX_FMT_JPEG; + } else if (strcmp(value, "NV12") == 0) { + front_fmt = V4L2_PIX_FMT_NV12; + } else if (strcmp(value, "YUV420") == 0 + || strcmp(value, "I420") == 0 + || strcmp(value, "YU12") == 0) { + front_fmt = V4L2_PIX_FMT_YUV420; + } else if (strcmp(value, "YVU420") == 0 + || strcmp(value, "YV12") == 0) { + front_fmt = V4L2_PIX_FMT_YVU420; + } else if (strcmp(value, "RGGB8") == 0) { + front_fmt = V4L2_PIX_FMT_SRGGB8; + } else if (strcmp(value, "BGGR8") == 0) { + front_fmt = V4L2_PIX_FMT_SBGGR8; + front_mbus = MEDIA_BUS_FMT_SBGGR8_1X8; + } else if (strcmp(value, "GRBG8") == 0) { + front_fmt = V4L2_PIX_FMT_SGRBG8; + } else if (strcmp(value, "GBRG8") == 0) { + front_fmt = V4L2_PIX_FMT_SGBRG8; + } else { + g_printerr("Unsupported pixelformat %s\n", value); + exit(1); + } + } else if (strcmp(name, "driver") == 0){ + front_dev_name = strdup(value); + } else { + g_printerr("Unknown key '%s' in [front]\n", name); exit(1); } } else if (strcmp(section, "device") == 0) { - if (strcmp(name, "rear") == 0) { - rear_dev_name = strdup(value); - } else if (strcmp(name, "front") == 0) { - front_dev_name = strdup(value); + if (strcmp(name, "csi") == 0) { + media_drv_name = strdup(value); } else { g_printerr("Unknown key '%s' in [device]\n", name); exit(1); @@ -452,6 +609,173 @@ config_ini_handler(void *user, const char *section, const char *name, return 1; } +int +find_dev_node(int maj, int min, char* fnbuf) +{ + DIR *d; + struct dirent *dir; + struct stat info; + + d = opendir("/dev"); + while ((dir = readdir(d)) != NULL) { + sprintf(fnbuf, "/dev/%s", dir->d_name); + stat(fnbuf, &info); + if (!S_ISCHR(info.st_mode)){ + continue; + } + if (major(info.st_rdev) == maj && minor(info.st_rdev) == min) { + return 0; + } + } + return -1; +} + +int +setup_rear() +{ + struct media_link_desc link = {0}; + + // Disable the interface<->front link + link.flags = 0; + link.source.entity = front_entity_id; + link.source.index = 0; + link.sink.entity = interface_entity_id; + link.sink.index = 0; + + if(xioctl(media_fd, MEDIA_IOC_SETUP_LINK, &link) < 0){ + g_printerr("Could not disable front camera link\n"); + return -1; + } + + // Enable the interface<->rear link + link.flags = MEDIA_LNK_FL_ENABLED; + link.source.entity = rear_entity_id; + link.source.index = 0; + link.sink.entity = interface_entity_id; + link.sink.index = 0; + + if(xioctl(media_fd, MEDIA_IOC_SETUP_LINK, &link) < 0){ + g_printerr("Could not enable rear camera link\n"); + return -1; + } + + current_width = rear_width; + current_height = rear_height; + current_fmt = rear_fmt; + current_rotate = rear_rotate; + // Find camera node + init_sensor(rear_dev, rear_width, rear_height, rear_mbus); + return 0; +} + +int +setup_front() +{ + struct media_link_desc link = {0}; + + // Disable the interface<->rear link + link.flags = 0; + link.source.entity = rear_entity_id; + link.source.index = 0; + link.sink.entity = interface_entity_id; + link.sink.index = 0; + + if(xioctl(media_fd, MEDIA_IOC_SETUP_LINK, &link) < 0){ + g_printerr("Could not disable front camera link\n"); + return -1; + } + + // Enable the interface<->rear link + link.flags = MEDIA_LNK_FL_ENABLED; + link.source.entity = front_entity_id; + link.source.index = 0; + link.sink.entity = interface_entity_id; + link.sink.index = 0; + + if(xioctl(media_fd, MEDIA_IOC_SETUP_LINK, &link) < 0){ + g_printerr("Could not enable rear camera link\n"); + return -1; + } + current_width = front_width; + current_height = front_height; + current_fmt = front_fmt; + current_rotate = front_rotate; + // Find camera node + init_sensor(front_dev, front_width, front_height, front_mbus); + return 0; +} + +int +find_cameras() +{ + struct media_entity_desc entity = {0}; + int ret; + int found = 0; + + while (1) { + entity.id = entity.id | MEDIA_ENT_ID_FLAG_NEXT; + ret = xioctl(media_fd, MEDIA_IOC_ENUM_ENTITIES, &entity); + if (ret < 0){ + break; + } + printf("At node %s, (0x%x)\n", entity.name, entity.type); + if(strncmp(entity.name, front_dev_name, strlen(front_dev_name)) == 0) { + front_entity_id = entity.id; + find_dev_node(entity.dev.major, entity.dev.minor, front_dev); + printf("Found front cam, is %s at %s\n", entity.name, front_dev); + found++; + } + if(strncmp(entity.name, rear_dev_name, strlen(rear_dev_name)) == 0) { + rear_entity_id = entity.id; + find_dev_node(entity.dev.major, entity.dev.minor, rear_dev); + printf("Found rear cam, is %s at %s\n", entity.name, rear_dev); + found++; + } + if (entity.type == MEDIA_ENT_F_IO_V4L) { + interface_entity_id = entity.id; + find_dev_node(entity.dev.major, entity.dev.minor, dev_name); + printf("Found v4l2 interface node at %s\n", dev_name); + } + } + if(found < 2){ + return -1; + } + return 0; +} + + +int +find_media_fd() +{ + DIR *d; + struct dirent *dir; + int fd; + char fnbuf[20]; + struct media_device_info mdi = {0}; + d = opendir("/dev"); + while ((dir = readdir(d)) != NULL) { + if(strncmp(dir->d_name, "media", 5) == 0) { + sprintf(fnbuf, "/dev/%s", dir->d_name); + printf("Checking %s\n", fnbuf); + fd = open(fnbuf, O_RDWR); + xioctl(fd, MEDIA_IOC_DEVICE_INFO, &mdi); + printf("Found media device: %s\n", mdi.driver); + if (strcmp(mdi.driver, media_drv_name) == 0){ + media_fd = fd; + return 0; + } + close(fd); + } + } + return 1; +} + +void +on_shutter_clicked(GtkWidget *widget, gpointer user_data) +{ + capture = 1; +} + int main(int argc, char *argv[]) { @@ -475,8 +799,10 @@ main(int argc, char *argv[]) GObject *window = gtk_builder_get_object(builder, "window"); GObject *preview_box = gtk_builder_get_object(builder, "preview_box"); + GObject *shutter = gtk_builder_get_object(builder, "shutter"); preview_image = gtk_builder_get_object(builder, "preview"); g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL); + g_signal_connect(shutter, "clicked", G_CALLBACK(on_shutter_clicked), NULL); GtkCssProvider *provider = gtk_css_provider_new(); if (access("camera.css", F_OK) != -1) { @@ -501,7 +827,15 @@ main(int argc, char *argv[]) return 1; } - dev_name = rear_dev_name; + if (find_media_fd() == -1) { + g_printerr("Could not find the media node\n"); + return 1; + } + if (find_cameras() == -1) { + g_printerr("Could not find the cameras\n"); + return 1; + } + setup_rear(); int fd = open(dev_name, O_RDWR); if (fd == -1) { @@ -513,9 +847,9 @@ main(int argc, char *argv[]) start_capturing(fd); // Get a new frame every 34ms ~30fps - g_timeout_add(34, get_frame, fd); - + printf("window show\n"); gtk_widget_show(window); + g_idle_add(get_frame, fd); gtk_main(); return 0; } diff --git a/pinephone.ini b/pinephone.ini new file mode 100644 index 0000000..355abb4 --- /dev/null +++ b/pinephone.ini @@ -0,0 +1,16 @@ +[device] +csi=sun6i-csi + +[rear] +driver=ov5640 +width=1280 +height=720 +fmt=BGGR8 +rotate=270 + +[front] +driver=gc2145 +width=1280 +height=720 +fmt=BGGR8 +rotate=90