[CV]检测灰色块

遇到一个看起来非常简单的问题:一张图片里面有一些某种颜色的色块,怎么样找到它们?
sample-mask
比较囧的是这个问题的起因。因为图片的标注文件丢了,不得不这么反向做检测来找回这些标注…想想人脸那么麻烦的结构都可以被检测出来,CV对付这种几乎完美的单颜色色块应该是小菜一碟吧。所以,大家虽然感觉反向检测自己处理的图片比较囧,但是完全不觉得这是个问题。同屋的哥们当场表示,他可以在10分钟之内搞定。

他的做法是我们一开始的想法,先按照色块的颜色(RGB: 128,128,128)把图片二值化,由于JPEG压缩,色块可能会有噪声。
sample-mask-binarized

然后我们准备对每行求和,对每列求和,会得到两个“直方图”,然后根据“峰”的位置和宽度就可以知道色块的位置和大小了。这个方法的确可以处理一张图里只有一个色块的情况,但是当图里有多个色块的时候,会出现“峰”的叠加,比如这张图,按行求和之后,由于有并排的色块。直方图就会变成这样:
sample-mask-histogram
这种情况之前这种方法就不好处理了。

结果这个看起来非常简单的问题,我们也折腾了好半天。最后还是得人指点,用连通分量来做,才得以解决。

做法是在二值化的图像上,找到不同的连通块 (Connected component),然后留下比较大的,就是灰色块了。为了处理噪声,当然需要用Gaussian做一做模糊之类的。效果还不错。(彩色色块表示检测出来的灰色色块)

large-sample-mask

large-sample-mask-result

问题总是没有看起来的那么简单。
matlab 代码放到 Github 上了: detect-gray-square

PCA的实现

PCA,全称是Principal component analysis,中文叫做主成分分析,是一种常用的数据处理手段。

直观的说,PCA是一种降维的手法。比如现在我们有1000个数据点,每个数据点是一个128维的向量,存储上可以是一个1000×128维的数组。经过PCA处理,我们仍然得到1000个数据点,但是每个数据点是一个小于128维的向量,比如我们用PCA将128维的数据降到64维。
PCA可以保证,在降维之后,数据表示的信息损失最小。

“损失最小”具体怎么定义?
还是以1000个128维的点为例,这1000个点,也就是1000个向量在一个128维的空间中。从在任何一维,也就是一个方向上来看,如果在这个方向上,各个向量大小差异很大,那么这个方向是很重要的。
也就是,反过来看,如果在某个方向上,每一个向量大小都很接近,那么如果不考虑这个方向,也就是去掉这一维的数据,对我们分析这1000个点并没有多大的影响。所以,“损失最小”对应着“差异最小”。

那么具体怎么做呢?

这里是两种常用的方法: SVD分解和EIG分解(特征值分解)。
共同点在于先从数据得到一个矩阵M,M的特征值个数对应着数据的维度,特征值越大那么对应的这一维越重要,也就是“差异越大”。

SVD分解, matlab

    sub_input_data = (input_data - repmat(mean(input_data),count,1))/sqrt(count-1);
    [U,S,V] = svd(sub_input_data);
    % First out_dim columns as PCA bases
    pcaV = V(:,1:out_dim);
    output_data = input_data * pcaV;

EIG分解, matlab

    mean_input_data = mean(input_data);
    sub_input_data = input_data - repmat(mean_input_data, count,1);
    mean_mat = sub_input_data' * sub_input_data ./ (count - 1);
    cov_mat = mean_mat;
    [V D] = eig(cov_mat);
    % Last out_dim columns as PCA bases
    pcaV = V(:,in_dim - out_dim + 1: in_dim);
    output_data = input_data * pcaV;

如果用C++的话,OpenCV本身就提供PCA,当然也可以自己实现。
OpenCV下可以用这个方法做EIG分解。

cv::eigen(covMat, eigenValues, eigenVectors);

