实现RGBtoHSV C ++,错误的H输出

时间:2016-06-14 00:18:39

标签: c++ image c++11 image-processing

我正在尝试在HSV维度上做Sobel算子(我的导游告诉我在HSV中这样做但我不明白为什么它在HSV上比在RGB上更好)。 我已经构建了一个从RGB转换为HSV的函数。虽然我在C ++中有一些平庸的知识,但我对图像处理感到困惑,因此我尽量保持代码尽可能简单,这意味着我不关心(在这个阶段)时间和空间。 从查看结果我得到灰度级bmp照片,我的V和S似乎很好但我的H看起来非常粗暴。 我在这里有两个问题: 1.与源照片相比,灰度级的普通H照片应该如何? 2.我在代码中哪里错了:

   void RGBtoHSV(unsigned char image[][NUMBER_OF_COLUMNS][NUMBER_OF_COLORS],
float Him[][NUMBER_OF_COLUMNS],
float Vim[][NUMBER_OF_COLUMNS], 
float Sim[][NUMBER_OF_COLUMNS])
{

double Rn, Gn, Bn;

double   C;
double H, S, V;

for (int row = 0; row < NUMBER_OF_ROWS; row++)
{
    for (int column = 0; column < NUMBER_OF_COLUMNS; column++)
    {
        Rn = (1.0*image[row][column][R]) / 255;
        Gn = (1.0*image[row][column][G] )/ 255;
        Bn = (1.0*image[row][column][B] )/ 255;

        //double RGBn[3] = { Rn, Gn, Bn };

        double max = Rn;
        if (max < Gn) max = Gn;
        if (max < Bn) max = Bn;
        double min = Rn;
        if (min > Gn) min = Gn;
        if (min > Bn) min = Bn;

        C = max - min;

        H = 0;
        if (max==0)
        {
            S = 0;
            H = -1; //undifined;
            V = max;
        }
        else
        {

        /*  if (max == Rn)
                H = (60.0* ((int)((Gn - Bn) / C) % 6));
            else if (max == Gn)
                H = 60.0*( (Bn - Rn)/C + 2);
            else
                H = 60.0*( (Rn - Gn)/C + 4);
            */

            if (max == Rn)
                H = (   60.0* ( (Gn - Bn) / C) )   ;
            else if (max == Gn)
                H = 60.0*((Bn - Rn) / C + 2);
            else
                H = 60.0*((Rn - Gn) / C + 4);

            V = max; //AKA lightness
            S = C / max; //saturation 
        }


        while (H < 0) 
            H += 360;
        while (H>360) 
            H -= 360;

        Him[row][column] = (float)H;

        Vim[row][column] = (float)V;
        Sim[row][column] = (float)S;
    }
}
}

也是我的hsvtorgb:

