How to perform skin tone matching - opencv

( face )
( body )
Hi, i am new to image processing and openCV C/C++. I am wondering that is it possible to extract skin tone from the first image (face). And then applied to the second image (body).
In other words, user upload his face image and the program extract the skin tone from that image and apply it to the body.
Thanks,
Aisha

This is a hard problem to solve, especially given the variation of colours depending on lighting and reflection. I have worked previously on finding skin in images, and generally the Cr (chroma red) component of the YCbCr colour space stands out prominently on skin. You might be able to exploit this information to find skin regions.
Here are a couple of papers that attempt to use colour for locating human skin:
1. Interaction between hands and wearable cameras
2. Markerless inspection of augmented reality objects

For finding skin you can use one of this formulas:
1) With normilized RGB space:
for(int i = 0; i < m_image->height; ++i)
{
for(int j = 0; j < m_image->width; ++j)
{
if (m_image->nChannels == 3)
{
int valueR = (reinterpret_cast<uchar*>(m_image->imageData + i * m_image->widthStep))[j * 3 + 2];
int valueG = (reinterpret_cast<uchar*>(m_image->imageData + i * m_image->widthStep))[j * 3 + 1];
int valueB = (reinterpret_cast<uchar*>(m_image->imageData + i * m_image->widthStep))[j * 3];
float normR = static_cast<float>(valueR) / static_cast<float>(valueR + valueG + valueB);
float normG = static_cast<float>(valueG) / static_cast<float>(valueR + valueG + valueB);
float normB = static_cast<float>(valueB) / static_cast<float>(valueR + valueG + valueB);
if ((normB / normG < 1.249) &&
(( normR + normG + normB ) / ( 3 * normR ) > 0.696 ) &&
( 1/3.0 - normB/( normR + normG + normB ) > 0.014 ) &&
(normG/(3* (normR + normG + normB)) < 0.108 ))
{
//pixel is skin
}
}
}
2) in RGB space:
for(size_t i = 0; i < m_image->height; ++i)
{
for(size_t j = 0; j < m_image->width; ++j)
{
if (m_image->nChannels == 3)
{
int R = (reinterpret_cast<uchar*>(m_image->imageData + i * m_image->widthStep))[j * 3 + 2];
int G = (reinterpret_cast<uchar*>(m_image->imageData + i * m_image->widthStep))[j * 3 + 1];
int B = (reinterpret_cast<uchar*>(m_image->imageData + i * m_image->widthStep))[j * 3];
if (( R > 95) && ( G > 40 ) && ( B > 20 ) &&
(std::max(R, std::max( G, B) ) - std::min(R, std::min(G, B) ) > 15) &&
(std::abs(R - G) > 15) && (R > G) && (R > B))
{
//skin pixel
}
}
}
3) in YCrCb space:
for(size_t i = 0; i < m_image->height; ++i)
{
for(size_t j = 0; j < m_image->width; ++j)
{
if (m_image->nChannels == 3)
{
int Cr = (reinterpret_cast<uchar*>(image->imageData + i * image->widthStep))[j * 3 + 2];
int Cb = (reinterpret_cast<uchar*>(image->imageData + i * image->widthStep))[j * 3 + 1];
int Y = (reinterpret_cast<uchar*>(image->imageData + i * image->widthStep))[j * 3];
if (( Y > 80 ) && ( Cb > 85 ) && ( Cb < 135 ) &&
(Cr > 135) && (Cr < 180))
{
//skin pixel
}
}
}
}

Related

CUDA tiled 2D Convolution in shared memory is slower than global memory

