ma.mehralian
ma.mehralian

Reputation: 1284

OpenCV: lab color quantization to predefined colors

I trying to reduce my image colors to some predefined colors using the following function:

void quantize_img(cv::Mat &lab_img, std::vector<cv::Scalar> &lab_colors) {
    float min_dist, dist;
    int min_idx;
    for (int i = 0; i < lab_img.rows*lab_img.cols * 3; i += lab_img.cols * 3) {
        for (int j = 0; j < lab_img.cols * 3; j += 3) {
            min_dist = FLT_MAX;
            uchar &l = *(lab_img.data + i + j + 0);
            uchar &a = *(lab_img.data + i + j + 1);
            uchar &b = *(lab_img.data + i + j + 2);
            for (int k = 0; k < lab_colors.size(); k++) {
                double &lc = lab_colors[k](0);
                double &ac = lab_colors[k](1);
                double &bc = lab_colors[k](2);
                dist = (l - lc)*(l - lc)+(a - ac)*(a - ac)+(b - bc)*(b - bc);
                if (min_dist > dist) {
                    min_dist = dist;
                    min_idx = k;
                }
            }
            l = lab_colors[min_idx](0);
            a = lab_colors[min_idx](1);
            b = lab_colors[min_idx](2);
        }
    }
}

However it does not seem to work properly! For example the output for the following input looks amazing!

if (!(src = imread("im0.png")).data)
    return -1;
cvtColor(src, lab, COLOR_BGR2Lab);
std::vector<cv::Scalar> lab_color_plate_({
    Scalar(100,  0 ,   0),  //white
    Scalar(50 ,  0 ,   0),  //gray
    Scalar(0  ,  0 ,   0),  //black
    Scalar(50 , 127, 127),  //red
    Scalar(50 ,-128, 127),  //green
    Scalar(50 , 127,-128),  //violet
    Scalar(50 ,-128,-128),  //blue
    Scalar(68 , 46 ,  75),  //orange
    Scalar(100,-16 ,  93)   //yellow
});
//convert from conventional Lab to OpenCV Lab
for (int k = 0; k < lab_color_plate_.size(); k++) {
    lab_color_plate_[k](0) *= 255.0 / 100.0;
    lab_color_plate_[k](1) += 128;
    lab_color_plate_[k](2) += 128;
}
quantize_img(lab, lab_color_plate_);
cvtColor(lab, lab, CV_Lab2BGR);
imwrite("im0_lab.png", lab);

Input image: Input image

Output image enter image description here

Can anyone explain where the problem is?

Upvotes: 0

Views: 1116

Answers (1)

api55
api55

Reputation: 11420

After checking your algorithm I noticed that the algorithm is correct 100% and the problem is your color space.... Let's take one of the colors that is changed "wrongly" like the green from the trees.

Using a color picker tool in GIMP it tells you that at least one of the green used is in RGB (111, 139, 80). When this is converted to LAB, you get (54.4, -20.7, 28.3). The distance to green is (by your formula) 21274.34 , and with grey the distance is 1248.74... so it will choose grey over green, even though it is a green color.

A lot of values in LAB can generate a green value. You can test it out the color ranges in this webpage. I would suggest you to use HSV or HSL and compare the H values only which is the Hue. The other values changes only the tone of green, but a small range in the Hue determines that it is green. This will probably give you more accurate results.

As some suggestion to improve your code, use Vec3b and cv::Mat functions like this:

for (int i = 0; i < lab_img.rows; ++i) {
    for (int j = 0; j < lab_img.cols; ++j) {
        Vec3b pixel = lab_img.at<Vec3b>(i,j);
    }
}

This way the code is more readable, and some checks are done in debug mode.

The other way would be to do a one loop since you don't care about indices

 auto currentData = reinterpret_cast<Vec3b*>(lab_img.data); 
 for (size_t i = 0; i < lab_img.rows*lab_img.cols; i++)
 {
     auto& pixel = currentData[i];
 }

This way is also better. This last part is just a suggestion, there is nothing wrong with your current code, just harder to read understand to the outside viewer.

Upvotes: 1

Related Questions