从彩色背景中提取黑色物体

时间:2014-04-29 12:28:59

标签: matlab opencv image-processing computer-vision k-means

人眼很容易从其他颜色中辨别黑色。但是计算机呢?

我在普通A4纸上打印了一些色块。由于有三种墨水组成彩色图像,青色,品红色和黄色,我设置每个块的颜色C = 20%,C = 30%,C = 40%,C = 50%,其余两种颜色这是我的源图像的第一列。到目前为止,没有黑色( K 的CMYK)墨水应该打印。之后,我设置每个点的颜色K = 100%,其余颜色为0以打印黑点。

source

你可能觉得我的形象很奇怪而且可怕。实际上,图像被放大了30倍,并且可以清楚地看到墨水如何作弊我们的眼睛。色条妨碍我识别这些黑点(点打印为800 dpi的一个像素)。如果没有颜色背景,我习惯blur并执行canny edge detector来提取边缘。但是,在添加彩色背景时,由于条带的原因,grayscaleedge detector无法获得良好的效果。为了解决这些问题,我的眼睛会怎样做?

我决定检查源图像的亮度。我提到this article和公式:

  

亮度= sqrt(0.299 R * R + 0.587 G * G + 0.114 B * B)

brightness

亮度更接近人类的感知,并且在黄色背景下效果非常好,因为与青色和品红色相比,黄色的亮度最高。但如何使青色和洋红色条带尽可能明亮?预期的结果是所有条带都消失了。


更复杂的图片

C = 40%,M = 40%

c40m40

C = 40%,Y = 40%

c40y40

Y = 40%,M = 40%

y40m40

C = 40%,Y = 40%亮度图像的FFT结果

c40y40 freq

任何人都可以给我一些提示来删除彩条吗?


@natan我试过FFT method你建议我,但我不幸在两个轴x和y都达到峰值。为了像你一样绘制频率,我将图像调整为正方形。

c40m40

c40m40 freq

5 个答案:

答案 0 :(得分:3)

我会将图像转换为HSV颜色空间,然后使用Value通道。这基本上分离了颜色和亮度信息。

这是50%的青色图像

enter image description here

然后你可以做一个简单的阈值来隔离点。

enter image description here

我只是很快就这样做了,我相信你可以获得更好的结果。也许在图像中找到轮廓,然后删除任何小面积的轮廓,以过滤任何剩余的噪音。

答案 1 :(得分:2)

在检查图像后,我认为稳健的阈值比任何事情都更简单。例如,看着C = 40%,M = 40%的照片,我首先将强度反转为黑色(信号)仅使用白色

im=(abs(255-im));

我们可以检查其RGB histograms using this

hist(reshape(single(im),[],3),min(single(im(:))):max(single(im(:)))); 
colormap([1 0 0; 0 1 0; 0 0 1]);

enter image description here

所以我们看到对某些中等强度有很大的贡献,而现在是白色的“信号”大多分为更高的值。然后我应用了一个简单的阈值如下:

thr = @(d) (max([min(max(d,[],1))  min(max(d,[],2))])) ;
for n=1:size(im,3)
    imt(:,:,n)=im(:,:,n).*uint8(im(:,:,n)>1.1*thr(im(:,:,n)));
end

imt=rgb2gray(imt);

并摆脱小于某些典型区域大小的物体

min_dot_area=20;
bw=bwareaopen(imt>0,min_dot_area);
imagesc(bw); 
colormap(flipud(bone));

这是与原始图像一起的结果: enter image description here

