aboutsummaryrefslogtreecommitdiffstats
path: root/color_pipe.c
diff options
context:
space:
mode:
authorPatrick Roth <roth@stettbacher.ch>2019-10-04 11:51:48 +0200
committerPatrick Roth <roth@stettbacher.ch>2019-10-04 11:51:48 +0200
commita0f501fa5650d0b6062cc8f26b34bce11137643d (patch)
tree8e31c5ac3409d4ce48887d88d4530b88a02c2660 /color_pipe.c
downloado3000-color-pipe-a0f501fa5650d0b6062cc8f26b34bce11137643d.tar.gz
o3000-color-pipe-a0f501fa5650d0b6062cc8f26b34bce11137643d.zip
initial commit
import from github
Diffstat (limited to 'color_pipe.c')
-rw-r--r--color_pipe.c969
1 files changed, 969 insertions, 0 deletions
diff --git a/color_pipe.c b/color_pipe.c
new file mode 100644
index 0000000..986989f
--- /dev/null
+++ b/color_pipe.c
@@ -0,0 +1,969 @@
+/**
+* @file main.cpp
+* @brief Color Image Processing Pipeline with O-3000 USB camera
+* @author Patrick Roth - roth@stettbacher.ch
+* @version 1.0
+* @date 2015-02-10
+* @copyright 2012-2016 Stettbacher Signal Processing AG
+*
+* @remarks
+*
+* <PRE>
+* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+* </PRE>
+*
+*/
+
+
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <stdio.h>
+
+#include "color_pipe.h"
+#include "color_pipe_private.h"
+#include "color.h"
+
+
+/**
+ * Logging macro for string error logging (libc). This macro inserts strerror(errno) in a suitable way.
+ */
+#define PRINTF_ERRNO(x) \
+ printf(x" in %s() line %d failed: %s\n", __func__, __LINE__-1, strerror(errno));
+
+
+/**
+ * Alignment size in bytes.
+ * Image buffers are aligned to this given boundary.
+ */
+#define ALIGNMENT_SIZE 32
+
+
+/**
+ * Coefficient definition used to undistort lenses.
+ */
+struct o3000_lens_coeffs_t {
+ struct dist_coeff_t dist_coeff; ///< distortion coefficients
+ struct camera_matrix_t camera_matrix; ///< camera matrix
+};
+
+
+/**
+ * Lens undistortion coefficients of various lens types supplied by
+ * Stettbacher Signal Processing.
+ */
+const static struct o3000_lens_coeffs_t o3000_lens_coeffs[] = {
+
+ // S-mount, focal length 2.8m, aperture 2.0 (O3000_LS_F2_8)
+ {
+ .dist_coeff = {
+ .k1 = -1.7989363928888906e+01,
+ .k2 = 4.2371667641386335e+02,
+ .p1 = -5.5177683005299717e-03,
+ .p2 = -1.8027296799469215e-02,
+ .k3 = -5.1212552122750130e+03,
+ },
+
+ .camera_matrix = {
+ .a11 = 5.5641130307342128e+03,
+ .a12 = 0,
+ .a13 = 6.4044160626552366e+02,
+ .a21 = 0,
+ .a22 = 5.5583733034586849e+03,
+ .a23 = 5.3305307740745866e+02,
+ .a31 = 0,
+ .a32 = 0,
+ .a33 = 1.0,
+ },
+ },
+
+ // S-mount, focal length 4.2mm, aperture 1.8 (O3000_LS_F4_2)
+ {
+ .dist_coeff = {
+ .k1 = -5.6100382549536558e+00,
+ .k2 = 3.7504235968196980e+01,
+ .p1 = -1.1849075953406191e-02,
+ .p2 = -2.0833317381133629e-02,
+ .k3 = -1.4657907716904774e+02,
+ },
+
+ .camera_matrix = {
+ .a11 = 3.8385004722247168e+03,
+ .a12 = 0,
+ .a13 = 6.5463814905337483e+02,
+ .a21 = 0,
+ .a22 = 3.8289385545784967e+03,
+ .a23 = 5.4227950629136478e+02,
+ .a31 = 0,
+ .a32 = 0,
+ .a33 = 1.0,
+ },
+ },
+
+ // S-mount, focal length 6.0mm, aperture 1.8 (O3000_LS_F6_0)
+ {
+ .dist_coeff = {
+ .k1 = -3.2037738664730195e+00,
+ .k2 = 1.1127115993662951e+01,
+ .p1 = -1.6455451408872675e-02,
+ .p2 = -2.4114999934222298e-02,
+ .k3 = -1.2882650294739891e+01,
+ },
+
+ .camera_matrix = {
+ .a11 = 3.7083736372135381e+03,
+ .a12 = 0,
+ .a13 = 6.6465346812371035e+02,
+ .a21 = 0,
+ .a22 = 3.6972315248821769e+03,
+ .a23 = 5.5003224793025629e+02,
+ .a31 = 0,
+ .a32 = 0,
+ .a33 = 1.0,
+ },
+ },
+
+ // S-mount, focal length 8.0mm, aperture 1.8 (O3000_LS_F8_0)
+ {
+ .dist_coeff = {
+ .k1 = -2.4661259044966712e+00,
+ .k2 = 1.1778658083457410e+00,
+ .p1 = -8.5928173466905556e-03,
+ .p2 = -1.4375183749585565e-02,
+ .k3 = 1.4290871342330237e+02,
+ },
+
+ .camera_matrix = {
+ .a11 = 4.3637409203781626e+03,
+ .a12 = 0,
+ .a13 = 6.6812858595376599e+02,
+ .a21 = 0,
+ .a22 = 4.3451519470626554e+03,
+ .a23 = 5.5034252965175574e+02,
+ .a31 = 0,
+ .a32 = 0,
+ .a33 = 1.0,
+ },
+ },
+
+ // S-mount, focal length 12.0mm, aperture 1.8 (O3000_LS_F12_0)
+ {
+ .dist_coeff = {
+ .k1 = -5.3454594843785479e+00,
+ .k2 = 6.4871676948306629e+01,
+ .p1 = 1.0455391312916947e-01,
+ .p2 = 4.7057889548236420e-02,
+ .k3 = 1.2045606388669163e+00,
+ },
+
+ .camera_matrix = {
+ .a11 = 1.0122924739235064e+04,
+ .a12 = 0,
+ .a13 = 5.4063808328357356e+02,
+ .a21 = 0,
+ .a22 = 1.0091265861649332e+04,
+ .a23 = 3.2225828876237193e+02,
+ .a31 = 0,
+ .a32 = 0,
+ .a33 = 1.0,
+ },
+ },
+
+ // CS-mount, focal length 2.8mm, aperture 1.6 (O3000_LCS_F2_8)
+ {
+ .dist_coeff = {
+ .k1 = -4.2767583407486480e+00,
+ .k2 = 2.6248731301034013e+01,
+ .p1 = 7.8609123258541538e-03,
+ .p2 = 3.5374054685996053e-03,
+ .k3 = -8.9935343886238059e+01,
+ },
+
+ .camera_matrix = {
+ .a11 = 2.6998000732890600e+03,
+ .a12 = 0,
+ .a13 = 6.3616455649992679e+02,
+ .a21 = 0,
+ .a22 = 2.6987125203839237e+03,
+ .a23 = 4.4895958452543323e+02,
+ .a31 = 0,
+ .a32 = 0,
+ .a33 = 1.0,
+ },
+ },
+
+ // CS-mount, focal length 4.2mm, aperture 1.4 (O3000_LCS_F4_2)
+ {
+ .dist_coeff = {
+ .k1 = -3.7570498088693711e+01,
+ .k2 = 1.5728357422468230e+03,
+ .p1 = 1.1791307984552163e-02,
+ .p2 = -1.3742991959700961e-02,
+ .k3 = 1.0475497983752284e+01,
+ },
+
+ .camera_matrix = {
+ .a11 = 9.9917306224860204e+03,
+ .a12 = 0,
+ .a13 = 6.5441343169200013e+02,
+ .a21 = 0,
+ .a22 = 9.9479425952720158e+03,
+ .a23 = 4.6795575668109700e+02,
+ .a31 = 0,
+ .a32 = 0,
+ .a33 = 1.0,
+ },
+ },
+
+ // CS-mount, focal length 6.0mm, aperture 1.4 (O3000_LCS_F6_0)
+ {
+ .dist_coeff = {
+ .k1 = -2.3964178081799389e+01,
+ .k2 = 4.4902969904416392e+02,
+ .p1 = 2.2481087999585000e-01,
+ .p2 = 1.1427760423539150e-01,
+ .k3 = 1.3202448608914709e+01,
+ },
+
+ .camera_matrix = {
+ .a11 = 1.0267898783331597e+04,
+ .a12 = 0,
+ .a13 = 5.9040975894428607e+02,
+ .a21 = 0,
+ .a22 = 1.0167762137245367e+04,
+ .a23 = 3.7217036432075685e+02,
+ .a31 = 0,
+ .a32 = 0,
+ .a33 = 1.0,
+ },
+ },
+
+ // CS-mount, focal length 8.0mm, aperture 1.4 (O3000_LCS_F8_0)
+ {
+ .dist_coeff = {
+ .k1 = -3.1323351826805144e+01,
+ .k2 = -8.8565542864692248e-01,
+ .p1 = 1.3154594427821961e-01,
+ .p2 = 1.3393386186128195e-01,
+ .k3 = -1.7372379469761756e-03,
+ },
+
+ .camera_matrix = {
+ .a11 = 1.6071195111825766e+04,
+ .a12 = 0,
+ .a13 = 5.9208178498651694e+02,
+ .a21 = 0,
+ .a22 = 1.6265935400534616e+04,
+ .a23 = 4.0867129284489448e+02,
+ .a31 = 0,
+ .a32 = 0,
+ .a33 = 1.0,
+ },
+ },
+
+ // CS-mount, focal length 12.0mm, aperture 1.4 (O3000_LCS_F12_0)
+ {
+ .dist_coeff = {
+ .k1 = -8.7854099735158311e+00,
+ .k2 = 3.0664687310188293e+02,
+ .p1 = -1.5840425493675159e-01,
+ .p2 = -2.4142181141228097e-02,
+ .k3 = 1.4519448386845686e+00,
+ },
+
+ .camera_matrix = {
+ .a11 = 1.2466587046030105e+04,
+ .a12 = 0,
+ .a13 = 6.9244116287526458e+02,
+ .a21 = 0,
+ .a22 = 1.2309699089674952e+04,
+ .a23 = 6.9766565927729926e+02,
+ .a31 = 0,
+ .a32 = 0,
+ .a33 = 1.0,
+ },
+ },
+};
+
+
+/**
+ * Color Correction Matrix for various ambient lights.
+ *
+ * How to get the color correction matrix (CCM):
+ *
+ * 1. Place a 24 patch Macbeth chart in a properly illuminated location. It's recommended to use a
+ * light booth with a normed color temperature (i. g. d65). Otherwise, you can do the
+ * calibration process during a cloudy day because the illument is about d65 (6500 K). Put
+ * the chart in the front of a window and switch off the room light.
+ * 2. Enable auto white balance and camera calibration (lense correction) algorithm. All other algorithms
+ * must be disabled.
+ * 3. Adjust image brightness and make sure that the lower left white patch has a value about 220.
+ * Use the XML-command brightness to reach the defined value.
+ * 4. Save the image and use the software SensorTune from Aptina to get the correction matrix.
+ */
+static const float ccm_presets[][3][3] = {
+
+ // CCM_PRESET_O3020
+ {
+ {1.7392, -0.7660, 0.1968},
+ {-0.2509, 1.5322, -0.1113},
+ {0.0840, -0.4782, 1.5641},
+ },
+};
+
+
+
+
+/**
+ * Initialize pipeline with reasonable default value.
+ *
+ * @param pipe Pointer to pipeline data.
+ */
+static void set_default_value(struct color_pipe_t *pipe) {
+
+ pipe->debayer_data.alg = BAYER_ALG_BILINEAR;
+ pipe->debayer_data.alg_new = pipe->debayer_data.alg;
+
+ pipe->awb_data.enable = 0;
+ pipe->awb_data.gray_threshold = 0.3;
+ pipe->awb_data.gray_threshold_new = pipe->awb_data.gray_threshold;
+ pipe->awb_data.ctrl_k = 0.01;
+ pipe->awb_data.ctrl_k_new = pipe->awb_data.ctrl_k;
+ pipe->awb_data.gain_red = 1.0;
+ pipe->awb_data.gain_blue = 1.0;
+
+ pipe->cam_calib_data.enable = 0;
+ pipe->cam_calib_data.lense = O3000_LS_F2_8;
+ pipe->cam_calib_data.lense_new = pipe->cam_calib_data.lense;
+ memcpy(&(pipe->cam_calib_data.dist_coeff), &o3000_lens_coeffs[pipe->cam_calib_data.lense].dist_coeff, sizeof(struct dist_coeff_t));
+ memcpy(&(pipe->cam_calib_data.camera_matrix), &o3000_lens_coeffs[pipe->cam_calib_data.lense].camera_matrix, sizeof(struct camera_matrix_t));
+ pipe->cam_calib_data.undistort_map_init = 0;
+
+ pipe->color_calib_data.enable = 0;
+ pipe->color_calib_data.ccm = CCM_PRESET_O3020;
+ pipe->color_calib_data.ccm_new = pipe->color_calib_data.ccm;
+ memcpy(pipe->color_calib_data.a, ccm_presets[pipe->color_calib_data.ccm], sizeof(pipe->color_calib_data.a));
+
+ pipe->sharp_data.enable = 0;
+ pipe->sharp_data.sharp_factor = 5.0;
+ pipe->sharp_data.sharp_factor_new = pipe->sharp_data.sharp_factor;
+ pipe->sharp_data.sharp_alg = SHARP_ALG_LOCAL;
+ pipe->sharp_data.sharp_alg_new = pipe->sharp_data.sharp_alg;
+ pipe->sharp_data.local_sens = 94.0;
+ pipe->sharp_data.local_sens_new = pipe->sharp_data.local_sens_new;
+
+ pipe->gamma_data.enable = 0;
+ pipe->gamma_data.gamma = 1.2;
+ pipe->gamma_data.gamma_new = pipe->gamma_data.gamma;
+}
+
+
+/**
+ * Free aligned memory.
+ *
+ * @param buf Pointer to aligned memory to be freed
+ */
+static void do_aligned_free(void *buf) {
+ if(buf != NULL) {
+ ALIGNED_FREE(buf);
+ }
+}
+
+
+/**
+ * Allocate aligned memory.
+ *
+ * @param alignment aligment size in bytes like 8, 16, 32
+ * @param size size in bytes to allocate
+ * @param func for debugging purposes do specify the calling function name
+ * @param line for debugging purposes do specify the line number from calling this function
+ * @return Pointer to aligned allocated memory or NULL on error
+ */
+static void *do_aligned_alloc(size_t alignment, size_t size, const char *func, int line) {
+ void *mem;
+
+ // The image size must be a multiple of the alignment size.
+ if((size % alignment) != 0) {
+ size = ((size/alignment)+1)*alignment;
+ }
+
+ mem = ALIGNED_ALLOC(alignment, size);
+ if(mem == NULL) {
+ printf("%s: aligned_alloc() line %d failed: %s\n", func, line, strerror(errno));
+ return NULL;
+ }
+ return mem;
+}
+
+
+/**
+ * Process raw image at color pipeline.
+ *
+ *
+ * @param color_pipe Pointer to pipeline data.
+ * @param img_buf raw input image
+ * @param img_header image header @see o3000.h
+ */
+void __stdcall color_pipe_process(struct color_pipe_t *__restrict__ color_pipe,
+ void *__restrict__ img_buf,
+ struct img_header_t *__restrict__ img_header) {
+
+ int height, width, bit_channel, is_color;
+ int header_version;
+ enum enumBayerPattern_t bayer_patter;
+ enum enumDataFormat_t raw_format;
+ void *img_out;
+ enum o3000_lenses_t lens_type;
+ enum ccm_preset_t ccm_type;
+
+
+ /*
+ * Extract image header information.
+ */
+ raw_format = (enum enumDataFormat_t) (img_header->format);
+ width = img_header->width;
+ height = img_header->height;
+ bayer_patter = (enum enumBayerPattern_t) (img_header->bayer_pattern);
+ header_version = img_header->version;
+
+ // set bit per pixel
+ if(raw_format == DF_RAW_MONO_8 || raw_format == DF_RAW_BAYER_8) {
+ bit_channel = 8;
+ }
+ else {
+ bit_channel = 12;
+ }
+
+ // set flag to indicate mono or color image
+ if(raw_format == DF_RAW_MONO_8 || raw_format == DF_RAW_MONO_12 || raw_format == DF_HDR_MONO_20_COMP) {
+ is_color = 0;
+ }
+ else {
+ is_color = 1;
+ }
+
+ // set output image to raw image
+ img_out = img_buf;
+
+ /*
+ * Pipeline stage: Demosaicing
+ */
+ if(is_color) {
+ color_pipe->debayer_data.img_raw = img_buf;
+ color_pipe->debayer_data.height = height;
+ color_pipe->debayer_data.width = width;
+ color_pipe->debayer_data.format = raw_format;
+ color_pipe->debayer_data.start_pattern = bayer_patter;
+ debayer(&(color_pipe->debayer_data));
+
+ img_out = color_pipe->debayer_data.img_rgb;
+ }
+
+
+ /*
+ * Pipeline stage: White-Balancing
+ */
+ if(color_pipe->awb_data.enable && is_color) {
+
+ // reset color gains if gray threshold or proportional factor have changed
+ if( color_pipe->awb_data.ctrl_k != color_pipe->awb_data.ctrl_k_new ||
+ color_pipe->awb_data.gray_threshold != color_pipe->awb_data.gray_threshold_new) {
+ color_pipe->awb_data.gain_red = 1;
+ color_pipe->awb_data.gain_blue = 1;
+
+ }
+
+ // apply user parameter (double buffered)
+ color_pipe->awb_data.ctrl_k = color_pipe->awb_data.ctrl_k_new;
+ color_pipe->awb_data.gray_threshold = color_pipe->awb_data.gray_threshold_new;
+
+
+
+ color_pipe->awb_data.img_in = img_out;
+ color_pipe->awb_data.bit_channel = bit_channel;
+ color_pipe->awb_data.height = height;
+ color_pipe->awb_data.width = width;
+ white_balance(&(color_pipe->awb_data));
+
+ img_out = color_pipe->awb_data.img_rgb_balanced;
+ }
+ else {
+ // always reset color gain if stage is disabled
+ color_pipe->awb_data.gain_red = 1;
+ color_pipe->awb_data.gain_blue = 1;
+ }
+
+
+ /*
+ * Pipeline stage: Camera calibration
+ */
+ if(color_pipe->cam_calib_data.enable) {
+
+ // apply user parameter (double buffered)
+ lens_type = color_pipe->cam_calib_data.lense_new;
+ if(color_pipe->cam_calib_data.lense != lens_type) {
+ color_pipe->cam_calib_data.lense = lens_type;
+ memcpy(&(color_pipe->cam_calib_data.dist_coeff), &o3000_lens_coeffs[lens_type].dist_coeff, sizeof(struct dist_coeff_t));
+ memcpy(&(color_pipe->cam_calib_data.camera_matrix), &o3000_lens_coeffs[lens_type].camera_matrix, sizeof(struct camera_matrix_t));
+ }
+
+ color_pipe->cam_calib_data.img_in = img_out;
+ color_pipe->cam_calib_data.is_color = is_color;
+
+ // reninit undistortion map if image format or resolution have changed
+ if( color_pipe->cam_calib_data.bit_channel != bit_channel ||
+ color_pipe->cam_calib_data.tot_width != width ||
+ color_pipe->cam_calib_data.tot_height != height) {
+
+ color_pipe->cam_calib_data.undistort_map_init = 0;
+ }
+
+
+ color_pipe->cam_calib_data.bit_channel = bit_channel;
+ color_pipe->cam_calib_data.tot_width = width;
+ color_pipe->cam_calib_data.tot_height = height;
+
+ // field-of-view available since O-3000 image header version 4
+ if(header_version >= 4) {
+ // reninit undistortion map if field-of-view has changed
+ if( color_pipe->cam_calib_data.fov_x_start != img_header->fov_x_start ||
+ color_pipe->cam_calib_data.fov_x_end != img_header->fov_x_end ||
+ color_pipe->cam_calib_data.fov_y_start != img_header->fov_y_start ||
+ color_pipe->cam_calib_data.fov_y_end != img_header->fov_y_end) {
+
+ color_pipe->cam_calib_data.undistort_map_init = 0;
+ }
+ color_pipe->cam_calib_data.fov_x_start = img_header->fov_x_start;
+ color_pipe->cam_calib_data.fov_x_end = img_header->fov_x_end;
+ color_pipe->cam_calib_data.fov_y_start = img_header->fov_y_start;
+ color_pipe->cam_calib_data.fov_y_end = img_header->fov_y_end;
+ }
+ else {
+ // assume that image is displayed without ROI (region-of-interest)
+ color_pipe->cam_calib_data.fov_x_start = 0;
+ color_pipe->cam_calib_data.fov_x_end = width-1;
+ color_pipe->cam_calib_data.fov_y_start = 0;
+ color_pipe->cam_calib_data.fov_y_end = height-1;
+ }
+ camera_calib(&(color_pipe->cam_calib_data));
+
+ img_out = color_pipe->cam_calib_data.img_calib;
+ }
+
+
+ /*
+ * Pipeline stage: Color Correction
+ */
+ if(color_pipe->color_calib_data.enable && is_color) {
+
+ // apply user parameter (double buffered)
+ ccm_type = color_pipe->color_calib_data.ccm_new;
+ if(color_pipe->color_calib_data.ccm != ccm_type) {
+ color_pipe->color_calib_data.ccm = ccm_type;
+ memcpy(color_pipe->color_calib_data.a, ccm_presets[ccm_type], sizeof(color_pipe->color_calib_data.a));
+ }
+
+ color_pipe->color_calib_data.img_in = img_out;
+ color_pipe->color_calib_data.bit_channel = bit_channel;
+ color_pipe->color_calib_data.width = width;
+ color_pipe->color_calib_data.height = height;
+ color_calib(&(color_pipe->color_calib_data));
+
+ img_out = color_pipe->color_calib_data.img_calib;
+ }
+
+
+ /*
+ * Pipeline stage: Image sharpening
+ */
+ if(color_pipe->sharp_data.enable) {
+
+ // apply user parameter (double buffered)
+ color_pipe->sharp_data.sharp_factor = color_pipe->sharp_data.sharp_factor_new;
+ color_pipe->sharp_data.sharp_alg = color_pipe->sharp_data.sharp_alg_new;
+ color_pipe->sharp_data.local_sens = color_pipe->sharp_data.local_sens_new;
+
+ color_pipe->sharp_data.img_in = img_out;
+ color_pipe->sharp_data.is_color = is_color;
+ color_pipe->sharp_data.bit_channel = bit_channel;
+ color_pipe->sharp_data.width = width;
+ color_pipe->sharp_data.height = height;
+ sharpening(&(color_pipe->sharp_data));
+
+ img_out = color_pipe->sharp_data.img_sharp;
+ }
+
+
+ /*
+ * Pipeline stage: Gamma correction
+ */
+ if(color_pipe->gamma_data.enable) {
+
+ // apply user parameter (double buffered)
+ color_pipe->gamma_data.gamma = color_pipe->gamma_data.gamma_new;
+
+ color_pipe->gamma_data.img_in = img_out;
+ color_pipe->gamma_data.is_color = is_color;
+ color_pipe->gamma_data.bit_channel = bit_channel;
+ color_pipe->gamma_data.width = width;
+ color_pipe->gamma_data.height = height;
+ gamma_corr(&(color_pipe->gamma_data));
+
+ img_out = color_pipe->gamma_data.img_gamma;
+ }
+
+
+ /*
+ * Return processed image depending on active pipeline stages.
+ */
+ color_pipe->img_out = img_out;
+ color_pipe->is_color = is_color;
+ color_pipe->bit_channel = bit_channel;
+ color_pipe->width = width;
+ color_pipe->height = height;
+}
+
+
+/**
+ * Pipeline stage configuration: Auto-White-Balancing
+ *
+ * @param color_pipe Pointer to pipeline context
+ * @param enable not 0: enable, 0: disable
+ * @param alg demosaicing algorithm type
+ */
+void __stdcall color_pipe_stageconf_debayer(struct color_pipe_t *color_pipe, enum bayer_alg_t alg) {
+
+ // paranoia
+ if(color_pipe == NULL) {
+ printf("%s: Pipeline pointer is NULL!\n", __func__);
+ return;
+ }
+ color_pipe->debayer_data.alg_new = alg;
+}
+
+
+/**
+ * Pipeline stage configuration: Auto-White-Balancing
+ *
+ * @param color_pipe Pointer to pipeline context
+ * @param enable not 0: enable, 0: disable
+ * @param gray_threshold gray threshold (default 0.3)
+ * @param ctrl_gain gray threshold (default 0.01)
+ */
+void __stdcall color_pipe_stageconf_awb(struct color_pipe_t *color_pipe, int enable, float gray_threshold, float ctrl_gain) {
+
+ // paranoia
+ if(color_pipe == NULL) {
+ printf("%s: Pipeline pointer is NULL!\n", __func__);
+ return;
+ }
+
+ color_pipe->awb_data.enable = enable;
+ color_pipe->awb_data.gray_threshold_new = gray_threshold;
+ color_pipe->awb_data.ctrl_k_new = ctrl_gain;
+}
+
+
+/**
+ * Pipeline stage configuration: Camera Calibration
+ *
+ * @param color_pipe Pointer to pipeline context
+ * @param enable not 0: enable, 0: disable
+ * @param lense initialize pipeline stage with given lense type
+ */
+void __stdcall color_pipe_stageconf_cam_calib(struct color_pipe_t *color_pipe, int enable, enum o3000_lenses_t lense) {
+
+ // paranoia
+ if(color_pipe == NULL) {
+ printf("%s: Pipeline pointer is NULL!\n", __func__);
+ return;
+ }
+
+ // range check
+ if(lense < 0 || lense >= (sizeof(o3000_lens_coeffs)/sizeof(struct o3000_lens_coeffs_t))) {
+ printf("%s: Invalid lense type %d\n", __func__, lense);
+ return;
+ }
+
+ color_pipe->cam_calib_data.enable = enable;
+ color_pipe->cam_calib_data.lense_new = lense;
+ color_pipe->cam_calib_data.undistort_map_init = 0;
+}
+
+
+/**
+ * Pipeline stage configuration: Color Calibration
+ *
+ * @param color_pipe Pointer to pipeline context
+ * @param enable not 0: enable, 0: disable
+ * @param ccm_preset initialize pipeline stage with given color correction preset data
+ */
+void __stdcall color_pipe_stageconf_color_calib(struct color_pipe_t *color_pipe, int enable,
+ enum ccm_preset_t ccm_preset) {
+
+ // paranoia
+ if(color_pipe == NULL) {
+ printf("%s: Pipeline pointer is NULL!\n", __func__);
+ return;
+ }
+
+ // range check
+ if(ccm_preset < 0 || ccm_preset >= (sizeof(ccm_presets)/sizeof(ccm_presets[0]))) {
+ printf("%s: Invalid color type %d\n", __func__, ccm_preset);
+ return;
+ }
+
+ color_pipe->color_calib_data.enable = enable;
+ color_pipe->color_calib_data.ccm_new = ccm_preset;
+}
+
+
+/**
+ * Pipeline stage configuration: Sharpening
+ *
+ * @param color_pipe Pointer to pipeline context
+ * @param enable not 0: enable, 0: disable
+ * @param factor sharpening factor (default 5.0)
+ * @param alg algorithm type
+ * @param sens sensitivity (default 94.0)
+ */
+void __stdcall color_pipe_stageconf_sharp(struct color_pipe_t *color_pipe, int enable,
+ float factor, enum sharp_alg_t alg, float sens) {
+
+ // paranoia
+ if(color_pipe == NULL) {
+ printf("%s: Pipeline pointer is NULL!\n", __func__);
+ return;
+ }
+ color_pipe->sharp_data.enable = enable;
+ color_pipe->sharp_data.sharp_factor_new = factor;
+ color_pipe->sharp_data.sharp_alg_new = alg;
+ color_pipe->sharp_data.local_sens_new = sens;
+}
+
+
+/**
+ * Pipeline stage configuration: Gamma Correction
+ *
+ * @param color_pipe Pointer to pipeline context
+ * @param enable not 0: enable, 0: disable
+ * @param gamma gamma factor (1.0 means no gamma correction, default 1.2)
+ */
+void __stdcall color_pipe_stageconf_gamma(struct color_pipe_t *color_pipe, int enable, float gamma) {
+
+ // paranoia
+ if(color_pipe == NULL) {
+ printf("%s: Pipeline pointer is NULL!\n", __func__);
+ return;
+ }
+ color_pipe->gamma_data.enable = enable;
+ color_pipe->gamma_data.gamma_new = gamma;
+}
+
+
+/**
+ * Open color image processing pipeline.
+ * This function allocates memory for various pipe algorithm. The pipeline is set up for a maximum possible image size defined
+ * by the height, width and bitdepth per color channel.
+ *
+ * NOTE
+ * This function uses dynamic memory allocation. If the pipeline isn't use anymore do close it by calling @ref color_pipe_close.
+ *
+ * @param color_pipe On return: Pointer to pipeline data. Dynamic memory is allocated.
+ * @param max_img_height maximum possible image height in number of pixels
+ * @param max_img_width maximum possible image width in number of pixels
+ * @param bits_per_channel maximum possible number of bits per color channel
+ * @return 0 on success, -1 on error
+ */
+int __stdcall color_pipe_open(struct color_pipe_t **color_pipe, const int max_img_height, const int max_img_width,
+ const int bits_per_channel) {
+
+ int byte_per_pixel, max_img_size, max_img_size_yuv, max_img_size_binary;
+ struct color_pipe_t *data;
+
+ if(color_pipe == NULL) {
+ printf("%s: Pipeline data pointer is NULL!\n", __func__);
+ return -1;
+ }
+
+ data = calloc(1, sizeof(struct color_pipe_t));
+ if(data == NULL) {
+ PRINTF_ERRNO("calloc");
+ return -1;
+ }
+
+
+ /*
+ * Calculate the number of bytes per pixel are used for a color image.
+ * Always, a color image has 3 channels with the given bit-depth max_img_bpp.
+ *
+ * e. g. 8 bits-per-channel results to 3 byte per pixel
+ * 12 bits-per-channel results to 6 byte per pixel
+ */
+ if((bits_per_channel%8) == 0) {
+ byte_per_pixel = bits_per_channel/8;
+ }
+ else {
+ byte_per_pixel = bits_per_channel/8 + 1;
+ }
+ byte_per_pixel *= 3;
+
+
+ /*
+ * Do calculate the maximum possible image size.
+ */
+ max_img_size = max_img_height*max_img_width*byte_per_pixel;
+
+
+ /*
+ * The YUV image uses 16 bit-per-channel always.
+ */
+ max_img_size_yuv = max_img_height*max_img_width*3*2;
+
+
+ /*
+ * The binary image uses 8 bit-per-channel always.
+ */
+ max_img_size_binary = max_img_height*max_img_width*3;
+
+
+ /*
+ * Important note for dynamic memory allocation:
+ * Various pipeline algorithms are using SIMD instructions like SSE2 (128 bit register) and AVX (256 bit registers). Therfore any
+ * image buffer is allocated to a 32-byte boundary. Using SIMD instructions on a unaligned buffer may generate a general-protection exception.
+ */
+
+ // allocate memory for demosaicing algorithm
+ data->debayer_data.img_rgb = do_aligned_alloc(ALIGNMENT_SIZE, max_img_size, __func__, __LINE__-1);
+ if(data->debayer_data.img_rgb == NULL) {
+ goto _pipe_open_abort;
+ }
+
+ // allocate memory for auto white balancing algorithm
+ data->awb_data.img_rgb_balanced = do_aligned_alloc(ALIGNMENT_SIZE, max_img_size, __func__, __LINE__-1);
+ if(data->awb_data.img_rgb_balanced == NULL) {
+ goto _pipe_open_abort;
+ }
+ data->awb_data.img_yuv = do_aligned_alloc(ALIGNMENT_SIZE, max_img_size_yuv, __func__, __LINE__-1);
+ if(data->awb_data.img_yuv == NULL) {
+ goto _pipe_open_abort;
+ }
+
+ // allocate memory for camera calibration algorithm
+ data->cam_calib_data.img_calib = do_aligned_alloc(ALIGNMENT_SIZE, max_img_size, __func__, __LINE__-1);
+ if(data->cam_calib_data.img_calib == NULL) {
+ goto _pipe_open_abort;
+ }
+ data->cam_calib_data.calib_map = do_aligned_alloc(ALIGNMENT_SIZE,
+ sizeof(struct lense_undistort_coord_t)*max_img_height*max_img_width,
+ __func__, __LINE__-1);
+ if(data->cam_calib_data.calib_map == NULL) {
+ goto _pipe_open_abort;
+ }
+
+ // allocate memory for color calibration algorithm
+ data->color_calib_data.img_calib = do_aligned_alloc(ALIGNMENT_SIZE, max_img_size, __func__, __LINE__-1);
+ if(data->color_calib_data.img_calib == NULL) {
+ goto _pipe_open_abort;
+ }
+
+ // allocate memory for sharpening algorithm
+ data->sharp_data.img_sharp = do_aligned_alloc(ALIGNMENT_SIZE, max_img_size, __func__, __LINE__-1);
+ if(data->sharp_data.img_sharp == NULL) {
+ goto _pipe_open_abort;
+ }
+ data->sharp_data.img_yuv = do_aligned_alloc(ALIGNMENT_SIZE, max_img_size_yuv, __func__, __LINE__-1);
+ if(data->sharp_data.img_yuv == NULL) {
+ goto _pipe_open_abort;
+ }
+ data->sharp_data.img_yuv_sharp = do_aligned_alloc(ALIGNMENT_SIZE, max_img_size_yuv, __func__, __LINE__-1);
+ if(data->sharp_data.img_yuv_sharp == NULL) {
+ goto _pipe_open_abort;
+ }
+ data->sharp_data.img_sobel = do_aligned_alloc(ALIGNMENT_SIZE, max_img_size_yuv, __func__, __LINE__-1);
+ if(data->sharp_data.img_sobel == NULL) {
+ goto _pipe_open_abort;
+ }
+ data->sharp_data.img_gauss = do_aligned_alloc(ALIGNMENT_SIZE, max_img_size_yuv, __func__, __LINE__-1);
+ if(data->sharp_data.img_gauss == NULL) {
+ goto _pipe_open_abort;
+ }
+ data->sharp_data.sharp_mask = do_aligned_alloc(ALIGNMENT_SIZE, max_img_size_binary, __func__, __LINE__-1);
+ if(data->sharp_data.sharp_mask == NULL) {
+ goto _pipe_open_abort;
+ }
+
+ // allocate memory for gamma correction algorithm
+ data->gamma_data.img_gamma = do_aligned_alloc(ALIGNMENT_SIZE, max_img_size, __func__, __LINE__-1);
+ if(data->gamma_data.img_gamma == NULL) {
+ goto _pipe_open_abort;
+ }
+
+ // Lookup table size depends on bits per color channel.
+ data->gamma_data.gamma_table = do_aligned_alloc(ALIGNMENT_SIZE, (1<<bits_per_channel)*sizeof(int), __func__, __LINE__-1);
+ if(data->gamma_data.gamma_table == NULL) {
+ goto _pipe_open_abort;
+ }
+
+ set_default_value(data);
+ *color_pipe = data;
+
+ // detect CPU features
+#if (WITH_SIMD == 1)
+ if(cpu_feature_init()) {
+ printf("%s: Detecting CPU features failed\n", __func__);
+ }
+#endif // WITH_SIMD
+ return 0;
+
+_pipe_open_abort:
+ color_pipe_close(data);
+ return -1;
+}
+
+
+/**
+ * Close color image processing pipeline.
+ * This function cleans up the pipeline and is freeing the used memory.
+ *
+ * @param data Pointer to pipeline data
+ */
+int __stdcall color_pipe_close(struct color_pipe_t *data) {
+
+ if(data == NULL) {
+ printf("%s: Pipeline data pointer is NULL!\n", __func__);
+ return -1;
+ }
+
+ // free various image buffers
+ do_aligned_free(data->debayer_data.img_rgb);
+ do_aligned_free(data->awb_data.img_rgb_balanced);
+ do_aligned_free(data->awb_data.img_yuv);
+ do_aligned_free(data->cam_calib_data.img_calib);
+ do_aligned_free(data->cam_calib_data.calib_map);
+ do_aligned_free(data->color_calib_data.img_calib);
+ do_aligned_free(data->sharp_data.img_sharp);
+ do_aligned_free(data->sharp_data.img_yuv);
+ do_aligned_free(data->sharp_data.img_yuv_sharp);
+ do_aligned_free(data->sharp_data.img_sobel);
+ do_aligned_free(data->sharp_data.img_gauss);
+ do_aligned_free(data->sharp_data.sharp_mask);
+ do_aligned_free(data->gamma_data.img_gamma);
+ do_aligned_free(data->gamma_data.gamma_table);
+
+ // free various image buffers
+ free(data);
+ return 0;
+}