I performed two convolution using constant memory for mask.
One without tiling in global memory:
__global__ void constGradientConvolution(uint8_t* inputImgData, uint8_t* gradientImgData, int w, int h) {
// Calculate the global thread positions
int row = blockIdx.y * blockDim.y + threadIdx.y;
int col = blockIdx.x * blockDim.x + threadIdx.x;
// Starting index for calculation
int start_r = row - SOBEL_OP_RADIUS;
int start_c = col - SOBEL_OP_RADIUS;
// Temp value for calculation
int temp = 0;
// Iterate over all the rows
for (int i = 0; i < SOBEL_OP_DIM; i++) {
// Go over each column
for (int j = 0; j < SOBEL_OP_DIM; j++) {
// Range check for rows
if ((start_r + i) >= 0 && (start_r + i) < h) {
// Range check for columns
if ((start_c + j) >= 0 && (start_c + j) < w) {
// Accumulate result
temp += inputImgData[(start_r + i) * w + (start_c + j)] *
constMask[i * SOBEL_OP_DIM + j];
}
}
}
}
// Write back the result
gradientImgData[row * w + col] = (uint8_t)abs(temp);
}
and one with tiling, loading in shared memory, credits to https://www.cstechera.com/2015/07/two-dimensional-2d-image-convolution-in-CUDA.html:
__global__ void tiledGradientConvolution(uint8_t* inputImgData, uint8_t* gradientImgData, int width, int height) {
__shared__ uint8_t N_ds[SharedDim_y][SharedDim_x];
// First batch loading
int dest = threadIdx.y * TILE_WIDTH + threadIdx.x,
destY = dest / SharedDim_x, destX = dest % SharedDim_x,
srcY = blockIdx.y * TILE_HEIGHT + destY - SOBEL_OP_RADIUS,
srcX = blockIdx.x * TILE_WIDTH + destX - SOBEL_OP_RADIUS,
src = (srcY * width + srcX);
if (srcY >= 0 && srcY < height && srcX >= 0 && srcX < width)
N_ds[destY][destX] = inputImgData[src];
else
N_ds[destY][destX] = 0;
for (int iter = 1; iter <= (SharedDim_x * SharedDim_y) / (TILE_WIDTH * TILE_HEIGHT); iter++)
{
// other batch loading
dest = threadIdx.y * TILE_WIDTH + threadIdx.x + iter * (TILE_WIDTH * TILE_HEIGHT);
destY = dest / SharedDim_x, destX = dest % SharedDim_x;
srcY = blockIdx.y * TILE_HEIGHT + destY - SOBEL_OP_RADIUS;
srcX = blockIdx.x * TILE_WIDTH + destX - SOBEL_OP_RADIUS;
src = (srcY * width + srcX);
if (destY < SharedDim_y && destX < SharedDim_x)
{
if (srcY >= 0 && srcY < height && srcX >= 0 && srcX < width)
N_ds[destY][destX] = inputImgData[src];
else
N_ds[destY][destX] = 0;
}
}
__syncthreads();
int temp = 0;
int y, x;
for (y = 0; y < SOBEL_OP_DIM; y++)
for (x = 0; x < SOBEL_OP_DIM; x++)
temp += N_ds[threadIdx.y + y][threadIdx.x + x] * constMask[y * SOBEL_OP_DIM + x];
y = blockIdx.y * TILE_HEIGHT + threadIdx.y;
x = blockIdx.x * TILE_WIDTH + threadIdx.x;
if (y < height && x < width) {
gradientImgData[y * width + x] = (uint8_t)abs(temp);
}
__syncthreads();
}
according to nvprof shared memory implementations is slower:
Type Time(%) Time Calls Avg Min Max Name
GPU activities: 53.27% 387.52us 2 193.76us 190.70us 196.82us [CUDA memcpy DtoH]
24.28% 176.62us 2 88.311us 608ns 176.01us [CUDA memcpy HtoD]
11.56% 84.102us 1 84.102us 84.102us 84.102us tiledGradientConvolution(unsigned char*, unsigned char*, int, int)
10.90% 79.270us 1 79.270us 79.270us 79.270us constGradientConvolution(unsigned char*, unsigned char*, int, int)
this is the configuration kernel:
#define SOBEL_OP_DIM 3
#define SOBEL_OP_RADIUS (SOBEL_OP_DIM / 2)
// tile dimension
#define TILE_WIDTH 16
#define TILE_HEIGHT 16
// Allocate masks in constant memory
__constant__ int constMask[SOBEL_OP_DIM * SOBEL_OP_DIM];
// Shared Memory Elements needed to be loaded as per Mask Size
#define SharedDim_x (TILE_WIDTH + SOBEL_OP_DIM - 1)
#define SharedDim_y (TILE_HEIGHT + SOBEL_OP_DIM - 1)
// in main code//
dim3 dimBlock(TILE_WIDTH, TILE_HEIGHT);
dim3 dimGrid((test.w + TILE_WIDTH - 1) / TILE_WIDTH, (test.h + TILE_HEIGHT - 1) / TILE_HEIGHT);
I expect shared memory to be faster, but i can figure out what cause conflincts in loading from global memory.
Any help would be appreciated. Thanks you in advance.

