【问题标题】:Advanced rgb2hsv conversion Matlab to opnecv/C++ access to pixel value高级 rgb2hsv 转换 Matlab 到 opencv/C++ 获取像素值
【发布时间】:2016-09-25 08:29:57
【问题描述】:

我正在使用目标 C/C++ 和 openCV 构建程序。我在 Objective C 方面非常熟练,但对 C++ 很陌生。
我正在构建自定义 RGB2HSV 算法。我的算法与 openCV 库 cvtColor(in, out, CV_RGB2HSV) 略有不同。
我尝试将 Matlab 格式转换为 opencV/C++ 生成的 HSV 图像非常清晰,在进一步处理之前不需要额外的过滤。下面的代码——Matlab 代码是不言自明的。

我尝试将其转换为 C++/openCV 函数,但我试图访问图像的像素值时碰壁了。我是 C++ 新手。 我阅读了很多关于如何访问 Mat 结构的方法,但通常我会在零的位置获得一堆字母或一个通常类似于“\202 k g”的数字。当我尝试对说 \202 进行任何乘法运算时,结果与数学无关。

请帮助我正确访问像素值。同样在当前版本中,使用 uchar 将不起作用,因为某些值超出了 0-255 范围。 算法不是我的。我什至无法指出来源,但它提供的结果显然比普通的 RGB2HSV 更好。

下面的算法也是针对一个像素的。它需要应用于图像中的每个像素,因此在最终版本中需要使用 for { for {}} 循环进行包装。

我也希望与社区分享这种方法,以便每个人都可以从中受益并节省预过滤。

请帮我把它翻译成 C++/openCV。如果可能的话,最好采用速度明智的做法。或者至少如何清楚地访问像素值,以便它适用于一系列数学方程。提前致谢。

function[H, S, V] = rgb2hsvPixel(R,G,B)

% Algorithm:

% In case of 8-bit and 16-bit images, `R`, `G`, and `B` are converted to the
% floating-point format and scaled to fit the 0 to 1 range.
%
%    V = max(R,G,B)
%    S = / (V - min(R,G,B)) / V               if V != 0
%        \ 0                                  otherwise
%        / 60*(G-B) / (V - min(R,G,B))        if V=R
%    H = | 120 + 60*(B-R) / (V - min(R,G,B))  if V=G
%        \ 240 + 60*(R-G) / (V - min(R,G,B))  if V=B
%
% If `H<0` then `H=H+360`. On output `0<=V<=1`, `0<=S<=1`, `0<=H<=360`.



        red = (double(R)-16)*255/224;                 % \  
        green = (double(G)-16)*255/224;               %  }- R,G,B  (0 <-> 255) ->  (-18.2143 <-> 272.0759)
        blue = (min(double(B)*2,240)-16)*255/224;     % /
        minV = min(red,min(green,blue));
        value = max(red,max(green,blue));

        delta = value - minV;
        if(value~=0)
            sat = (delta*255) / value;% s
            if (delta ~= 0) 
                if( red == value )
                    hue = 60*( green - blue ) / delta;      % between yellow & magenta
                elseif( green == value )
                    hue = 120 + 60*( blue - red ) / delta;  % between cyan & yellow
                else
                    hue = 240 + 60*( red - green ) / delta; % between magenta & cyan
                end
                if( hue < 0 )
                    hue = hue + 360;
                end
            else 
                hue = 0;
                sat = 0;
            end
        else 
            % r = g = b = 0
            sat = 0;
            hue = 0;
        end
        H = max(min(floor(((hue*255)/360)),255),0);
        S = max(min(floor(sat),255),0);
        V = max(min(floor(value),255),0);
    end

【问题讨论】:

  • 你到底在问什么?如何在 C++ 中访问 Mat 对象中一个像素的值?或者您想要一个完整的工作代码来将 RGB 转换为 HSV?
  • 至少如何获得一个像素的可行值。如果有人可以帮助建立对社区有益的整个功能。我可以用简单的数学逻辑构建它,但我知道它不是 C++ 中最有效的方法

标签: c++ image algorithm matlab opencv


【解决方案1】:

要访问 3 通道、8 位精度图像(类型 CV_8UC3)中的像素值,您必须这样做:

cv::Mat image;
cv::Vec3b BGR = image.at<cv::Vec3b>(i,j);

如果如您所说,8 位精度和范围还不够,您可以声明 CV_32F 类型的 cv::Mat 来存储浮点 32 位数字。

cv::Mat image(height, width, CV_32FC3);
//fill your image with data
for(int i = 0; i < image.rows; i++) {
    for(int j = 0; j < image.cols; j++) {
        cv::Vec3f BGR = image.at<cv::Vec3f>(i,j)
        //process your pixel
        cv::Vec3f HSV; //your calculated HSV values
        image.at<cv::Vec3f>(i,j) = HSV;
    }
}

请注意,OpenCV 以 BGR 顺序而不是 RGB 存储 rgb 值。查看OpenCV docs 了解更多信息。

