From 38fd591828f923e1f98a253c73d5f7b9fb184d6e Mon Sep 17 00:00:00 2001 From: Martijn Braam Date: Wed, 2 Sep 2020 19:45:28 +0200 Subject: [PATCH] Make the camera work in bayer mode --- CMakeLists.txt | 4 +- bayer.c | 2187 +++++++++++++++++++++++++++++++++++++++++++++++ bayer.h | 87 ++ build/GTKCamera | Bin 0 -> 91808 bytes main.c | 408 ++++++++- pinephone.ini | 16 + 6 files changed, 2663 insertions(+), 39 deletions(-) create mode 100644 bayer.c create mode 100644 bayer.h create mode 100755 build/GTKCamera create mode 100644 pinephone.ini 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 0000000000000000000000000000000000000000..cb0cdbc3a53e12dc1923ad6725869d6f4fb9b108 GIT binary patch literal 91808 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 literal 0 HcmV?d00001 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