What is slices in OpenGL?

In the code bellow , Why we need slices ? and what does it for ?
//https://github.com/danginsburg/opengles-book-samples/blob/604a02cc84f9cc4369f7efe93d2a1d7f2cab2ba7/iPhone/Common/esUtil.h#L110
int esGenSphere(int numSlices, float radius, float **vertices,
float **texCoords, uint16_t **indices, int *numVertices_out) {
int numParallels = numSlices / 2;
int numVertices = (numParallels + 1) * (numSlices + 1);
int numIndices = numParallels * numSlices * 6;
float angleStep = (2.0f * ES_PI) / ((float) numSlices);
if (vertices != NULL) {
*vertices = malloc(sizeof(float) * 3 * numVertices);
}
if (texCoords != NULL) {
*texCoords = malloc(sizeof(float) * 2 * numVertices);
}
if (indices != NULL) {
*indices = malloc(sizeof(uint16_t) * numIndices);
}
for (int i = 0; i < numParallels + 1; i++) {
for (int j = 0; j < numSlices + 1; j++) {
int vertex = (i * (numSlices + 1) + j) * 3;
if (vertices) {
(*vertices)[vertex + 0] = radius * sinf(angleStep * (float)i) * sinf(angleStep * (float)j);
(*vertices)[vertex + 1] = radius * cosf(angleStep * (float)i);
(*vertices)[vertex + 2] = radius * sinf(angleStep * (float)i) * cosf(angleStep * (float)j);
}
if (texCoords) {
int texIndex = (i * (numSlices + 1) + j) * 2;
(*texCoords)[texIndex + 0] = (float)j / (float)numSlices;
(*texCoords)[texIndex + 1] = 1.0f - ((float)i / (float)numParallels);
}
}
}
// Generate the indices
if (indices != NULL) {
uint16_t *indexBuf = (*indices);
for (int i = 0; i < numParallels ; i++) {
for (int j = 0; j < numSlices; j++) {
*indexBuf++ = i * (numSlices + 1) + j;
*indexBuf++ = (i + 1) * (numSlices + 1) + j;
*indexBuf++ = (i + 1) * (numSlices + 1) + (j + 1);
*indexBuf++ = i * (numSlices + 1) + j;
*indexBuf++ = (i + 1) * (numSlices + 1) + (j + 1);
*indexBuf++ = i * (numSlices + 1) + (j + 1);
}
}
}
if (numVertices_out) {
*numVertices_out = numVertices;
}
return numIndices;
}
That code generates a sphere mesh that looks like this:
Source: https://commons.wikimedia.org/wiki/File:Sphere_wireframe_10deg_6r.svg CC BY 3.0
As you can see in the picture, there are horizontal parallel lines, and vertical lines which all meet at the poles. The horizontal lines are typically called parallels whereas the vertical ones are called meridians. The author of that code apparently didn't know this term, so they called it "slices" instead.

Converting from RGB to Lαβ Color spaces and converting it back to RGB using OpenCV

