Paper currency recognition through image processing

In paper currency, I want to check that the strip is broken or a solid line. For this, I took a picture with strong light. And I had the following two pictures: one is real currency and the other is fake. I cropped the image where the strip exists, and restored the opening, and finally counted the black pixels. But the result is not what I wanted. Any help?

Here are the images:

enter image description hereenter image description here

%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(:)); 
+5
source share
2 answers

Here is my rather unsuccessful attempt to determine if your bank note is false or real. One thing that I noticed between the notes is that the real note has its thin strip more or less continuous, while the fake strip has fragmented thin lines in the strip. We can say that a fake note has more than one line in a thin strip, and in a real note there is only one line . Let's try and get our image so that we ourselves find the strip (as you already tried), and we count how many lines we see. If we see only one line, it is real, but if we see more than one line, it is a fake. I will guide you through how I did this with intermediate images between them.

Step # 1 - Read the picture (of course)

I will directly read your images from StackOverflow. imread great for reading images from online addresses:

 %%//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 

Step # 2 - Lay out the image in HSV and analyze

One thing that I noticed is that the stripes are very dark and the banknote is mostly green. I used basic color image processing as a preprocessing step. I converted the image to HSV ( Hue-Saturation-Value ) and looked at each component separately:

 %% 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'); 

Here's what the images look like:

enter image description here

enter image description here

In this code, I show each of the components side by side with each other for hue, saturation and value, respectively. You will notice something very peculiar. A thin black strip has a saturation that is quite high, which makes sense, since black can be considered as a β€œcolor”, which is pure saturation (devoid of white light). The value component has its own band with very low values, which also makes sense, since the value reflects the brightness / intensity of the color.

Step # 3 - Threshold of saturation and values ​​for creating a binary image

With the observations I made above, I am going to set the threshold for an image looking at the saturation and value planes. Any points with a total saturation that are quite high and values ​​that are quite low are candidates that are part of the black bar. I am going to cut out only the stripes myself, to ease the situation (as you already did). Please note that the position of the stripes in each image is different, so I had to adjust accordingly. I simply extracted the correct columns, leaving the rows and fragments the same. These saturation thresholds and values ​​are pretty ad-hoc, so I had to play with them to get good results.

 %%//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'); 

Here's what the images look like:

enter image description here

You can see that a real band more or less has more connectivity than a fake band. Let do a little more processing to clear it.

Step # 4 - do some minor closures

If you look at the thin black strip of a fake note, you will see that each black line is separated by quite a few pixels, while the real note really has no separation. However, you will see that in the real strip above there are still some parts of the line that are disabled. So, try connecting the line. This is safe because if we do this on a fake image, the parts of the strip are so far apart that the closure should not matter, but it will help in our real image analysis. Thus, I closed these images on a 6-pixel line, which is vertical. Here is the code for this:

 %%//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'); 

Here's what the images look like:

enter image description here

Step 5 - Final Cleaning

You will notice that for each of the images on the edges there are several noisy pixels. So let me use the area opening through bwareaopen . This function removes areas of pixels in a black and white image that have less than a certain area. I am going to select 15 to get rid of pixels at the edges that do not belong to the strip. Thus:

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

Here's what the images look like:

enter image description here

Step # 6 - Count the Number of Black Lines

The final step is to simply count the number of black lines in each image. If there is only 1, it means that the banknote is real, and if it is more than 1, it means that the banknote is fake. We can use bwlabel and use the second parameter to count the number of objects. In other words:

 %%//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)]); 

In MATLAB we get the following result:

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

As you can see, a real note has only one line, while a fake note has more than one. You will have to play with this code, depending on which bank you want it to work, but start somewhere.


Full code

Just for completeness, here is the complete code so you can copy and paste and run in MATLAB yourself.

 %%//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)]); 

Edit - September 4, 2014

You contacted me and wanted to know how to find a large black bar, which is located to the right of each note. This is actually not so bad. The image you posted is a different fake recording of a different size than the others. So I'm going to resize this image so that this image is about the same size as the others you showed. This is the image you posted in the comments:

Having looked at all the notes, they hover between the 195th column and the 215th column. It is estimated that each image has 320 columns. Now the process related to the way I discover if a banknote is fake is to look at the overall intensity of the black bar itself. You will notice that the fake notes either do not have a black strip, or the strip is rather dull and pale. We can, of course, use this to our advantage. Here is a short list of what I did to detect the black strip:

  • Read the images and resize them to the same columns if necessary
  • Extract 195th-215th columns for all images
  • Convert grayscale images with rgb2gray
  • Threshold of images using an intensity level of 30. I used 30 heuristically, since this was predominantly the intensity that I saw of which the black bar consisted. Then you invert the images so that black turns white. I need a black bar to become white for further analysis. I am using im2bw for this.
  • I open the image as before, but indicate a large area of ​​about 100, so that I get rid of any false noisy and isolated pixels.
  • I am doing a closure using a 5 x 5 square structuring element to ensure that any detached areas that are near larger areas connect to each other.
  • Then I count the total number of objects remaining. If the number is not 1 , then this is a fake note. If this is only 1, then this is a real note.

Here is the complete code:

 %% //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)]); 

Remember that you will have to play with these options according to your needs. Here's what the numbers look like at every step:

enter image description here

enter image description here

enter image description here

enter image description here

enter image description here

... and finally, the number of objects:

 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 

Good luck

+16
source

Here is what I have tried. It is important that you allow some protective strip around the line segment in this approach, and not limit the area to the width of the line segment.

  • upload rgb image
  • close the rgb image so that the dark line segments are removed or highlighted, then convert this closed image to a gray scale (we will call it im1 )
  • convert the rgb image to gray, then increase the contrast (we'll call it im2 )
  • take the difference: im1 - im2
  • take the projection of this difference image. That should give you a 1-D signal
  • you can further smooth this signal
  • threshold value and find the number of segments
  • you should get more segments for a polyline image

Below are the results for the given image samples:

Input Images:

enter image description hereenter image description here

Difference Images:

enter image description hereenter image description here

Projections and segments:

enter image description here

And here is the Matlab code:

 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') 
+1
source

Source: https://habr.com/ru/post/1201464/


All Articles