如何使用OpenCV减少图像中的颜色数量?
image-processing
opencv
6
0

我有一组图像文件,并且想要将它们的颜色数量减少到64种。如何使用OpenCV做到这一点?

我需要这个,以便可以处理64尺寸的图像直方图。我正在实施CBIR技术

我想要的是将色彩量化到4位调色板。

参考资料:
Stack Overflow
收藏
评论
共 5 个回答
高赞 时间 活跃

您可能会考虑使用K均值,但是在这种情况下,它很可能会非常慢。更好的方法可能是自己“手动”执行此操作。假设您具有CV_8UC3类型的图像,即每个像素由0到255( Vec3b )的3个RGB值表示的图像。您可以将这256个值“映射”到仅4个特定值,这将产生4 x 4 x 4 = 64可能的颜色。

我有一个数据集,在这里我需要确保暗=黑色,亮=白色,并减少两者之间所有颜色的数量。这是我所做的(C ++):

inline uchar reduceVal(const uchar val)
{
    if (val < 64) return 0;
    if (val < 128) return 64;
    return 255;
}

void processColors(Mat& img)
{
    uchar* pixelPtr = img.data;
    for (int i = 0; i < img.rows; i++)
    {
        for (int j = 0; j < img.cols; j++)
        {
            const int pi = i*img.cols*3 + j*3;
            pixelPtr[pi + 0] = reduceVal(pixelPtr[pi + 0]); // B
            pixelPtr[pi + 1] = reduceVal(pixelPtr[pi + 1]); // G
            pixelPtr[pi + 2] = reduceVal(pixelPtr[pi + 2]); // R
        }
    }
}

导致[0,64) [64,128)变为0[64,128) -> 64[128,255) -> 255 ,产生27种颜色:

在此处输入图片说明在此处输入图片说明

在我看来,这比其他答案中提到的任何其他方法都简洁,清晰,快速。

您还可以考虑将这些值减小为某个数字的倍数之一,例如:

inline uchar reduceVal(const uchar val)
{
    if (val < 192) return uchar(val / 64.0 + 0.5) * 64;
    return 255;
}

这将产生一组5种可能的值: {0, 64, 128, 192, 255} ,即125种颜色。

收藏
评论

这里建议的答案确实很好。我以为我也会补充我的想法。在这里,我遵循许多评论的提法,据说可以用RGB图像中每个通道的2位来表示64种颜色。

下面代码中的函数将图像和量化所需的位数作为输入。它使用位操作来“丢弃” LSB位,并仅保留所需数量的位。结果是一种灵活的方法,可以将图像量化为任意数量的位。

#include "include\opencv\cv.h"
#include "include\opencv\highgui.h"

// quantize the image to numBits 
cv::Mat quantizeImage(const cv::Mat& inImage, int numBits)
{
    cv::Mat retImage = inImage.clone();

    uchar maskBit = 0xFF;

    // keep numBits as 1 and (8 - numBits) would be all 0 towards the right
    maskBit = maskBit << (8 - numBits);

    for(int j = 0; j < retImage.rows; j++)
        for(int i = 0; i < retImage.cols; i++)
        {
            cv::Vec3b valVec = retImage.at<cv::Vec3b>(j, i);
            valVec[0] = valVec[0] & maskBit;
            valVec[1] = valVec[1] & maskBit;
            valVec[2] = valVec[2] & maskBit;
            retImage.at<cv::Vec3b>(j, i) = valVec;
        }

        return retImage;
}


int main ()
{
    cv::Mat inImage;
    inImage = cv::imread("testImage.jpg");
    char buffer[30];
    for(int i = 1; i <= 8; i++)
    {
        cv::Mat quantizedImage = quantizeImage(inImage, i);
        sprintf(buffer, "%d Bit Image", i);
        cv::imshow(buffer, quantizedImage);

        sprintf(buffer, "%d Bit Image.png", i);
        cv::imwrite(buffer, quantizedImage);
    }

    cv::waitKey(0);
    return 0;
}

这是上述函数调用中使用的图像:

在此处输入图片说明

每个RGB通道量化为2位的图像(总共64种颜色):

在此处输入图片说明

每个通道3位:

