Here is my rather botched attempt at determining whether your bank note is fake or real. One thing I have noticed between the notes is that the real note has its thin strip to be more or less continuous while the fake strip has fragmented thin lines in the strip. One could say that the fake note has more than one line in the thin strip while the real note only has one line. Let's try and get our image so that we detect the strip itself (as you have also have tried) and we count how many lines we see. If we see just one line, it's real but if we see more than one line it's fake. I'll walk you through how I did it with intermediate images in between.
Step #1 - Read in the image (of course)
I'm going to directly read your images in from StackOverflow. imread
is great for reading images from online URLs:
%%//Read in image
clear all;
close all;
Ireal = imread('https://i.sstatic.net/SqbnIm.jpg'); %//Real
Ifake = imread('https://i.sstatic.net/2U3DEm.jpg'); %//Fake
Step #2 - Decompose image into HSV and analyse
One thing I have noticed is that the strips are very dark while the bank note is predominantly green in colour. I used some basic colour image processing as a pre-processing step. I transformed the image into HSV (Hue-Saturation-Value) and took a look 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 are what the images look like:
In this code, I'm displaying each of the components side by side each other for the hue, saturation and value respectively. You'll notice something very peculiar. The black thin strip has a saturation that is quite high, which makes sense as black could be considered as a "colour" that is pure saturation (devoid of white light). The value component has its strip with values to be very low, which also makes sense as value captures the lightness / intensity of the colour.
Step #3 - Threshold the saturation and value planes to create a binary image
With the observations I made above, I'm going to threshold the image looking at the saturation and value planes. Any points that have a combined saturation that is rather high and values that are rather low are candidates that are part of the black strip. I am going to crop out just the strips by themselves to make things easier (as you have already did too). Take note that the position of the strips in each image differ, so I had to adjust accordingly. I simply extracted out the right columns, while leaving the rows and slices the same. These saturation and value thresholds are rather ad-hoc, so I had to play around with these 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');
These are what the images look like:
You can see that the real strip more or less has more connectivity than the fake strip. Let's do a bit more processing to clean this up
Step #4 - Do some minor closings
If you take a look at the thin black strip of the fake note, you'll see that each black line is separated by quite a few pixels while the real note really has no separation. However, you'll see that in the real strip above, there are still some parts of the line that are disconnected. As such, let's try and connect the line together. This is safe because if we were to do this on the fake image, the parts of the strip are so far apart that closing shouldn't make a difference, but it'll help in our real image analysis. As such, I closed these images by a 6 pixel line that is vertical. Here's the code to do that:
%%//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');
These are what the images look like:
Step #5 - Final cleanup
You'll notice that for each of the images, there are some noisy pixels on the edges. As such, let's use an area opening through bwareaopen
. This function removes pixel areas in a black and white image that have less than a certain area. I'm going to choose 15 to get rid of the pixels along the edges that don't belong to the strip. As such:
%%//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:
Step #6 - Count the number of black lines
The last step is to simply count the number of black lines in each image. If there is just 1, this denotes that the bank note is real, while if there is more than 1, this denotes that the bank note is fake. We can use bwlabel
and use the second parameter to count how many objects there are. 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)]);
We get the following output in 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
As you can see, the real note has just one line while the fake note has more than one. You'll have to play with this code depending on what bank note you have to get this to work, but this is somewhere to start.
Full code
Just for completeness, here is the full code so you can copy and paste and run in MATLAB yourself.
%%//Read in image
clear all;
close all;
Ireal = imread('https://i.sstatic.net/SqbnIm.jpg'); % Real
Ifake = imread('https://i.sstatic.net/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 4th, 2014
You contacted me and wanted to know how to detect the large black strip that is to the right of each note. This is actually not so bad to do. The image that you posted that is the other fake note is of a different size than the others. As such, I'm going to resize this image so that this image is roughly the same size as the others you have shown. This is the image you have posted in the comments:
By looking at all of the notes, they hover between the 195th column to the 215th column. This is assuming that each image has 320 columns. Now, the process behind how I detect whether the bank note is fake is by looking at the overall intensity of the black strip itself. You'll notice that the fake notes either do not have a black strip, or the strip is rather dull and faded. We can certainly use this to our advantage. Here is a quick list of what I did to detect the black strip:
- Read in the images and resize them to all the same columns when necessary
- Extract the 195th to 215th columns for all of the images
- Convert the images to grayscale using
rgb2gray
- Threshold the images using about intensity level 30. I used 30 heuristically as this was predominantly the intensity that I saw that the black strip consisted of. You then invert the images so that black becomes white. I need the black strip to become white for further analysis. I use
im2bw
to do this.
- I area open the image like I did before, but specifying a larger area of about 100 to ensure I get rid of any spurious noisy and isolated pixels.
- I do a closing using a square structuring element of 5 x 5 to ensure that any disconnected regions that are near larger regions get connected to each other.
- I then count the total number of objects that are left. If the count is not equal to 1, then it's fake note. If it's just 1, then it's a real note.
Here is the full code:
%% //Read in images
clear all;
close all;
Ireal = imread('https://i.sstatic.net/SqbnIm.jpg'); % Real
Ifake = imread('https://i.sstatic.net/2U3DEm.jpg'); % Fake
Ifake2 = imread('https://i.sstatic.net/NpJ4q.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)]);
Bear in mind that you'll have to play around with these parameters to suit your purposes. Here are what the figures look like at each step:
... and finally the object count:
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!