I am currently trying to convert colors between RGB (red, green, blue) color space and Lαβ color space, Based on the details in the this paper.
My difficulties are in reversing the conversion process. When the result is not as same as initial RGB Mat. I think I missing something in type castings between Mats but I can't tell what is it!
here is my code:
<!-- language: lang-cc -->
Mat DetectTrackFace::RGB2LAlphBeta(Mat &src)
{
Mat dest;
Mat L_AlphBeta(src.rows, src.cols, CV_32FC3);
//cvtColor(src,dest,CV_BGR2XYZ);
float X,Y,Z,L,M,S,_L,Alph,Beta;
int R,G,B;
for(int i = 0; i < src.rows; i++)
{
for(int j = 0; j < src.cols; j++)
{
B = src.at<Vec3b>(i, j)[0];
G = src.at<Vec3b>(i, j)[1];
R = src.at<Vec3b>(i, j)[2];
X = ( 0.4124 * R ) + ( 0.3576 * G ) + ( 0.1805 * B);
Y = ( 0.2126 * R ) + ( 0.7152 * G ) + ( 0.0722 * B);
Z = ( 0.0193 * R ) + ( 0.1192 * G ) + ( 0.9505 * B);
L = (0.3897 * X) + (0.6890 * Y) + (-0.0787 * Z);
M = (-0.2298 * X) + (1.1834* Y) + (0.0464 * Z);
S = (0.0000 * X) + (0.0000 * Y) + (1.0000 * Z);
//for handling log
if(L == 0.0000) L=1.0000;
if(M == 0.0000) M = 1.0000;
if( S == 0.0000) S = 1.0000;
//LMS to Lab
_L = (1.0 / sqrt(3.0)) *((1.0000 * log10(L)) + (1.0000 * log10(M)) + (1.0000 * log10(S)));
Alph =(1.0 / sqrt(6.0)) * ((1.0000 * log10(L)) + (1.0000 * log10(M)) + (-2.0000 * log10(S)));
Beta = (1.0 / sqrt(2.0)) * ((1.0000 * log10(L)) + (-1.0000 * log10(M)) + (-0.0000 * log10(S)));
L_AlphBeta.at<Vec3f>(i, j)[0] = _L;
L_AlphBeta.at<Vec3f>(i, j)[1] = Alph;
L_AlphBeta.at<Vec3f>(i, j)[2] = Beta;
}
}
return L_AlphBeta;
}
Mat DetectTrackFace::LAlphBeta2RGB(Mat &src)
{
Mat XYZ(src.rows, src.cols, src.type());
Mat BGR(src.rows, src.cols, CV_8UC3);
float X,Y,Z,L,M,S,_L,Alph,Beta, B,G,R;
for(int i = 0; i < src.rows; i++)
{
for(int j = 0; j < src.cols; j++)
{
_L = src.at<Vec3f>(i, j)[0]*1.7321;
Alph = src.at<Vec3f>(i, j)[1]*2.4495;
Beta = src.at<Vec3f>(i, j)[2]*1.4142;
/*Inv_Transform_logLMS2lab =
0.33333 0.16667 0.50000
0.33333 0.16667 -0.50000
0.33333 -0.33333 0.00000*/
L = (0.33333*_L) + (0.16667 * Alph) + (0.50000 * Beta);
M = (0.33333 * _L) + (0.16667 * Alph) + (-0.50000 * Beta);
S = (0.33333 * _L) + (-0.33333 * Alph) + (0.00000* Beta);
L = pow(10 , L);
if(L == 1) L=0;
M = pow(10 , M);
if(M == 1) M=0;
S = pow(10 , S);
if(S == 1) S=0;
/*Inv_Transform_XYZ2LMS
1.91024 -1.11218 0.20194
0.37094 0.62905 0.00001
0.00000 0.00000 1.00000*/
X = (1.91024 *L ) + (-1.11218 * M ) +(0.20194 * S);
Y = (0.37094 * L ) + (0.62905 * M ) +(0.00001 * S);
Z = (0.00000 * L) + (0.00000 * M ) +(1.00000 * S);
/*Inv_Transform_RGB2XYZ
3.240625 -1.537208 -0.498629
-0.968931 1.875756 0.041518
0.055710 -0.204021 1.056996*/
R = ( 3.240625 * X) + ( -1.537208 * Y) + ( -0.498629 * Z);
G = ( -0.968931 * X) + ( 1.875756 * Y) + ( 0.041518 * Z);
B = ( 0.055710 * X) + ( -0.204021 * Y) + ( 1.056996 * Z);
if(R>255) R = 255;
if(G>255) G = 255;
if(B>255) B = 255;
if(R<0) R = 0;
if(G<0) G = 0;
if(B<0) B = 0;
if(R > 255 || G > 255 || B > 255 || R < 0 || G < 0 || B<0)
cout<<"R = "<<R<<" G = "<<G <<" B = "<<B<<endl;
BGR.at<Vec3b>(i, j)[0] = (uchar)B;
BGR.at<Vec3b>(i, j)[1] = (uchar)G;
BGR.at<Vec3b>(i, j)[2] = (uchar)R;
}
}
//normalize(BGR,BGR, 255, 0, NORM_MINMAX, CV_8UC3 );
return BGR;
}
You have float to uchar truncation errors in the function LAlphBeta2RGB here:
BGR.at<Vec3b>(i, j)[0] = (uchar)B;
BGR.at<Vec3b>(i, j)[1] = (uchar)G;
BGR.at<Vec3b>(i, j)[2] = (uchar)R;
You can solve this using:
BGR(i, j)[0] = uchar(cvRound(B));
BGR(i, j)[1] = uchar(cvRound(G));
BGR(i, j)[2] = uchar(cvRound(R));
However, you shouldn't take care of conversion problems explicitly. You can use saturate_cast to handle this for you. You can declare R,G,B variables as uchar:
uchar B, G, R;
and perform the conversion as:
R = saturate_cast<uchar>((3.240625 * X) + (-1.537208 * Y) + (-0.498629 * Z));
G = saturate_cast<uchar>((-0.968931 * X) + (1.875756 * Y) + (0.041518 * Z));
B = saturate_cast<uchar>((0.055710 * X) + (-0.204021 * Y) + (1.056996 * Z));
and then assign as:
BGR(i, j)[0] = B;
BGR(i, j)[1] = G;
BGR(i, j)[2] = R;
Or avoid using R,G,B entirely using:
BGR(i, j)[2] = saturate_cast<uchar>((3.240625 * X) + (-1.537208 * Y) + (-0.498629 * Z));
BGR(i, j)[1] = saturate_cast<uchar>((-0.968931 * X) + (1.875756 * Y) + (0.041518 * Z));
BGR(i, j)[0] = saturate_cast<uchar>((0.055710 * X) + (-0.204021 * Y) + (1.056996 * Z));
Here the full code. I took the liberty to use Mat_ instead of Mat as functions arguments, to avoid using at<type>() to access pixel values. In fact, you are already assuming that inputs of your functions are CV_8UC3 and CV_32FC3, respectively.
#include <opencv2\opencv.hpp>
#include <iostream>
using namespace std;
using namespace cv;
Mat RGB2LAlphBeta(Mat3b &src)
{
Mat3f L_AlphBeta(src.rows, src.cols);
//cvtColor(src,dest,CV_BGR2XYZ);
float X, Y, Z, L, M, S, _L, Alph, Beta;
int R, G, B;
for (int i = 0; i < src.rows; i++)
{
for (int j = 0; j < src.cols; j++)
{
B = src(i, j)[0];
G = src(i, j)[1];
R = src(i, j)[2];
X = (0.4124 * R) + (0.3576 * G) + (0.1805 * B);
Y = (0.2126 * R) + (0.7152 * G) + (0.0722 * B);
Z = (0.0193 * R) + (0.1192 * G) + (0.9505 * B);
L = (0.3897 * X) + (0.6890 * Y) + (-0.0787 * Z);
M = (-0.2298 * X) + (1.1834* Y) + (0.0464 * Z);
S = (0.0000 * X) + (0.0000 * Y) + (1.0000 * Z);
//for handling log
if (L == 0.0000) L = 1.0000;
if (M == 0.0000) M = 1.0000;
if (S == 0.0000) S = 1.0000;
//LMS to Lab
_L = (1.0 / sqrt(3.0)) *((1.0000 * log10(L)) + (1.0000 * log10(M)) + (1.0000 * log10(S)));
Alph = (1.0 / sqrt(6.0)) * ((1.0000 * log10(L)) + (1.0000 * log10(M)) + (-2.0000 * log10(S)));
Beta = (1.0 / sqrt(2.0)) * ((1.0000 * log10(L)) + (-1.0000 * log10(M)) + (-0.0000 * log10(S)));
L_AlphBeta(i, j)[0] = _L;
L_AlphBeta(i, j)[1] = Alph;
L_AlphBeta(i, j)[2] = Beta;
}
}
return L_AlphBeta;
}
Mat LAlphBeta2RGB(Mat3f &src)
{
Mat3f XYZ(src.rows, src.cols);
Mat3b BGR(src.rows, src.cols);
float X, Y, Z, L, M, S, _L, Alph, Beta;
for (int i = 0; i < src.rows; i++)
{
for (int j = 0; j < src.cols; j++)
{
_L = src(i, j)[0] * 1.7321;
Alph = src(i, j)[1] * 2.4495;
Beta = src(i, j)[2] * 1.4142;
/*Inv_Transform_logLMS2lab =
0.33333 0.16667 0.50000
0.33333 0.16667 -0.50000
0.33333 -0.33333 0.00000*/
L = (0.33333*_L) + (0.16667 * Alph) + (0.50000 * Beta);
M = (0.33333 * _L) + (0.16667 * Alph) + (-0.50000 * Beta);
S = (0.33333 * _L) + (-0.33333 * Alph) + (0.00000* Beta);
L = pow(10, L);
if (L == 1) L = 0;
M = pow(10, M);
if (M == 1) M = 0;
S = pow(10, S);
if (S == 1) S = 0;
/*Inv_Transform_XYZ2LMS
1.91024 -1.11218 0.20194
0.37094 0.62905 0.00001
0.00000 0.00000 1.00000*/
X = (1.91024 *L) + (-1.11218 * M) + (0.20194 * S);
Y = (0.37094 * L) + (0.62905 * M) + (0.00001 * S);
Z = (0.00000 * L) + (0.00000 * M) + (1.00000 * S);
/*Inv_Transform_RGB2XYZ
3.240625 -1.537208 -0.498629
-0.968931 1.875756 0.041518
0.055710 -0.204021 1.056996*/
BGR(i, j)[2] = saturate_cast<uchar>((3.240625 * X) + (-1.537208 * Y) + (-0.498629 * Z));
BGR(i, j)[1] = saturate_cast<uchar>((-0.968931 * X) + (1.875756 * Y) + (0.041518 * Z));
BGR(i, j)[0] = saturate_cast<uchar>((0.055710 * X) + (-0.204021 * Y) + (1.056996 * Z));
}
}
//normalize(BGR,BGR, 255, 0, NORM_MINMAX, CV_8UC3 );
return BGR;
}
int main()
{
Mat3b img = imread("path_to_image");
Mat3f labb = RGB2LAlphBeta(img);
Mat3b rgb = LAlphBeta2RGB(labb);
Mat3b diff;
absdiff(img, rgb, diff);
// Check if all pixels are equals
cout << ((sum(diff) == Scalar(0, 0, 0, 0)) ? "Equals" : "Different");
return 0;
}

