【问题标题】:Implement RGBtoHSV C++ , wrong H output实现 RGBtoHSV C++,错误的 H 输出
【发布时间】:2016-06-23 18:09:38
【问题描述】:

我正在尝试在 HSV 维度中执行 Sobel 运算符(我的指南告诉我在 HSV 中执行此操作,但我不明白为什么它在 HSV 上比在 RGB 上效果更好)。 我已经构建了一个从 RGB 转换为 HSV 的函数。虽然我在 C++ 方面有一些平庸的知识,但我对图像处理感到困惑,因此我试图使代码尽可能简单,这意味着我(在这个阶段)不关心时间和空间。 从我在灰度 bmp 照片中得到的结果来看,我的 V 和 S 似乎很好,但我的 H 看起来很乱。 我在这里有 2 个问题: 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: 听取@gpasch 的建议并使用better reference 并删除mod6 我现在可以恢复RGB 原始照片了!但不幸的是,现在我的灰度照片比以前更加混乱。 我将编辑有关的代码,以便获得有关如何保存 H 灰度照片的更多信息。

【问题讨论】:

  • 对于灰度图像,S(饱和度)应该为零,H(色调)未定义,或者由于其值(色轮上的角度)没有意义而采用一些规范角度灰色阴影。
  • @ChristopherOicles 可以将 S 设置为 0 但 H 呢?我将添加另一段可能也有问题的代码
  • C == 0 表示像素为黑色、白色或灰色。 H在这种情况下没有意义,所以任意设置为0,只是为了给这些情况下H的一致值。
  • 你从哪里得到转换的方程式?

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


【解决方案1】:

这就是浏览垃圾网站的危险;我建议如下:

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

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

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

【讨论】:

  • 您对 mod 6 的看法是正确的。我删除了它,现在我转换回 rgb 的图像看起来像原来的!多谢 !。但是现在灰度的H照片看起来像垃圾Original photoHphoto
  • 我很高兴有耐心的人终于能够解决这个问题。
  • mod 6 实际上很好 - 它源于色彩空间的六边形性质。 H 的问题在于归一化缩放。
【解决方案2】:

我正在尝试在 HSV 维度中执行 Sobel 算子(我的指南告诉我在 HSV 中执行此操作,但我不明白为什么它在 HSV 上比在 RGB 上效果更好)

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

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

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

  1. 我的代码哪里错了:

您的代码存在两个主要问题。首先是HSVfloattoGrayconvert 中的色调缩放是错误的。您的代码设置了factor=1.0/360.0f,然后乘以因子,这意味着它乘以360。如果您简单地乘以因子,它会产生预期的输出。这是因为前面的计算对 S 和 V 使用归一化值 (0..1),但对 H 使用角度以度为单位,因此您需要除以 360 才能对 H 进行归一化。

其次,转回RGB有问题,主要与计算Htag有关,这里要计算x的原始值,但floor只有在打开扇区时才会出现。

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

我采用了您的代码,添加了一些函数来生成输入数据和保存输出文件,使其完全独立,并修复了上述错误,同时对源代码进行了最小的更改。

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

提取的色调通道为:

饱和度通道为:

最后的价值:

修复 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 更易于编写且更便携。

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-01-13
    • 2010-11-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-03-26
    • 1970-01-01
    相关资源
    最近更新 更多