void HSVtoRGB(unsigned char image[][NUMBER_OF_COLUMNS][NUMBER_OF_COLORS],
float Him[][NUMBER_OF_COLUMNS],
float Vim[][NUMBER_OF_COLUMNS],
float Sim[][NUMBER_OF_COLUMNS])
{

double R1, G1, B1;

double   C;
double   V;
double S;
double H;
int Htag;
double Htag2;
double x;
double m;

for (int row = 0; row < NUMBER_OF_ROWS; row++)
{
    for (int column = 0; column < NUMBER_OF_COLUMNS; column++)
    {
        H = (double)Him[row][column];
        S = (double)Sim[row][column];
        V = (double)Vim[row][column];


        C = V*S;

        Htag = (int) (H / 60.0);
        Htag2 = H/ 60.0;


        //x = C*(1 - abs(Htag % 2 - 1));
        double tmp1 = fmod(Htag2, 2);
        double temp=(1 - abs(tmp1 - 1));
        x = C*temp;
        //switch (Htag)
        switch (Htag)
        {
        case 0 : 
            R1 = C;
            G1 = x;
            B1 = 0;
            break;
        case 1:
            R1 = x;
            G1 = C;
            B1 = 0;
            break;
        case 2:
            R1 = 0;
            G1 = C;
            B1 = x;
            break;
        case 3:
            R1 = 0;
            G1 = x;
            B1 = C;
            break;
        case 4:
            R1 = x;
            G1 = 0;
            B1 = C;
            break;
        case 5:
            R1 = C;
            G1 = 0;
            B1 = x;
            break;
        default:
            R1 = 0;
            G1 = 0;
            B1 = 0;
            break;

        }


        m = V - C;
   //this is also good change I found
        //image[row][column][R] = unsigned char( (R1 + m)*255);
        //image[row][column][G] = unsigned char( (G1 + m)*255);
        //image[row][column][B] = unsigned char( (B1 + m)*255);

        image[row][column][R] = round((R1 + m) * 255);
        image[row][column][G] = round((G1 + m) * 255);
        image[row][column][B] = round((B1 + m) * 255);



            }
      }
   }


   void HSVfloattoGrayconvert(unsigned char grayimage[NUMBER_OF_ROWS]        [NUMBER_OF_COLUMNS], float hsvimage[NUMBER_OF_ROWS][NUMBER_OF_COLUMNS], char hsv)
  {
//grayimage , flaotimage , h/s/v
   float factor;
if (hsv == 'h' || hsv == 'H') factor = (float) 1 / 360;
else factor = 1;
for (int row = 0; row < NUMBER_OF_ROWS; row++)
{
    for (int column = 0; column < NUMBER_OF_COLUMNS; column++)
    {
        grayimage[row][column] = (unsigned char) (0.5f + 255.0f * (float)hsvimage[row][column] / factor);
          }
      }
 }

和我的主要:

 unsigned char ColorImage1[NUMBER_OF_ROWS][NUMBER_OF_COLUMNS]   [NUMBER_OF_COLORS];
float Himage[NUMBER_OF_ROWS][NUMBER_OF_COLUMNS];
float Vimage[NUMBER_OF_ROWS][NUMBER_OF_COLUMNS];
float Simage[NUMBER_OF_ROWS][NUMBER_OF_COLUMNS];

 unsigned char ColorImage2[NUMBER_OF_ROWS][NUMBER_OF_COLUMNS]   [NUMBER_OF_COLORS];

unsigned char HimageGray[NUMBER_OF_ROWS][NUMBER_OF_COLUMNS];
unsigned char VimageGray[NUMBER_OF_ROWS][NUMBER_OF_COLUMNS];
unsigned char SimageGray[NUMBER_OF_ROWS][NUMBER_OF_COLUMNS];

unsigned char HAfterSobel[NUMBER_OF_ROWS][NUMBER_OF_COLUMNS];
unsigned char VAfterSobel[NUMBER_OF_ROWS][NUMBER_OF_COLUMNS];
unsigned char SAfterSobal[NUMBER_OF_ROWS][NUMBER_OF_COLUMNS];

   unsigned char HSVcolorAfterSobal[NUMBER_OF_ROWS][NUMBER_OF_COLUMNS][NUMBER_OF_COLORS];

unsigned char RGBAfterSobal[NUMBER_OF_ROWS][NUMBER_OF_COLUMNS][NUMBER_OF_COLORS];


int KernelX[3][3] = { 
{-1,0,+1}, {-2,0,2}, {-1,0,1 }
};

  int KernelY[3][3] = {
   {-1,-2,-1}, {0,0,0}, {1,2,1}
  };

  void main()
{

//work
LoadBgrImageFromTrueColorBmpFile(ColorImage1, "P22A.bmp");

// add noise
AddSaltAndPepperNoiseRGB(ColorImage1, 350, 255);
StoreBgrImageAsTrueColorBmpFile(ColorImage1, "saltandpepper.bmp");
AddGaussNoiseCPPstileRGB(ColorImage1, 0.0, 1.0);
StoreBgrImageAsTrueColorBmpFile(ColorImage1, "Saltandgauss.bmp");

//saves hsv in float array 
RGBtoHSV(ColorImage1, Himage, Vimage, Simage); 

//saves hsv float arrays in unsigned char arrays
HSVfloattoGrayconvert(HimageGray, Himage, 'h');
HSVfloattoGrayconvert(VimageGray, Vimage, 'v');
HSVfloattoGrayconvert(SimageGray, Simage, 's');


StoreGrayImageAsGrayBmpFile(HimageGray, "P22H.bmp");
StoreGrayImageAsGrayBmpFile(VimageGray, "P22V.bmp");
StoreGrayImageAsGrayBmpFile(SimageGray, "P22S.bmp");

    WaitForUserPressKey();

  }