具体Matlab代码也比较简单,放在Github上了。
MatlabPCA

原始数据:

PCA之后:

Draw ROC Curve

A piece of fairly simple Matlab script to draw the ROC Curve from an array of scores and an array of labels.

function [Tps, Fps] = ROC(scores, labels)
 
%% Sort Labels and Scores by Scores
sl = [scores; labels];
[d1 d2] = sort(sl(1,:));
 
sorted_sl = sl(:,d2);
s_scores = sorted_sl(1,:);
s_labels = round(sorted_sl(2,:));
 
%% Constants
counts = histc(s_labels, unique(s_labels));
 
Tps = zeros(1, size(s_labels,2) + 1);
Fps = zeros(1,  size(s_labels,2) + 1);
 
negCount = counts(1);
posCount = counts(2);
 
%% Shift threshold to find the ROC
for thresIdx = 1:(size(s_scores,2)+1)
 
    % for each Threshold Index
    tpCount = 0;
    fpCount = 0;
 
    for i = [1:size(s_scores,2)]
 
        if (i >= thresIdx)           % We think it is positive
            if (s_labels(i) == 1)   % Right!
                tpCount = tpCount + 1;
            else                    % Wrong!
                fpCount = fpCount + 1;
            end
        end
 
    end
 
    Tps(thresIdx) = tpCount/posCount;
    Fps(thresIdx) = fpCount/negCount;
 
end
 
%% Draw the Curve

% Sort [Tps;Fps]
x = Tps;
y = Fps;

% Interplotion to draw spline line
count = 100;
dist = (x(1) - x(size(x,2)))/100;
xx = [x(1):-dist:x(size(x,2))];

% In order to get the interpolations, we remove all the unique numbers
[d1 d2] = unique(x);
uni_x = x(1,d2);
uni_y = y(1,d2);
yy = spline(uni_x,uni_y,xx);

% No value should exceed 1
yy = min(yy, 1);

plot(x,y,'x',xx,yy);

Hope it helps.


Some improvements were added.

For a sample input:

>> scores = rand(1,20)*100

scores =

  Columns 1 through 7

   43.8744   38.1558   76.5517   79.5200   18.6873   48.9764   44.5586

  Columns 8 through 14

   64.6313   70.9365   75.4687   27.6025   67.9703   65.5098   16.2612

  Columns 15 through 20

   11.8998   49.8364   95.9744   34.0386   58.5268   22.3812

>> labels = round(rand(1,20))

labels =

  Columns 1 through 12

     1     0     1     1     1     1     1     0     0     0     1     0

  Columns 13 through 20

     1     0     1     0     0     0     1     0

>> ROC(scores,labels);

Gives an output like:
ROC

Fork it on Github: DrawROC on Github

[Libsvm]libsvm for matlab compilation error on mac

Error:

>> make
/bin/bash: -c: line 0: unexpected EOF while looking for matching `"'
/bin/bash: -c: line 1: syntax error: unexpected end of file
??? Error using ==> mex at 208
Unable to complete successfully.

Error in ==> make at 5
mex -O -largeArrayDims -I..\ -c ..\svm.cpp

Fix:

  1. Under folder ./libsvm-3.1/matlab
  2. Open file make.m
  3. replace backslash(\) to slash(/)
  4. save and close file
  5. execute the make command inside matlab


Error:

>> make
i686-apple-darwin10-gcc-4.2.1: svm.obj: No such file or directory
i686-apple-darwin10-gcc-4.2.1: svm_model_matlab.obj: No such file or directory

    mex: link of ' "svmtrain.mexmaci64"' failed.

??? Error using ==> mex at 208
Unable to complete successfully.

Error in ==> make at 7
mex -O -largeArrayDims -I../ svmtrain.c svm.obj svm_model_matlab.obj

Fix:

  1. Open the make.m file again
  2. Replace all the occurrences of .obj to .o
  3. Save and close
  4. Try again!

Hope these help :]