【讨论】:

  • 我认为应该是:cv::Mat image(height, width, CV_32FC3); 而不是cv::Mat image(height, width, CV_32F);?
  • @Catree 你是对的。我的坏,我没有注意。我会改正的。
  • 我正在测试访问像素值的方法: cout (x,y); cout (x,y)[0] (x,y) (x,y) 我错过了什么吗?
  • 不,这是正常结果。通过指定 Vec3b,您告诉 OpenCV 该矩阵应该包含 8 位数据。使用 Vec3f,它将是 32 位浮点数据。而 Vec3s 将是 16 位短数据。这会影响 OpenCV 访问底层原始数据的方式。如果您想知道为什么会收到字母 z,那是因为 image.at&lt;Vec3b&gt;(x,y)[0]ucharcout 将其视为字母。
  • 事实上,使用 Vec3b 或 Vec3f 并不能获得相同的像素。让我解释一下:你有一个 2x2 矩阵 [[a,b],[c,d]]。假设每个字母代表一个 Vec3b。 image.at<:vec3b>(0,0) 将为您提供 a 的值。而 image.at<:vec3s>(0,0) 将为您提供一个 Vec3s,其坐标是由 a 和 b 中的位串联而成的 16 位短裤。并且 image.at<:vec3s>(1,1) 将导致错误,因为您超出了矩阵中原始数据的范围。
【解决方案2】:

如果您关心性能并且对像素索引相当满意,您可以直接使用Mat ptr

例如:

  cv::Mat img = cv::Mat::zeros(4, 8, CV_8UC3);

  uchar *ptr_row_img;
  int cpt = 0;
  for(int i = 0; i < img.rows; i++) {
    ptr_row_img = img.ptr<uchar>(i);

    for(int j = 0; j < img.cols; j++) {
      for(int c = 0; c < img.channels(); c++, cpt++, ++ptr_row_img) {
        *ptr_row_img = cpt;
      }
    }
  }

  std::cout << "img=\n" << img << std::endl;

前面的代码应该打印出来:

img= [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13、14、15、16、17、18、19、20、21、22、23; 24、25、26、 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41、42、43、44、45、46、47; 48、49、50、51、52、53、54、 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69、70、71; 72、73、74、75、76、77、78、79、80、81、82、 83、84、85、86、87、88、89、90、91、92、93、94、95]

at 访问对于大多数情况来说应该足够了,并且比使用 ptr 访问更具可读性/出错的可能性更低。

参考资料:

【讨论】:

  • 为了高效访问,我认为最好的还是使用指向数据的原始指针Vec3b* imageData = (Vec3b*) image.data;
【解决方案3】:

感谢大家的帮助。 感谢您的提示,我构建了自定义 rgb2hsv 函数 C++/openCV。

分别从左上角开始,bgr->gray->edges、bgr->HSV->edges、bgr->customHSV->edges之后的边 在它们下面的每个过滤器的相应设置中,以达到大致相同的清晰结果。过滤器的半径越大,计算就越复杂和耗时。

它在图像处理的后续步骤中产生更清晰的边缘。 可以通过 r g b 通道中的参数进行进一步调整:

红色 = (red-16)*1.1384; //255/244=1.1384 这里 16 – 数字越大,V 越清晰 255/244 - 也会影响将其扩展到 0-255 范围之外的结果,稍后将被剪裁。 这里的数字似乎是黄金比例,但任何人都可以根据特定需求进行调整。

使用此功能,可以通过将颜色直接连接到原始图像中的适当通道来避免将 BGR 转换为 RGB。

在性能方面可能有点笨拙。就我而言,它用于色彩平衡和直方图调整的第一步,因此速度并不是那么关键。

要在持续处理视频流中使用它需要速度优化,我认为通过使用指针和降低循环复杂度。优化不完全是我的一杯茶。因此,如果有人帮助为社区优化它,那就太好了。 在这里可以使用了:

Mat bgr2hsvCustom ( Mat& image )
{
    //smallParam = 16;
    for(int x = 0; x < image.rows; x++)
    {
        for(int y = 0; y<image.cols; y++)
        {
            //assigning vector to individual float BGR values
            float blue  = image.at<cv::Vec3b>(x,y)[0];
            float green = image.at<cv::Vec3b>(x,y)[1];
            float red   = image.at<cv::Vec3b>(x,y)[2];

            float sat, hue, minValue, maxValue, delta;

            float const ang0    = 0; // func min and max don't accept varaible and number
            float const ang240  = 240;
            float const ang255  = 255;

            red = (red-16)*1.1384; //255/244
            green = (green-16)*1.1384;
            blue = (min(blue*2,ang240)-16)*1.1384;
            minValue = min(red,min(green,blue));
            maxValue = max(red,max(green,blue));
            delta = maxValue - minValue;

            if (maxValue != 0)
            {
                sat = (delta*255) / maxValue;
                if ( delta != 0)
                {
                    if (red == maxValue){
                        hue  =      60*(green - blue)/delta;
                    }
                    else if( green == maxValue ) {
                        hue = 120 + 60*( blue - red )/delta;
                    }
                    else{
                        hue = 240 + 60*( red - green )/delta;
                    }
                    if( hue < 0 ){
                        hue = hue + 360;
                    }
                }
                else{
                    sat = 0;
                    hue = 0;
                }
            }
            else{
                hue = 0;
                sat = 0;
            }
            image.at<cv::Vec3b>(x,y)[0] = max(min(floor(maxValue),ang255),ang0);         //V
            image.at<cv::Vec3b>(x,y)[1] = max(min(floor(sat),ang255),ang0);              //S
            image.at<cv::Vec3b>(x,y)[2] = max(min(floor(((hue*255)/360)),ang255),ang0);  //H
        }
    }
    return image;
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-11-20
    • 1970-01-01
    • 2014-12-23
    • 1970-01-01
    • 2015-10-15
    • 2014-05-21
    相关资源
    最近更新 更多