这个阈值的起源来自this code我写的是在嘈杂的背景中以二维峰值或斑点的形式假设稀疏信号。稀疏我的意思是没有堆积的山峰。在这种情况下,当在x或y轴上投影max(图像)时((max(im,[],1)(max(im,[],1),您可以很好地测量背景。这是因为您采用{{{{{{{ 1}}向量。

如果你想以不同的方式看待它,你可以看一下图像强度的直方图。背景应该是某种强度附近的某种正态分布,信号应该高于该强度,但出现次数要低得多。通过查找其中一个轴(x或y)的max(im),您可以发现最大噪声级别。

你会看到阈值在直方图中选择了那个点上面仍然有一些噪音,但是所有信号都高于它。这就是我将其调整为max(im)的原因。最后,有许多更好的方法可以获得稳健的阈值,这是一种快速而肮脏的方式,在我看来已经足够了......

答案 2 :(得分:2)

感谢大家发布他的答案!经过一番搜索和尝试后,我还想出了一种自适应方法从彩色背景中提取这些黑点。似乎只考虑 亮度无法完美解决问题。因此, natan 计算和分析RGB直方图的方法更加稳健。不幸的是,我仍然无法获得一个强大的阈值来提取其他颜色样本中的黑点,因为当我们添加更深的颜色(例如Cyan> 60)或将两种颜色混合在一起时(例如Cyan = 50,事情变得越来越难以预测)洋红色= 50)。

有一天,我谷歌“提取颜色”,TinEye's color extractioncolor thief激励我。它们都是非常酷的应用程序,前网站处理的图像正是我想要的。所以我决定自己实现类似的东西。我在这里使用的算法是 k-means clustering 。其他一些与搜索相关的关键词可能是color palettecolor quantationgetting dominant color

我首先使用高斯滤镜来平滑图像。

GaussianBlur(img, img, Size(5, 5), 0, 0);

OpenCV具有kmeans功能,它为编码节省了大量时间。我修改了this code

// Input data should be float32 
Mat samples(img.rows * img.cols, 3, CV_32F);
for (int i = 0; i < img.rows; i++) {
    for (int j = 0; j < img.cols; j++) {
        for (int z = 0; z < 3; z++) {
            samples.at<float>(i + j * img.rows, z) = img.at<Vec3b>(i, j)[z];
        }
    }
}

// Select the number of clusters
int clusterCount = 4;
Mat labels;
int attempts = 1;
Mat centers;
kmeans(samples, clusterCount, labels, TermCriteria(CV_TERMCRIT_ITER|CV_TERMCRIT_EPS, 10, 0.1), attempts, KMEANS_PP_CENTERS, centers);

// Draw clustered result
Mat cluster(img.size(), img.type());
for (int i = 0; i < img.rows; i++) {
     for(int j = 0; j < img.cols; j++) { 
        int cluster_idx = labels.at<int>(i + j * img.rows, 0);
        cluster.at<Vec3b>(i, j)[0] = centers.at<float>(cluster_idx, 0);
        cluster.at<Vec3b>(i, j)[1] = centers.at<float>(cluster_idx, 1);
        cluster.at<Vec3b>(i, j)[2] = centers.at<float>(cluster_idx, 2);
    }
}
imshow("clustered image", cluster); 
// Check centers' RGB value
cout << centers;

聚类后,我将结果转换为灰度,找到最黑的颜色,这种颜色更可能是黑点的颜色。

// Find the minimum value
cvtColor(cluster, cluster, CV_RGB2GRAY);
Mat dot = Mat::zeros(img.size(), CV_8UC1);
cluster.copyTo(dot);
int minVal = (int)dot.at<uchar>(dot.cols / 2, dot.rows / 2);
for (int i = 0; i < dot.rows; i += 3) {
    for (int j = 0; j < dot.cols; j += 3) {
        if ((int)dot.at<uchar>(i, j) < minVal) {
            minVal = (int)dot.at<uchar>(i, j);
        }
    }
}
inRange(dot, minVal - 5 , minVal + 5, dot);
imshow("dot", dot);

让我们测试两张图片。

clusterCount = 4

enter image description here

enter image description here

enter image description here

clusterCount = 5

enter image description here

enter image description here

enter image description here

k-means聚类的一个缺点是固定的clusterCount不能应用于每个图像。对于较大的图像,聚类也不是那么快。这个问题让我很烦恼。我更好的实时性能(在iPhone上)的脏方法是裁剪1/16的图像并聚集较小的区域。然后将原始图像中的所有像素与每个聚类中心进行比较,并选择最接近“黑色”颜色的像素。我只是计算两种RGB颜色之间的欧氏距离。

答案 3 :(得分:0)

一种简单的方法是只对所有像素进行阈值处理。这是用伪代码表达的想法。

for each pixel in image
    if brightness < THRESHOLD
        pixel = BLACK
    else
        pixel = WHITE

或者如果你总是处理青色,品红色和黄色背景,那么你可能会用标准获得更好的结果

if pixel.r < THRESHOLD and pixel.g < THRESHOLD and pixel.b < THRESHOLD

这种方法只能为简单的图像提供良好的效果,除了黑点以外什么都不是。

您可以尝试使用THRESHOLD的值来为图像找到一个好的值。

答案 4 :(得分:0)

我建议转换为一些基于色度的色彩空间,如LCH,并调整亮度和色度的同时阈值。这是L&lt;的结果掩码。 50&amp; C&lt; 25为输入图像:

enter image description here

似乎您需要自适应阈值,因为不同的值最适合图像的不同区域。

您也可以使用HSV或HSL作为色彩空间,但它们在感知上比从实验室派生的LCH更不均匀。