编辑:更改代码+添加方程式的来源: Soruce:对于方程式:

  1. http://www.rapidtables.com/convert/color/hsv-to-rgb.htm
  2. http://www.rapidtables.com/convert/color/rgb-to-hsv.htm
  3. EDIT3: 听取@gpasch建议并使用better reference并删除mod6我现在能够恢复RGB原始照片!但不幸的是,现在我的灰度级H照片比之前更加混乱。 我将编辑代码,以便了解有关我如何保存H灰度照片的更多信息。

2 个答案:

答案 0 :(得分:2)

这是通过垃圾网站的危险;我建议如下:

https://www.cs.rit.edu/~ncs/color/t_convert.html

那个mod 6在那里看起来很可疑。

您还需要确保您了解H的度数为0到360;如果您的过滤器预期为0..1,则您有更改。

答案 1 :(得分:0)

  

我正在尝试在HSV维度上做Sobel算子(我的导游告诉我在HSV中这样做但我不明白为什么它在HSV上比在RGB上更好)

这取决于你想要达到的目标。例如,如果您尝试基于亮度进行边缘检测,那么只需处理V通道可能比处理所有三个RGB通道并在之后将它们组合起来更简单。

  
      
  1. 与源照片相比,灰度级的普通H照片应该如何?
  2.   

您会看到相似颜色的区域显示为类似的灰色阴影,对于真实世界的场景,您仍会看到渐变。但是,如果空间相邻的区域颜色相隔很远,则会出现急剧跳跃。通常可以识别形状。

  
      
  1. 我在代码中哪里错了:
  2.   

您的代码存在两个主要问题。首先是HSVfloattoGrayconvert中的色调缩放是错误的。您的代码正在设置factor=1.0/360.0f但是除以因子,这意味着它乘以360.如果您只是乘以因子,它会产生预期的输出。这是因为早期的计算使用S和V的标准化值(0..1),但H的角度为度,因此需要除以360以标准化H.

其次,转换回RGB有一个问题,主要是计算Htag,你想要计算x的原始值,但仅在开启扇区时floor

请注意,尽管@gpasch建议,mod 6操作实际上是正确的。这是因为您使用的转换基于HSV的六边形颜色空间模型,这用于确定您的颜色所在的扇区。对于连续模型,您可以使用径向转换,而不是略有不同。两者都在Wikipedia上得到了很好的解释。

我拿了你的代码,添加了一些函数来生成输入数据并保存输出文件,因此它完全是独立的,并修复了上面的错误,同时对源进行了很少的更改。

给出以下生成的输入图像:

Generated source image - colour wash

提取的Hue频道是:

Hue channel

饱和度通道为:

Saturation channel

最后价值:

enter image description here

在修复HSV到RGB转换后,我验证了生成的输出图像与原始图像匹配。

更新的代码如下(如上所述,最低限度更改以进行独立测试):

#include <string>
#include <cmath>
#include <cstdlib>

enum ColorIndex
{
    R = 0,
    G = 1,
    B = 2,
};

namespace
{
    const unsigned NUMBER_OF_COLUMNS   = 256;
    const unsigned NUMBER_OF_ROWS      = 256;
    const unsigned NUMBER_OF_COLORS    = 3;
};