在此处输入图片说明

4位...

在此处输入图片说明

收藏
评论

该主题在《 OpenCV 2计算机视觉应用程序编程手册》中有详细介绍

第2章显示了一些归约运算,其中一个在C ++中演示,然后在Python中进行了演示:

#include <iostream>
#include <vector>

#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>


void colorReduce(cv::Mat& image, int div=64)
{    
    int nl = image.rows;                    // number of lines
    int nc = image.cols * image.channels(); // number of elements per line

    for (int j = 0; j < nl; j++)
    {
        // get the address of row j
        uchar* data = image.ptr<uchar>(j);

        for (int i = 0; i < nc; i++)
        {
            // process each pixel
            data[i] = data[i] / div * div + div / 2;
        }
    }
}

int main(int argc, char* argv[])
{   
    // Load input image (colored, 3-channel, BGR)
    cv::Mat input = cv::imread(argv[1]);
    if (input.empty())
    {
        std::cout << "!!! Failed imread()" << std::endl;
        return -1;
    } 

    colorReduce(input);

    cv::imshow("Color Reduction", input);   
    cv::imwrite("output.jpg", input);   
    cv::waitKey(0);

    return 0;
}

在下面可以找到输入图像(左)和此操作的输出 (右):

Python中的等效代码如下:( @ eliezer-bernart的积分

import cv2
import numpy as np

input = cv2.imread('castle.jpg')

# colorReduce()
div = 64
quantized = input // div * div + div // 2

cv2.imwrite('output.jpg', quantized)
收藏
评论

有很多方法可以做到这一点。 jeff7建议的方法还可以,但是有一些缺点:

  • 方法1具有必须选择的参数N和M,并且还必须将其转换为另一个颜色空间。
  • 回答的方法2可能很慢,因为您应该计算一个16.7万亿的柱状图直方图并按频率对其进行排序(以获得64个较高的频率值)

我喜欢使用基于最高有效位的算法在RGB颜色中使用并将其转换为64色图像。如果您使用的是C / OpenCV,则可以使用下面的函数。

如果您使用的是灰度图像,建议您使用OpenCV 2.3的LUT()函数,因为它速度更快。有一个有关如何使用LUT减少颜色数量的教程。请参阅: 教程:如何扫描图像,查找表...但是,如果您使用RGB图像,我会发现它更加复杂。

void reduceTo64Colors(IplImage *img, IplImage *img_quant) {
    int i,j;
    int height   = img->height;   
    int width    = img->width;    
    int step     = img->widthStep;

    uchar *data = (uchar *)img->imageData;
    int step2 = img_quant->widthStep;
    uchar *data2 = (uchar *)img_quant->imageData;

    for (i = 0; i < height ; i++)  {
        for (j = 0; j < width; j++)  {

          // operator XXXXXXXX & 11000000 equivalent to  XXXXXXXX AND 11000000 (=192)
          // operator 01000000 >> 2 is a 2-bit shift to the right = 00010000 
          uchar C1 = (data[i*step+j*3+0] & 192)>>2;
          uchar C2 = (data[i*step+j*3+1] & 192)>>4;
          uchar C3 = (data[i*step+j*3+2] & 192)>>6;

          data2[i*step2+j] = C1 | C2 | C3; // merges the 2 MSB of each channel
        }     
    }
}
收藏
评论

OpenCV库中已经提供了K-means聚类算法。简而言之,对于用户定义的k值(=簇数),它确定围绕数据簇的最佳质心。因此,在您的情况下,对于给定的k = 64,您可以找到围绕其质心将像素值聚类的质心。如果您在Google周围搜索,则有详细信息。 是k均值的简短介绍。

类似的东西,以你可能想在这里问的SO使用K-手段,希望它帮助。

另一种方法是在OpenCV中使用金字塔均值漂移滤波器功能。它产生的图像有些“变平”,即颜色数量较少,因此可能会对您有所帮助。

收藏
评论
新手导航
  • 社区规范
  • 提出问题
  • 进行投票
  • 个人资料
  • 优化问题
  • 回答问题

关于我们

常见问题

内容许可

联系我们

@2020 AskGo
京ICP备20001863号