2
votes

I am building a program in objective C/C++ and openCV. I am pretty skilled in Objective C but new to C++.
I am building custom RGB2HSV algorithm. My algorithm is slightly different from the openCV library cvtColor(in, out, CV_RGB2HSV).
The one I try to translate form Matlab to opencV/C++ produces so clear HSV image that no additional filtering is needed before further processing. Code below – Matlab code is self-explanatory.

I try to translate it to C++/openCV function out of it but I hit the wall trying to access pixel values of the image. I am new to C++. I read a lot on the ways how to access Mat structure but usually I obtain either bunch of letters in a place of zeros or a number typically something like this “\202 k g”. When I try to do any multiplication operations on the say \202 the result has nothing to do with math.

Please help me to properly access the pixel values. Also in current version using uchar won’t work because some values are outside 0-255 range. The algorithm is not mine. I cannot even point the source but it gives clearly better results than stock RGB2HSV.

Also the algorithm below is for one pixel. It needs to be applied each pixel in the image so in final version it need to wrapped with for { for {}} loops.

I also wish to share this method with community so everyone can benefit from it and saving on pre-filtering.

Please help me translate it to C++ / openCV. If possible with the best practices speed wise. Or at least how to clearly access the pixel value so it is workable with range of mathematical equations. Thanks in advance.

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
3
What are you asking exactly ? How to access the value of one pixel in a Mat object in C++ ? Or you want a complete, working code for converting RGB to HSV ?Sunreef
At least how to get to the workable value of a pixel. If someone can help build the entire function that would be beneficial for community. I can build it with simple mathematical logic but I am aware that it won't me the most effective way in C++Jad Gift

3 Answers

1
votes

To access the value of a pixel in a 3-channel, 8-bit precision image (type CV_8UC3) you have to do it like this:

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

If, as you say, 8-bit precision and range are not enough, you can declare a cv::Mat of type CV_32F to store floating point 32-bit numbers.

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;
    }
}

Be aware that OpenCV stores rgb values in the BGR order and not RGB. Take a look at OpenCV docs to learn more about it.

0
votes

If you are concerned by performance and fairly comfortable with pixel indexes, you can use directly the Mat ptr.

For example:

  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;

The previous code should print:

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]

The at access should be enough for most of the cases and is much more readable / less likely to make a mistake than using the ptr access.

References:

0
votes

enter image description hereThanks everybody for help. Thanks to your hints I constructed the custom rgb2hsv function C++/openCV.

From the top left respectively, edges after bgr->gray->edges, bgr->HSV->edges, bgr->customHSV->edges Below each of them corresponding settings of the filters to achieve approximately the same clear results. The bigger the radius of a filter the more complex and time consuming computations.

It produces clearer edges in next steps of image processing. It can be tweaked further experimenting with parameters in r g b channels:

red = (red-16)*1.1384; //255/244=1.1384 here 16 – the bigger the number the clearer V becomes 255/244 – also affect the outcome extending it beyond ranges 0-255, later to be clipped. This numbers here seem to be golden ratio but anyone can adjust for specific needs.

With this function translating BGR to RGB can be avoided by directly connecting colors to proper channels in raw image.

Probably it is a little clumsy performance wise. In my case it serves in first step of color balance and histogram adjustment so speed is not that critical.

To use in constant processing video stream it need speed optimization, I think by using pointers and reducing loop complexity. Optimization is not exactly my cup of tea. So if someone helped to optimize it for the community that would be great. Here it is ready to use:

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;
}