void RGBtoHSV(unsigned char image[][NUMBER_OF_COLUMNS][NUMBER_OF_COLORS],
              float Him[][NUMBER_OF_COLUMNS],
              float Vim[][NUMBER_OF_COLUMNS],
              float Sim[][NUMBER_OF_COLUMNS])
{
    double Rn, Gn, Bn;
    double   C;
    double H, S, V;

    for (int row = 0; row < NUMBER_OF_ROWS; row++)
    {
        for (int column = 0; column < NUMBER_OF_COLUMNS; column++)
        {
            Rn = image[row][column][R] / 255.0;
            Gn = image[row][column][G] / 255.0;
            Bn = image[row][column][B] / 255.0;

            double max = Rn;
            if (max < Gn) max = Gn;
            if (max < Bn) max = Bn;
            double min = Rn;
            if (min > Gn) min = Gn;
            if (min > Bn) min = Bn;

            C = max - min;

            H = 0;
            if (max==0)
            {
                S = 0;
                H = 0; // Undefined
                V = max;
            }
            else
            {
                if (max == Rn)
                    H = 60.0*fmod((Gn - Bn) / C, 6.0);
                else if (max == Gn)
                    H = 60.0*((Bn - Rn) / C + 2);
                else
                    H = 60.0*((Rn - Gn) / C + 4);

                V = max; //AKA lightness
                S = C / max; //saturation
            }


            while (H < 0)
                H += 360.0;
            while (H > 360)
                H -= 360.0;

            Him[row][column] = (float)H;
            Vim[row][column] = (float)V;
            Sim[row][column] = (float)S;
        }
    }
}

void HSVtoRGB(unsigned char image[][NUMBER_OF_COLUMNS][NUMBER_OF_COLORS],
              float Him[][NUMBER_OF_COLUMNS],
              float Vim[][NUMBER_OF_COLUMNS],
              float Sim[][NUMBER_OF_COLUMNS])
{

    double R1, G1, B1;

    double   C;
    double   V;
    double S;
    double H;
    double Htag;
    double x;
    double m;

    for (int row = 0; row < NUMBER_OF_ROWS; row++)
    {
        for (int column = 0; column < NUMBER_OF_COLUMNS; column++)
        {
            H = (double)Him[row][column];
            S = (double)Sim[row][column];
            V = (double)Vim[row][column];

            C = V*S;

            Htag = H / 60.0;

            double x = C*(1.0 - fabs(fmod(Htag, 2.0) - 1.0));

            int i = floor(Htag);
            switch (i)
            {
                case 0 :
                    R1 = C;
                    G1 = x;
                    B1 = 0;
                    break;
                case 1:
                    R1 = x;
                    G1 = C;
                    B1 = 0;
                    break;
                case 2:
                    R1 = 0;
                    G1 = C;
                    B1 = x;
                    break;
                case 3:
                    R1 = 0;
                    G1 = x;
                    B1 = C;
                    break;
                case 4:
                    R1 = x;
                    G1 = 0;
                    B1 = C;
                    break;
                case 5:
                    R1 = C;
                    G1 = 0;
                    B1 = x;
                    break;
                default:
                    R1 = 0;
                    G1 = 0;
                    B1 = 0;
                    break;

            }

            m = V - C;

            image[row][column][R] = round((R1 + m) * 255);
            image[row][column][G] = round((G1 + m) * 255);
            image[row][column][B] = round((B1 + m) * 255);
        }
    }
}


void HSVfloattoGrayconvert(unsigned char grayimage[][NUMBER_OF_COLUMNS], float hsvimage[][NUMBER_OF_COLUMNS], char hsv)
{
//grayimage , flaotimage , h/s/v
    float factor;
    if (hsv == 'h' || hsv == 'H') factor = 1.0f/360.0f;
    else factor = 1.0f;
    for (int row = 0; row < NUMBER_OF_ROWS; row++)
    {
        for (int column = 0; column < NUMBER_OF_COLUMNS; column++)
        {
            grayimage[row][column] = (unsigned char) (0.5f + 255.0f * (float)hsvimage[row][column] * factor);
        }
    }
}


