/**
* @file color_space.c
* @brief color space conversion utilities
* @author Patrick Roth - roth@stettbacher.ch
* @copyright Stettbacher Signal Processing AG
*
* @remarks
*
*
* 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
*
*
*/
#include
#include
#include
#if (WITH_SIMD == 1)
#include // see /usr/lib64/gcc/x86_64-suse-linux/4.7/include/immintrin.h
#endif // WITH_SIMD
#include "color.h"
#include "color_pipe_private.h"
/**
* RGB image (16 bit per color channel) to YUV color space conversion (scalar code).
*
* @param img_yuv On return: image in YUV color space (this buffer must be allocated externly)
* @param img_rgb RGB image
* @param height image height in number of pixels
* @param width image width in number of pixels
*/
static void rgb16_to_yuv_scalar(int16_t *img_yuv, const uint16_t *img_rgb, const int height, const int width)
#include "alg_rgb_to_yuv.h"
#ifdef __SSSE3__
/**
* RGB image (8 bit per color channel) to YUV color space conversion (vector code).
*
* @param img_yuv On return: image in YUV color space (this buffer must be allocated externly)
* @param img_rgb RGB image
* @param height image height in number of pixels
* @param width image width in number of pixels
*/
static void rgb8_to_yuv_vector(int16_t *img_yuv, const uint8_t *img_rgb, const int height, const int width) {
int i, size_mod8;
uint8_t *input_img;
int px_blue, px_green, px_red;
int px_y, px_u, px_v;
const int scale_fact = 7; // more than 7 is not possible due to overflow
const int y_r = (int)roundf(RGB2YUV_COEFF_Y_RED*(1< YUV transformation coefficient
* The values are scaled defined by scale_fact.
*
* y = 0.299*red + 0.587*green + 0.114*blue
* u = -0.299*red - 0.587*green + 0.886*blue
* v = 0.701*red - 0.587*green - 0.114*blue
*/
coeff_y_red = _mm_set1_epi16((short)y_r);
coeff_y_green = _mm_set1_epi16((short)y_g);
coeff_y_blue = _mm_set1_epi16((short)y_b);
coeff_u_red = _mm_set1_epi16((short)u_r);
coeff_u_green = _mm_set1_epi16((short)u_g);
coeff_u_blue = _mm_set1_epi16((short)u_b);
coeff_v_red = _mm_set1_epi16((short)v_r);
coeff_v_green = _mm_set1_epi16((short)v_g);
coeff_v_blue = _mm_set1_epi16((short)v_b);
// process 8 RGB-pixel-pair in parallel
for(i = 0; i < size_mod8; i+=8) {
// load 128 bit pixel value into SIMD register
px_buf1 = _mm_lddqu_si128((__m128i*)(input_img));
input_img += 16;
px_buf2 = _mm_lddqu_si128((__m128i*)(input_img));
input_img += 8;
// get first 6 red pixels
red1 = _mm_shuffle_epi8(px_buf1, mask_red1);
// get next 2 red pixels
red2 = _mm_shuffle_epi8(px_buf2, mask_red2);
// combine to 8 red pixels
red = _mm_or_si128(red1, red2);
// get first 5 green pixels
green1 = _mm_shuffle_epi8(px_buf1, mask_green1);
// get next 3 green pixels
green2 = _mm_shuffle_epi8(px_buf2, mask_green2);
// combine to 8 green pixels
green = _mm_or_si128(green1, green2);
// get first 5 blue pixels
blue1 = _mm_shuffle_epi8(px_buf1, mask_blue1);
// get next 3 blue pixels
blue2 = _mm_shuffle_epi8(px_buf2, mask_blue2);
// combine to 8 blue pixels
blue = _mm_or_si128(blue1, blue2);
/*
* calculate 8 Y-channel pixels
*/
red_tmp = _mm_mullo_epi16(coeff_y_red, red);
green_tmp = _mm_mullo_epi16(coeff_y_green, green);
blue_tmp = _mm_mullo_epi16(coeff_y_blue, blue);
ch_y = _mm_add_epi16(_mm_add_epi16(red_tmp, green_tmp), blue_tmp);
ch_y = _mm_srai_epi16(ch_y, scale_fact);
/*
* calculate 8 U-channel pixels
*/
red_tmp = _mm_mullo_epi16(coeff_u_red, red);
green_tmp = _mm_mullo_epi16(coeff_u_green, green);
blue_tmp = _mm_mullo_epi16(coeff_u_blue, blue);
ch_u = _mm_add_epi16(_mm_add_epi16(red_tmp, green_tmp), blue_tmp);
ch_u = _mm_srai_epi16(ch_u, scale_fact);
/*
* calculate 8 V-channel pixels
*/
red_tmp = _mm_mullo_epi16(coeff_v_red, red);
green_tmp = _mm_mullo_epi16(coeff_v_green, green);
blue_tmp = _mm_mullo_epi16(coeff_v_blue, blue);
ch_v = _mm_add_epi16(_mm_add_epi16(red_tmp, green_tmp), blue_tmp);
ch_v = _mm_srai_epi16(ch_v, scale_fact);
/*
* Store separate YUV buffer to one YUV image stream
*/
y1 = _mm_shuffle_epi8(ch_y, mask_y1);
y2 = _mm_shuffle_epi8(ch_y, mask_y2);
y3 = _mm_shuffle_epi8(ch_y, mask_y3);
u1 = _mm_shuffle_epi8(ch_u, mask_u1);
u2 = _mm_shuffle_epi8(ch_u, mask_u2);
u3 = _mm_shuffle_epi8(ch_u, mask_u3);
v1 = _mm_shuffle_epi8(ch_v, mask_v1);
v2 = _mm_shuffle_epi8(ch_v, mask_v2);
v3 = _mm_shuffle_epi8(ch_v, mask_v3);
yuv1 = _mm_or_si128(y1, _mm_or_si128(u1, v1));
yuv2 = _mm_or_si128(y2, _mm_or_si128(u2, v2));
yuv3 = _mm_or_si128(y3, _mm_or_si128(u3, v3));
/*
* Store 3 YUV SIMD register into memory
*/
_mm_store_si128((__m128i*)img_yuv, yuv1);
img_yuv += 8;
_mm_store_si128((__m128i*)img_yuv, yuv2);
img_yuv += 8;
_mm_store_si128((__m128i*)img_yuv, yuv3);
img_yuv += 8;
}
for(i = size_mod8; i < (width*height); i++) {
// put each RGB color on stack
px_red = *input_img;
input_img++;
px_green = *input_img;
input_img++;
px_blue = *input_img;
// color space conversion from RGB to YUV
px_y = (y_r*px_red + y_g*px_green + y_b*px_blue) >> scale_fact;
px_u = (u_r*px_red + u_g*px_green + u_b*px_blue) >> scale_fact;
px_v = (v_r*px_red + v_g*px_green + v_b*px_blue) >> scale_fact;
*img_yuv = px_y;
img_yuv++;
*img_yuv = px_u;
img_yuv++;
*img_yuv = px_v;
img_yuv++;
}
}
#else
/**
* RGB image (8 bit per color channel) to YUV color space conversion (scalar code).
*
* @param img_yuv On return: image in YUV color space (this buffer must be allocated externly)
* @param img_rgb RGB image
* @param height image height in number of pixels
* @param width image width in number of pixels
*/
static void rgb8_to_yuv_scalar(int16_t *img_yuv, const uint8_t *img_rgb, const int height, const int width)
#include "alg_rgb_to_yuv.h"
#endif // __SSSE3__
/**
* YUV to RGB (8 bit per color channel) color space conversion.
*
* @param img_rgb On return: image in RGB space (this buffer must be allocated externly)
* @param img_yuv YUV image
* @param height image height in number of pixels
* @param width image width in number of pixels
* @return 0 on success otherwise -1
*/
static void yuv_to_rgb8(uint8_t *img_rgb, const int16_t *img_yuv, const int height, const int width, const int pix_max)
#include "alg_yuv_to_rgb.h"
/**
* YUV to RGB (8 bit per color channel) color space conversion.
*
* @param img_rgb On return: image in RGB space (this buffer must be allocated externly)
* @param img_yuv YUV image
* @param height image height in number of pixels
* @param width image width in number of pixels
* @return 0 on success otherwise -1
*/
static void yuv_to_rgb16(uint16_t *img_rgb, const int16_t *img_yuv, const int height, const int width, const int pix_max)
#include "alg_yuv_to_rgb.h"
/**
* Apply color correction matrix on given RGB image (8 bit per color channel).
*
* @param img_calib On return: color calibrated image
* @param img_uncalib input image to calibrate
* @param color_bit_depth color channel bit depth (all channel have the same bit depth)
* @param height image height in number of pixels
* @param width image width in number of pixels
* @param a 3x3 color correction matrix
*/
static void rgb_color_correction16(uint16_t *img_calib, const uint16_t *img_uncalib,
const int color_bit_depth, const int height, const int width, float a[3][3])
#include "alg_ccm.h"
/**
* Apply color correction matrix on given RGB image (8 bit per color channel).
*
* @param img_calib On return: color calibrated image
* @param img_uncalib input image to calibrate
* @param color_bit_depth color channel bit depth (all channel have the same bit depth)
* @param height image height in number of pixels
* @param width image width in number of pixels
* @param a 3x3 color correction matrix
*/
static void rgb_color_correction8(uint8_t *img_calib, const uint8_t *img_uncalib,
const int color_bit_depth, const int height, const int width, float a[3][3])
#include "alg_ccm.h"
/**
* RGB to YUV color space conversion.
*
* @param img_yuv On return: image in YUV color space (this buffer must be allocated externly)
* @param img_rgb RGB image
* @param height image height in number of pixels
* @param width image width in number of pixels
* @param bit_channel bits per color channel
* @return 0 on success otherwise -1
*/
int color_rgb_to_yuv(int16_t *img_yuv, const void *img_rgb, const int height, const int width, const int bit_channel) {
if(bit_channel <= 8) {
#ifdef __SSSE3__
rgb8_to_yuv_vector(img_yuv, img_rgb, height, width);
#else
rgb8_to_yuv_scalar(img_yuv, img_rgb, height, width);
#endif
}
else if(bit_channel <= 16) {
rgb16_to_yuv_scalar(img_yuv, img_rgb, height, width);
}
return 0;
}
/**
* YUV to RGB color space conversion.
*
* @param img_rgb On return: image in RGB space (this buffer must be allocated externly)
* @param img_yuv YUV image
* @param height image height in number of pixels
* @param width image width in number of pixels
* @param bit_channel bits per color channel of RGB image
* @return 0 on success otherwise -1
*/
int color_yuv_to_rgb(void *img_rgb, const int16_t *img_yuv, const int height, const int width, const int bit_channel) {
int ret = 0;
int bpp = (1<bit_channel;
ret = 0;
if(bit_channel <= 8) {
rgb_color_correction8(color_calib->img_calib, color_calib->img_in, 8, color_calib->height, color_calib->width, color_calib->a);
}
else if(bit_channel <= 16) {
rgb_color_correction16(color_calib->img_calib, color_calib->img_in, 12, color_calib->height, color_calib->width, color_calib->a);
}
else {
printf("%s: Color calibration not possible on images with %d bits per color channel\n", __func__, bit_channel);
ret = -1;
}
return ret;
}