【问题标题】:Paper currency recognition by image processing通过图像处理识别纸币
【发布时间】:2014-10-24 04:50:18
【问题描述】:

在纸币中,我想检查条带是断线还是实线。为此,我拍了一张背景为强光的照片。我得到了以下两张照片,一张是真币,另一张是假币。我在条带存在的位置裁剪图像并进行开闭重建,最后计算黑色像素。但结果并不如我所愿。有什么帮助吗?

图片如下:

%Code for the thine strip
clear all;
close all;
I = imread('IMG_4267.jpg');
imageSize = size(I);
croppedImage = imcrop(I,[300 0 30 570]);
gray=rgb2gray(croppedImage);
se1 = strel('square',2);
I1e = imerode(gray, se1);
I1e = imreconstruct(I1e, gray);
I1b = imdilate(I1e, se1);
I1c = imreconstruct(imcomplement(I1b), imcomplement(I1e));
I1d = imcomplement(I1c);
Edge2=edge(I1d,'canny');
BW1 = im2bw(Edge2);
nBlack = sum(BW1(:));

【问题讨论】:

    标签: matlab image-processing computer-vision image-segmentation image-recognition


    【解决方案1】:

    这是我在确定您的钞票是假钞还是真钞时相当拙劣的尝试。我注意到纸币之间的一件事是,真纸币的细条或多或少是连续的,而假纸条的细线是零散的。可以说,假钞票在细条上多行,而真钞只有一行。让我们尝试获取我们的图像,以便我们检测条带本身(正如您也尝试过的那样)并计算我们看到的行数。如果我们只看到一条线,那是真的,但如果我们看到多条线,那就是假的。我将向您介绍我是如何处理中间图像的。

    第 1 步 - 读入图片(当然)

    我将直接从 StackOverflow 读取您的图像。 imread 非常适合从在线 URL 读取图像:

    %%//Read in image
    clear all;
    close all;
    Ireal = imread('http://i.stack.imgur.com/SqbnIm.jpg'); %//Real
    Ifake = imread('http://i.stack.imgur.com/2U3DEm.jpg'); %//Fake
    

    第 2 步 - 将图像分解为 HSV 并进行分析

    我注意到的一件事是,钞票的颜色很深,而钞票的颜色主要是绿色。我使用了一些基本的彩色图像处理作为预处理步骤。我将图像转换为 HSV (Hue-Saturation-Value) 并分别查看了每个组件:

    %% Pre-analysis
    hsvImageReal = rgb2hsv(Ireal);
    hsvImageFake = rgb2hsv(Ifake);
    figure;
    imshow([hsvImageReal(:,:,1) hsvImageReal(:,:,2) hsvImageReal(:,:,3)]);
    title('Real');
    figure;
    imshow([hsvImageFake(:,:,1) hsvImageFake(:,:,2) hsvImageFake(:,:,3)]);
    title('Fake');
    

    图片如下:

    在这段代码中,我将每个组件并排显示,分别表示色调、饱和度和值。你会注意到一些非常奇怪的东西。黑色细条具有相当高的饱和度,这是有道理的,因为黑色可以被视为纯饱和度(没有白光)的“颜色”。 value 组件的条带的值非常低,这也是有意义的,因为 value 捕获了颜色的亮度/强度。

    第 3 步 - 设置饱和度和值平面的阈值以创建二值图像

    根据我上面所做的观察,我将在饱和度和价值平面上对图像进行阈值处理。具有相当高的组合饱和度和相当低的值的任何点都是作为黑色条带一部分的候选点。我将自己裁剪出条带以使事情变得更容易(就像你已经做过的那样)。请注意,每个图像中条带的位置不同,因此我必须进行相应调整。我只是提取了正确的列,同时保持行和切片相同。这些饱和度和价值阈值是相当临时的,所以我不得不尝试这些以获得好的结果。

    %%//Initial segmentation
    croppedImageReal = hsvImageReal(:,90:95,:);
    croppedImageFake = hsvImageFake(:,93:98,:);
    satThresh = 0.4;
    valThresh = 0.3;
    BWImageReal = (croppedImageReal(:,:,2) > satThresh & croppedImageReal(:,:,3) < valThresh);
    figure;
    subplot(1,2,1);
    imshow(BWImageReal);
    title('Real');
    BWImageFake = (croppedImageFake(:,:,2) > satThresh & croppedImageFake(:,:,3) < valThresh);
    subplot(1,2,2);
    imshow(BWImageFake);
    title('Fake');
    

    这些是图像的样子:

    您可以看到,真正的条带或多或少比假条带具有更多的连通性。让我们做更多的处理来清理它

    步骤 #4 - 做一些小的关闭

    如果你看一下假钞的黑色细条,你会发现每条黑线都被相当多的像素分开,而真正的钞票真的没有分离。但是,您会看到,在上面的实际条带中,仍有部分线路断开。因此,让我们尝试将线路连接在一起。这是安全的,因为如果我们要对假图像执行此操作,则条带的各个部分相距甚远,关闭不应该有任何影响,但它有助于我们的真实图像分析。因此,我用一条 6 像素的垂直线封闭了这些图像。这是执行此操作的代码:

    %%//Post-process
    se = strel('line', 6, 90);
    BWImageCloseReal = imclose(BWImageReal, se);
    BWImageCloseFake = imclose(BWImageFake, se);
    figure;
    subplot(1,2,1);
    imshow(BWImageCloseReal);
    title('Real');
    subplot(1,2,2);
    imshow(BWImageCloseFake);
    title('Fake');
    

    这些是图像的样子:

    第 5 步 - 最终清理

    您会注意到,对于每张图像,边缘都有一些噪点像素。因此,让我们使用通过bwareaopen 开口的区域。此功能删除黑白图像中小于某个区域的像素区域。我将选择 15 来去除不属于条带的边缘像素。因此:

    %%//Area open the image
    figure;
    areaopenReal = bwareaopen(BWImageCloseReal, 15);
    imshow(areaopenReal);
    title('Real');
    figure;
    areaopenFake = bwareaopen(BWImageCloseFake, 15);
    imshow(areaopenFake);
    title('Fake');
    

    图片如下所示:

    第 6 步 - 计算黑线的数量

    最后一步是简单地计算每张图像中黑线的数量。如果只有1,则表示钞票是真实的,而如果多于1,则表示钞票是假的。我们可以使用bwlabel 并使用第二个参数来计算有多少对象。换句话说:

    %%//Count how many objects there are
    [~,countReal] = bwlabel(areaopenReal);
    [~,countFake] = bwlabel(areaopenFake);
    disp(['The total number of black lines for the real note is: ' num2str(countReal)]);
    disp(['The total number of black lines for the fake note is: ' num2str(countFake)]);
    

    我们在 MATLAB 中得到以下输出:

    The total number of black lines for the real note is: 1
    The total number of black lines for the fake note is: 4
    

    如您所见,真钞只有一行,而假钞不止一行。您必须使用此代码,具体取决于您必须使用哪种钞票才能使其工作,但这是开始的地方。


    完整代码

    为了完整起见,这里是完整的代码,您可以自己复制粘贴并在 MATLAB 中运行。

    %%//Read in image
    clear all;
    close all;
    Ireal = imread('http://i.stack.imgur.com/SqbnIm.jpg'); % Real
    Ifake = imread('http://i.stack.imgur.com/2U3DEm.jpg'); % Fake
    
    %%//Pre-analysis
    hsvImageReal = rgb2hsv(Ireal);
    hsvImageFake = rgb2hsv(Ifake);
    figure;
    imshow([hsvImageReal(:,:,1) hsvImageReal(:,:,2) hsvImageReal(:,:,3)]);
    title('Real');
    figure;
    imshow([hsvImageFake(:,:,1) hsvImageFake(:,:,2) hsvImageFake(:,:,3)]);
    title('Fake');
    
    %%//Initial segmentation
    croppedImageReal = hsvImageReal(:,90:95,:);
    croppedImageFake = hsvImageFake(:,93:98,:);
    satThresh = 0.4;
    valThresh = 0.3;
    BWImageReal = (croppedImageReal(:,:,2) > satThresh & croppedImageReal(:,:,3) < valThresh);
    figure;
    subplot(1,2,1);
    imshow(BWImageReal);
    title('Real');
    BWImageFake = (croppedImageFake(:,:,2) > satThresh & croppedImageFake(:,:,3) < valThresh);
    subplot(1,2,2);
    imshow(BWImageFake);
    title('Fake');
    
    %%//Post-process
    se = strel('line', 6, 90);
    BWImageCloseReal = imclose(BWImageReal, se);
    BWImageCloseFake = imclose(BWImageFake, se);
    figure;
    subplot(1,2,1);
    imshow(BWImageCloseReal);
    title('Real');
    subplot(1,2,2);
    imshow(BWImageCloseFake);
    title('Fake');
    
    %%//Area open the image
    figure;
    areaopenReal = bwareaopen(BWImageCloseReal, 15);
    subplot(1,2,1);
    imshow(areaopenReal);
    title('Real');
    subplot(1,2,2);
    areaopenFake = bwareaopen(BWImageCloseFake, 15);
    imshow(areaopenFake);
    title('Fake');
    
    %%//Count how many objects there are
    [~,countReal] = bwlabel(areaopenReal);
    [~,countFake] = bwlabel(areaopenFake);
    disp(['The total number of black lines for the real note is: ' num2str(countReal)]);
    disp(['The total number of black lines for the fake note is: ' num2str(countFake)]);
    

    编辑 - 2014 年 9 月 4 日

    您联系了我,想知道如何检测每个音符右侧的黑色大条。这实际上并没有那么糟糕。您发布的另一张假钞的图片与其他图片的尺寸不同。因此,我将调整此图像的大小,以使此图像与您显示的其他图像的大小大致相同。这是您在 cmets 中发布的图片:

    通过查看所有注释,它们会在第 195 列到第 215 列之间徘徊。这是假设每个图像有 320 列。现在,我检测钞票是否是假的背后的过程是通过查看黑条本身的整体强度。您会注意到假钞要么没有黑色条带,要么条带相当暗淡且褪色。我们当然可以利用这一点。以下是我为检测黑条所做的快速列表:

    • 读取图像并在必要时将它们调整为所有相同的列
    • 提取所有图像的第 195 到第 215 列
    • 使用rgb2gray将图像转换为灰度
    • 使用强度级别 30 对图像进行阈值处理。我试探性地使用了 30,因为这主要是我看到的黑条所包含的强度。然后反转图像,使黑色变为白色。我需要将黑色条带变成白色以便进一步分析。我使用im2bw 来执行此操作。
    • 我像以前一样打开图像区域,但指定了大约 100 的更大区域,以确保消除任何虚假的噪声和孤立像素。
    • 我使用 5 x 5 的方形结构元素进行闭合,以确保靠近较大区域的任何断开连接的区域相互连接。
    • 然后我计算剩余对象的总数。如果计数不等于 1,则为假钞。如果它只是 1,那么它就是一个真正的音符。

    这里是完整的代码:

    %% //Read in images
    clear all;
    close all;
    Ireal = imread('http://i.stack.imgur.com/SqbnIm.jpg'); % Real
    Ifake = imread('http://i.stack.imgur.com/2U3DEm.jpg'); % Fake
    Ifake2 = imread('http://i.imgur.com/SVJrwaV.jpg'); % Fake #2
    % //Resize so that we have the same dimensions as the other images
    Ifake2 = imresize(Ifake2, [160 320], 'bilinear');
    
    %% //Extract the black strips for each image
    blackStripReal = Ireal(:,195:215,:);
    blackStripFake = Ifake(:,195:215,:);
    blackStripFake2 = Ifake2(:,195:215,:);
    
    figure(1);
    subplot(1,3,1);
    imshow(blackStripReal);
    title('Real');
    subplot(1,3,2);
    imshow(blackStripFake);
    title('Fake');
    subplot(1,3,3);
    imshow(blackStripFake2);
    title('Fake #2');
    
    %% //Convert into grayscale then threshold
    blackStripReal = rgb2gray(blackStripReal);
    blackStripFake = rgb2gray(blackStripFake);
    blackStripFake2 = rgb2gray(blackStripFake2);
    
    figure(2);
    subplot(1,3,1);
    imshow(blackStripReal);
    title('Real');
    subplot(1,3,2);
    imshow(blackStripFake);
    title('Fake');
    subplot(1,3,3);
    imshow(blackStripFake2);
    title('Fake #2');
    
    %% //Threshold using about intensity 30
    blackStripRealBW = ~im2bw(blackStripReal, 30/255);
    blackStripFakeBW = ~im2bw(blackStripFake, 30/255);
    blackStripFake2BW = ~im2bw(blackStripFake2, 30/255);
    
    figure(3);
    subplot(1,3,1);
    imshow(blackStripRealBW);
    title('Real');
    subplot(1,3,2);
    imshow(blackStripFakeBW);
    title('Fake');
    subplot(1,3,3);
    imshow(blackStripFake2BW);
    title('Fake #2');
    
    %% //Area open the image
    figure(4);
    areaopenReal = bwareaopen(blackStripRealBW, 100);
    subplot(1,3,1);
    imshow(areaopenReal);
    title('Real');
    subplot(1,3,2);
    areaopenFake = bwareaopen(blackStripFakeBW, 100);
    imshow(areaopenFake);
    title('Fake');
    subplot(1,3,3);
    areaopenFake2 = bwareaopen(blackStripFake2BW, 100);
    imshow(areaopenFake2);
    title('Fake #2');
    
    %% //Post-process
    se = strel('square', 5);
    BWImageCloseReal = imclose(areaopenReal, se);
    BWImageCloseFake = imclose(areaopenFake, se);
    BWImageCloseFake2 = imclose(areaopenFake2, se);
    figure(5);
    subplot(1,3,1);
    imshow(BWImageCloseReal);
    title('Real');
    subplot(1,3,2);
    imshow(BWImageCloseFake);
    title('Fake');
    subplot(1,3,3);
    imshow(BWImageCloseFake2);
    title('Fake #2');
    
    %% //Count the total number of objects in this strip
    [~,countReal] = bwlabel(BWImageCloseReal);
    [~,countFake] = bwlabel(BWImageCloseFake);
    [~,countFake2] = bwlabel(BWImageCloseFake2);
    disp(['The total number of black lines for the real note is: ' num2str(countReal)]);
    disp(['The total number of black lines for the fake note is: ' num2str(countFake)]);
    disp(['The total number of black lines for the second fake note is: ' num2str(countFake2)]);
    

    请记住,您必须使用这些参数来满足您的目的。以下是每个步骤中的数字:

    ...最后是对象计数:

    The total number of black lines for the real note is: 1
    The total number of black lines for the fake note is: 2
    The total number of black lines for the second fake note is: 0
    

    祝你好运!

    【讨论】:

    • 完美解决了我的问题。如果我想识别钞票右侧的较宽条带怎么样,因为有些假钞有细条带而没有较宽条带。这是一个示例:imgur.com/SVJrwaV
    • @user3472037 - 对于所有纸币,较宽的条带通常位于同一区域吗?如果是,我可以建议使用我之前提到的饱和度和值的另一种方法,但我们会寻找单条/线的存在。如果位置或多或少固定,这将使工作更容易。告诉我!
    • @raryryeng 是的,与薄条不同,所有钞票的较宽区域都在同一区域。
    • @user3472037 - 我稍后会为您提供答案。我要去睡觉了。快说吧!
    • @raryryeng- 好的,我会等着。
    【解决方案2】:

    这是我尝试过的。在这种方法中,您需要在线段周围设置一些保护带,而不是将区域限制为线段的宽度,这一点很重要。

    • 加载 rgb 图像
    • 关闭 rgb 图像,以便移除或淡化较暗的线段,然后将此关闭的图像转换为灰度(我们将其称为 im1
    • 将 rgb 图像转换为灰度,然后增强对比度(我们称之为 im2
    • 取差值:im1 - im2
    • 对该差异图像进行投影。这应该会给你一个一维信号
    • 您还可以平滑此信号
    • 阈值并找到段数
    • 你应该为虚线图像获得更多的片段

    以下是给定示例图像的结果:

    输入图像:

    不同的图像:

    预测和细分:

    这是 Matlab 代码:

    clear all;
    close all;
    
    imt = imread('t.jpg');
    imf = imread('f.jpg');
    % convert to gray scale
    grt = rgb2gray(imt);
    grf = rgb2gray(imf);
    % contrast enhance the gray image to emphasize dark lines in lighter background
    grt = imadjust(grt);
    grf = imadjust(grf);
    % close rgb. choose a larger k. idea is to remove the dark line
    k = 7;
    se = ones(k);
    imtcl = imclose(imt, se);
    imfcl = imclose(imf, se);
    % convert closed image to gray scale
    grtcl = rgb2gray(imtcl);
    grfcl = rgb2gray(imfcl);
    % take the difference (closed-gray-scale - contrast-enhanced-gray-scale)
    difft = grtcl - grt;
    difff = grfcl - grf;
    % take the projection of the difference
    pt = sum(difft');
    pf = sum(difff');
    % smooth the projection
    ptfilt = conv(pt, ones(1, k)/k, 'same');
    pffilt = conv(pf, ones(1, k)/k, 'same');
    % threshold (multiplication by max element is just for visualization)
    tht = (pt > graythresh(pt))*max(pt);
    thf = (pf > graythresh(pf))*max(pf);
    % get the number of segments. we should get more segments for the broken line (nt < nf)
    [lblt, nt] = bwlabel(tht);
    [lblf, nf] = bwlabel(thf);
    
    figure,
    subplot(2, 1, 1), imshow(difft'), title('difference image for solid line')
    subplot(2, 1, 2), imshow(difff'), title('difference image for broken line')
    
    figure,
    subplot(2, 1, 1), plot(1:length(pt), pt, 1:length(pt), ptfilt, 1:length(pt), tht),
    title('solid line image'),
    legend('projection', 'smoothed', 'thresholded', 'location', 'eastoutside')
    subplot(2, 1, 2), plot(1:length(pf), pf, 1:length(pf), pffilt, 1:length(pf), thf),
    title('broken line image'),
    legend('projection', 'smoothed', 'thresholded', 'location', 'eastoutside')
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2022-11-19
      • 2012-12-06
      • 2012-05-23
      • 2019-06-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多