int KernelX[3][3] = {
    {-1,0,+1}, {-2,0,2}, {-1,0,1 }
};

int KernelY[3][3] = {
    {-1,-2,-1}, {0,0,0}, {1,2,1}
};

void GenerateTestImage(unsigned char image[][NUMBER_OF_COLUMNS][NUMBER_OF_COLORS])
{
    for (unsigned y = 0; y < NUMBER_OF_ROWS; y++)
    {
        for (unsigned x = 0; x < NUMBER_OF_COLUMNS; x++)
        {
            image[y][x][R] = x % 256;
            image[y][x][G] = y % 256;
            image[y][x][B] = (255-x) % 256;
        }
    }
}

void GenerateTestImage(unsigned char image[][NUMBER_OF_COLUMNS])
{
    for (unsigned y = 0; y < NUMBER_OF_ROWS; y++)
    {
        for (unsigned x = 0; x < NUMBER_OF_COLUMNS; x++)
        {
            image[x][y] = x % 256;
        }
    }
}

// Color (three channel) images
void SaveImage(unsigned char image[][NUMBER_OF_COLUMNS][NUMBER_OF_COLORS], const std::string& filename)
{
    FILE* fp = fopen(filename.c_str(), "w");
    fprintf(fp, "P6\n%u %u\n255\n", NUMBER_OF_COLUMNS, NUMBER_OF_ROWS);
    fwrite(image, NUMBER_OF_COLORS, NUMBER_OF_ROWS*NUMBER_OF_COLUMNS, fp);
    fclose(fp);
}

// Grayscale (single channel) images
void SaveImage(unsigned char image[][NUMBER_OF_COLUMNS], const std::string& filename)
{
    FILE* fp = fopen(filename.c_str(), "w");
    fprintf(fp, "P5\n%u %u\n255\n", NUMBER_OF_COLUMNS, NUMBER_OF_ROWS);
    fwrite(image, 1, NUMBER_OF_ROWS*NUMBER_OF_COLUMNS, fp);
    fclose(fp);
}

unsigned char ColorImage1[NUMBER_OF_ROWS][NUMBER_OF_COLUMNS][NUMBER_OF_COLORS];
unsigned char Himage[NUMBER_OF_ROWS][NUMBER_OF_COLUMNS];
unsigned char Simage[NUMBER_OF_ROWS][NUMBER_OF_COLUMNS];
unsigned char Vimage[NUMBER_OF_ROWS][NUMBER_OF_COLUMNS];
float HimageGray[NUMBER_OF_ROWS][NUMBER_OF_COLUMNS];
float SimageGray[NUMBER_OF_ROWS][NUMBER_OF_COLUMNS];
float VimageGray[NUMBER_OF_ROWS][NUMBER_OF_COLUMNS];

int main()
{
    // Test input
    GenerateTestImage(ColorImage1);
    SaveImage(ColorImage1, "test_input.ppm");

    //saves hsv in float array
    RGBtoHSV(ColorImage1, HimageGray, VimageGray, SimageGray);

    //saves hsv float arrays in unsigned char arrays
    HSVfloattoGrayconvert(Himage, HimageGray, 'h');
    HSVfloattoGrayconvert(Vimage, VimageGray, 'v');
    HSVfloattoGrayconvert(Simage, SimageGray, 's');

    SaveImage(Himage, "P22H.pgm");
    SaveImage(Vimage, "P22V.pgm");
    SaveImage(Simage, "P22S.pgm");

    // Convert back to get the original test image
    HSVtoRGB(ColorImage1, HimageGray, VimageGray, SimageGray);
    SaveImage(ColorImage1, "test_output.ppm");

    return 0;
}

输入图像是通过一个非常简单的算法生成的,它给出了每个维度的渐变,因此我们可以轻松地检查和验证预期的输出。我使用ppm/pgm文件,因为它们比BMP更容易编写,更便携。

希望这有帮助 - 如果您有任何问题,请告诉我。