How to apply a kernel to a raster image

Im trying to apply a Sharpen Kernel to a raster picture, Here is my kernel:
{ 0.0f,-1.0f,0.0f,
-1.0f,5.0f,-1.0f,
0.0f,-1.0f,0.0f }
And here is my Code:
struct Pixel{
GLubyte R, G, B;
float x, y;
};
. . .
for (unsigned i = 1; i < iWidth - 1; i++){
for (unsigned j = 1; j < iHeight - 1; j++){
float r = 0, g = 0, b = 0;
r += -(float)pixels[i + 1][j].R;
g += -(float)pixels[i + 1][j].G;
b += -(float)pixels[i + 1][j].B;
r += -(float)pixels[i - 1][j].R;
g += -(float)pixels[i - 1][j].G;
b += -(float)pixels[i - 1][j].B;
r += -(float)pixels[i][j + 1].R;
g += -(float)pixels[i][j + 1].G;
b += -(float)pixels[i][j + 1].B;
r += -(float)pixels[i][j - 1].R;
g += -(float)pixels[i][j - 1].G;
b += -(float)pixels[i][j - 1].B;
pixels[i][j].R = (GLubyte)((pixels[i][j].R * 5) + r);
pixels[i][j].G = (GLubyte)((pixels[i][j].G * 5) + g);
pixels[i][j].B = (GLubyte)((pixels[i][j].B * 5) + b);
}
}
But the colors get mixed up when I apply this kernel, Here is an example:
What am I doing wrong?
NOTE : I know that OpenGL can do this fast and easy, but I just wanted to experiment on this kind of masks.
EDIT : The first code had a bug:
pixels[i][j].R = (GLubyte)((pixels[i][j].R * 5) + r);
pixels[i][j].G = (GLubyte)((pixels[i][j].R/*G*/ * 5) + g);
pixels[i][j].B = (GLubyte)((pixels[i][j].R/*B*/ * 5) + b);
I fixed it but I still got that problem.
Iv changed the last three lines to this:
r = (float)((pixels[i][j].R * 5) + r);
g = (float)((pixels[i][j].G * 5) + g);
b = (float)((pixels[i][j].B * 5) + b);
if (r < 0) r = 0;
if (g < 0) g = 0;
if (b < 0) b = 0;
if (r > 255) r = 255;
if (g > 255) g = 255;
if (b > 255) b = 255;
pixels[i][j].R = r;
pixels[i][j].G = g;
pixels[i][j].B = b;
And now the output looks like this:
You have a copy-paste bug here:
pixels[i][j].R = (GLubyte)((pixels[i][j].R * 5) + r);
pixels[i][j].G = (GLubyte)((pixels[i][j].R * 5) + g);
pixels[i][j].B = (GLubyte)((pixels[i][j].R * 5) + b);
^
This should be:
pixels[i][j].R = (GLubyte)((pixels[i][j].R * 5) + r);
pixels[i][j].G = (GLubyte)((pixels[i][j].G * 5) + g);
pixels[i][j].B = (GLubyte)((pixels[i][j].B * 5) + b);
Also it looks like you may have iWidth/iHeight transposed, but it's hard to say without seeing the rest of the code. Typically though the outer loop iterates over rows, so the upper bound would be the number of rows, i.e. the image height.
Most importantly though you have a fundamental problem in that you're trying to perform a neighbourhood operation in-place. Each output pixel depends on its neighbours, but you're modifying these neighbours as you iterate through the image. You need to do this kind of operation out-of-place, i.e. have a separate output image:
out_pixels[i][j].R = r;
out_pixels[i][j].G = g;
out_pixels[i][j].B = b;
so that the input image does not get modified. (Note also that you'll want to copy the edge pixels over from the input image to the output image.)

Get puzzled on histogram equalization in Qt

I'm new to image processing.When I tried the histogram equalization algorithm,I got an error which I can't explain,just as the picture shows below.
I'm sorry that I can't upload a picture right now.I'll use picasa instead.
https://picasaweb.google.com/lh/photo/xoxhWR7waVp50uLh-Ko78C_rLSYVGhhn6l5Yer6Tngc?feat=directlink
The original picture is on the left.
My algorithm is turn RGB into YCbCr and equalize Y,leave Cb and Cr be.Then convert YCbCr into RGB again to show the picture after equalization.
Here's the conversion code.
void MainWindow::rGBToYCbCr(uchar *bmpArray,uchar *lumaArray,uchar *cBCrArray,int startPoint)
{
for(int i = 0,m = 0,n = 0; i < bmpWidth * bmpHeight; i++,m+=3,n+=2)
{
lumaArray[i] = (uchar)(0.299 * bmpArray[startPoint + m + 2] + 0.587 *bmpArray[startPoint + m + 1] + 0.115 * bmpArray[startPoint + m]);
cBCrArray[n] = (uchar)(-0.169 * bmpArray[startPoint + m + 2] - 0.331 * bmpArray[startPoint + m + 1] + 0.5 * bmpArray[startPoint + m]) + 128;//cb
cBCrArray[n+1] = (uchar)(0.5 * bmpArray[startPoint + m + 2] - 0.419 * bmpArray[startPoint + m + 1] - 0.081 * bmpArray[startPoint + m]) + 128;//cr
}
}
void MainWindow::yCbCrToRGB(uchar *lumaArray,uchar *targetArray,uchar *CbCrArray,int startPoint)
{
for(int i = 0,m = 0,n=0; i < 3 * bmpWidth * bmpHeight; i+=3,m++,n+=2)
{
targetArray[startPoint + i + 2] = lumaArray[m] + (uchar)(1.402 * (CbCrArray[n + 1] - 128));
targetArray[startPoint + i + 1] = lumaArray[m] - (uchar)(0.344 * (CbCrArray[n] - 128)) - (uchar)(0.714 * (CbCrArray[n + 1] - 128));
targetArray[startPoint + i] = lumaArray[m] + (uchar)(1.772 * (CbCrArray[n] - 128));
}
}
And here is the equlization algorithm.
void MainWindow::histogramEqulizeGrayScale(uchar *bmpArray,int startPoint)
{
int hisTimes[256]={0};
for(int i = 0; i < bmpWidth * bmpHeight; i++)
hisTimes[(int)bmpArray[startPoint + i]]++;
double pixmapProbability[256];
for(int i = 0; i < 256; i++)
{
int sum = 0;
for(int j = 0; j <= i; j++)
sum += hisTimes[j];
pixmapProbability[i] = (double)sum / (double)(bmpWidth * bmpHeight);
}
for(int i = 0; i < 256; i++)
hisTimes[i] = pixmapProbability[i] * 256;
for(int i = 0; i < bmpWidth * bmpHeight; i++)
bmpArray[startPoint + i] = hisTimes[bmpArray[startPoint + i]];
}
Why would the color become so terrible?
could it be that your color values are overflowing?
You have to clamp the results of your calculations for each color to [0,255]

Resources