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