/** * @file sharp.c * @brief sharpening algorithm * @author Patrick Roth - roth@stettbacher.ch * @version 1.0 * @date 2015-08-27 * @copyright 2012-2016 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 #if (WITH_SIMD == 1) #include // see /usr/lib64/gcc/x86_64-suse-linux/4.7/include/immintrin.h #endif // WITH_SIMD #include "color_pipe_private.h" #include "color.h" #include "filter.h" /** * Transform monochrome to YUV image. * * @param img_yuv On return: image in YUV color space * @param img_mono input monochrome image * @param height image height in number of pixels * @param width image width in number of pixels * @param bit_channel monochrome bit resolution */ static void mono_to_yuv(int16_t *img_yuv, const void *img_mono, const int height, const int width, const int bit_channel) { int y, x, index_mono, index_yuv; const uint8_t *in8 = img_mono; const uint16_t *in16 = img_mono; index_mono = 0; index_yuv = 0; for(y = 0; y < height; y++) { for(x = 0; x < width; x++) { if(bit_channel <= 8) { img_yuv[index_yuv] = in8[index_mono]; img_yuv[index_yuv+1] = 0; img_yuv[index_yuv+2] = 0; } else if(bit_channel <= 16) { img_yuv[index_yuv] = in16[index_mono]; img_yuv[index_yuv+1] = 0; img_yuv[index_yuv+2] = 0; } index_mono++; index_yuv += 3; } } } /** * Transform YUV to monochrome image. * * @param img_mono On return: monochrome image * @param img_yuv input monochrome image * @param height image height in number of pixels * @param width image width in number of pixels * @param bit_channel monochrome bit resolution */ static void yuv_to_mono(void *img_mono, const int16_t *img_yuv, const int height, const int width, const int bit_channel) { int y, x, index_mono, index_yuv; int8_t *out8 = img_mono; int16_t *out16 = img_mono; const int pix_max = (1< pix_max) { value = pix_max; } if(bit_channel <= 8) { out8[index_mono] = value; } else if(bit_channel <= 16) { out16[index_mono] = value; } index_mono++; index_yuv += 3; } } } /** * Sharp pixel by applying 3x3 filter kernel. Fixed-point is used. * The kernel weights around the center are equal. * * @param a_other kernel weight around center * @param a_center kernel weight at center position * @param p11 pixel value at position 1/1 * @param p12 pixel value at position 1/2 * @param p13 pixel value at position 1/3 * @param p21 pixel value at position 2/1 * @param p22 pixel value at position 2/2 * @param p23 pixel value at position 2/3 * @param p31 pixel value at position 3/1 * @param p32 pixel value at position 3/2 * @param p33 pixel value at position 3/3 * @param shift_fact The shifting factor defines how many number of bits the kernel and pixel were shifted to left. * @return filtered pixel value */ static inline int16_t do_sharp(const int16_t a_other, const int16_t a_center, const int16_t p11, const int16_t p12, const int16_t p13, const int16_t p21, const int16_t p22, const int16_t p23, const int16_t p31, const int16_t p32, const int16_t p33, const int shift_fact) { int16_t out; out = ( a_other*p11 + a_other*p12 + a_other*p13 + a_other*p21 + a_center*p22 + a_other*p23 + a_other*p31 + a_other*p32 + a_other*p33) >> shift_fact; return out; } #if 0 /* * Sharpening algorithm by using SSE instructions. * It's slower than the scalar algorithm above!! */ static int16_t do_sharp_sse(__m128i coeff_line0, __m128i coeff_line1, __m128i coeff_line2, __m128i px_line0, __m128i px_line1, __m128i px_line2, __m128i mask, int shift_fact) { __m128i y_line0, y_line1, y_line2, madd_line0, madd_line1, madd_line2; int32_t res[2]; y_line0 = _mm_shuffle_epi8(px_line0, mask); y_line1 = _mm_shuffle_epi8(px_line1, mask); y_line2 = _mm_shuffle_epi8(px_line2, mask); madd_line0 = _mm_madd_epi16(y_line0, coeff_line0); madd_line1 = _mm_madd_epi16(y_line1, coeff_line1); madd_line2 = _mm_madd_epi16(y_line2, coeff_line2); madd_line0 = _mm_hadd_epi32(madd_line0, madd_line1); madd_line0 = _mm_hadd_epi32(madd_line0, madd_line2); madd_line0 = _mm_hadd_epi32(madd_line0, madd_line0); madd_line0 = _mm_hadd_epi32(madd_line0, madd_line0); _mm_storel_epi64((__m128i*)&res, madd_line0); return (res[0] >> shift_fact); } #endif /** * Make given YUV image sharper. Use the given the filter strength to tune the sharpening strength. * If the local sharpening is set only those pixels are sharpened defined at the sharpening mask. * * This sharpening algorithm high-pass filters the input image and adds it to itself. Therfore all edges become sharper. * The sharpening is done on the Y-channel only. The brightness is of interest. The U and V channel won't be touched to avoid color * shiftings. * * @param img_out On return: high-pass filtered YUV image * @param img_in YUV image to filter with given kernel * @param height image height in number of pixels * @param width image width in number of pixels * @param sharp_strength sharpening strength factor * @param max_y maximum Y-channel value (depends on bit per pixel) * @param local_flag not 0 if local sharpening must be done based on sharpening mask * @param sharp_mask sharpening mask (binary image) */ static void make_sharper(int16_t *img_out, const int16_t * img_in, const int height, const int width, const float sharp_strength, const int max_y, const int local_flag, const int8_t *sharp_mask) { int y, x, index_upper, index_center, index_lower; int16_t filter_output; // __m128i coeff_line0, coeff_line1, coeff_line2, mask; /* * don't touch it or check high-pass filter coefficient a_center for overflow!! */ const int shift_fact = 10; /* * High-pass filter coefficients * * e. g. shift_fact = 10 and sharp_strength = 4 * --> a_other = -4/8.0 * 2^10 = 2^9 --> no overflow * --> a_center = 4*2^10 = 2^12 --> no overflow * * e. g. shift_fact = 10 and sharp_strength = 32 * --> a_other = -32/8.0 * 2^10 = 2^12 --> no overflow * --> a_center = 32*2^10 = 2^15 --> overflow because this value cis not possible with int16_t datatype */ const int16_t a_other = -1.0*sharp_strength/8.0*(1<is_color; img_sharp = sharp_data->img_sharp; img_unsharp = sharp_data->img_in; bit_channel = sharp_data->bit_channel; width = sharp_data->width; height = sharp_data->height; img_yuv = sharp_data->img_yuv; sharp_factor = sharp_data->sharp_factor; sharp_alg = sharp_data->sharp_alg; local_sens = sharp_data->local_sens; img_yuv_sharp = sharp_data->img_yuv_sharp; img_sobel = sharp_data->img_sobel; img_gauss = sharp_data->img_gauss; sharp_mask = sharp_data->sharp_mask; /* * Sharpening is done on Y-channel. * In case of color image, the RGB is transformed to YUV. In case of monochrom image, * the Y-channel is used only. */ if(is_color) { // RGB to YUV transformation color_rgb_to_yuv(img_yuv, img_unsharp, height, width, bit_channel); } else { mono_to_yuv(img_yuv, img_unsharp, height, width, bit_channel); } /* * In case of local sharpening, do calculate sharpening mask. */ if(sharp_alg == SHARP_ALG_LOCAL) { filter_sobel_3s16(img_sobel, img_yuv, height, width, 0, 1, 1); filter_gauss_3s16(img_gauss, img_sobel, height, width, 3, 1.0, 0, 1, 1); // incresing the kernel size need more computing performance local_sens = (int)((1.0-local_sens/100.0)*(1<