counting the number of objects on image with MatLab - matlab

I need to count the number of chalks on image with MatLab. I tried to convert my image to grayscale image and than allocate borders. Also I tried to convert my image to binary image and do different morphological operations with it, but I didn't get desired result. May be I did something wrong. Please help me!
My image:

You can use the fact that chalk is colorful and the separators are gray. Use rgb2hsv to convert the image to HSV color space, and take the saturation component. Threshold that, and then try using morphology to separate the chalk pieces.

This is also not a full solution, but hopefully it can provide a starting point for you or someone else.
Like Dima I noticed the chalk is brightly colored while the dividers are almost gray. I thought you could try and isolate gray pixels (where a gray pixel says red=blue=green) and go from there. I tried applying filters and doing morphological operations but couldn't find something satisfactory. still, I hope this helps
mim = imread('http://i.stack.imgur.com/RWBDS.jpg');
%we average all 3 color channels (note this isn't exactly equivalent to
%rgb2gray)
grayscale = uint8(mean(mim,3));
%now we say if all channels (r,g,b) are within some threshold of one another
%(there's probabaly a better way to do this)
my_gray_thresh=25;
graymask = (abs(mim(:,:,1) - grayscale) < my_gray_thresh)...
& (abs(mim(:,:,2) - grayscale) < my_gray_thresh)...
& (abs(mim(:,:,3) - grayscale) < my_gray_thresh);
figure(1)
imshow(graymask);

Ok so I spent a little time working on this- but unfortunately I'm out of time today and I apologize for the incomplete answer, but maybe this will get you started- (if you need more help, I'll edit this post over the weekend to give you a more complete answer :))
Here's the code-
for i=1:3
I = RWBDS(:,:,i);
se = strel('rectangle', [265,50]);
Io = imopen(I, se);
Ie = imerode(I, se);
Iobr = imreconstruct(Ie, I);
Iobrd = imdilate(Iobr, se);
Iobrcbr = imreconstruct(imcomplement(Iobrd), imcomplement(Iobr));
Iobrcbr = imcomplement(Iobrcbr);
Iobrcbrm = imregionalmax(Iobrcbr);
se2 = strel('rectangle', [150,50]);
Io2 = imerode(Iobrcbrm, se2);
Ie2 = imdilate(Io2, se2);
fgm{i} = Ie2;
end
fgm_final = fgm{1}+fgm{2}+fgm{3};
figure, imagesc(fgm_final);
It does still pick up the edges on the side of the image, but from here you're going to use connected bwconnectedcomponents, and you'll get the lengths of the major and minor axes, and by looking at the ratios of the objects it will get rid those.
Anyways good luck!
EDIT:
I played with the code a tiny bit more, and updated the code above with the new results. In cases when I was able to get rid of the side "noise" it also got rid of the side chalks. I figured I'd just leave both in.
What I did: In most cases a conversion to HSV color space is the way to go, but as shown by #rayryeng this is not the way to go here. Hue works really well when there is one type of color- if for example all chalks were red. (Logically you would think that going with the color channel would be better though, but this is no the case.) In this case, however, the only thing all chalks have in common is the relative shape. My solution basically used this concept by setting the structuring element se to something of the basic shape and ratio of the chalk and performing morphological operations- as you originally guessed was the way to go.
For more details, I suggest you read matlab's documentation on these specific functions.
And I'll let you figure out how to get the last chalk based on what I've given you :)

Related

Filling the gaps in a binary image

I have found a couple areas referring to filling of gaps in binary images in matlab, however I am still struggling. I have written the following code but I cannot get it to work. Here is my binary image:
.
However, what I'm trying to achieve is the following
.
Does anyone know how to do this? I have been trying using imfill but I know I think I need to define boundaries also with the bwlabel function but I dont know how. Any help would be greatly appreciated.
%%Blade_Image_Processing
clc;
clear;
%%Video file information
obj = VideoReader('T9_720p;60p_60mm_f4.MOV');
% Sampling rate - Frames per second
fps = get(obj, 'FrameRate');
dt = 1/fps;
% ----- find image info -----
file_info = get(obj);
image_width = file_info.Width;
image_height = file_info.Height;
% Desired image size
x_range = 1:image_height;
y_range = 1:image_width;
szx = length(x_range);
szy = length(y_range);
%%Get grayscale image
grayscaleimg1 = rgb2gray(read(obj,36));
grayscaleimg = imadjust(grayscaleimg1);
diff_im = medfilt2(grayscaleimg, [3 3]);
t1=60;
t2=170;
range=(diff_im > t1 & diff_im <= t2);
diff_im (range)=255;
diff_im (~range)=0;
% Remove all those pixels less than 300px
diff_im = bwareaopen(diff_im,2000);
%imshow(diff_im)
%imhist(grayscaleimg)
%Fill gaps in binary image
BW2 = imfill(diff_im,'holes');
There are two main problems: desired object has no readily usable distinguishing features, and it touches other object. Second problem could be perhaps cleared with morphological opening/closing (touching object is thin, desired object not, is this always the case?), but first problem remains. If your object touched edge but others didn't or vice versa, you could do something with imfill and subtractions. As it is now, MAYBE something like this would work:
With opening/closing remove connection, so your object is disjoint.
With imfill, remove what is left of this thin horizontal thing.
Then, you can bwlabel and remove everything that touches sides or bottom of the image - in shown case that would leave only your object.
Exact solution depends heavily on what additional constrains are there for your pictures. I believe it is not a one-shot, rather you have more of those pictures and want to correctly find objects on all? You have to check what holds for all pictures, such as if object always touches only something thin or if it always touches only upper edge etc.

Detecting text in image using MSER

I'm trying to follow this tutorial http://www.mathworks.com/help/vision/examples/automatically-detect-and-recognize-text-in-natural-images.html to detect text in image using Matlab.
As a first step, the tutorial uses detectMSERFeatures to detect textual regions in the image. However, when I use this step on my image, the textual regions aren't detected.
Here is the snippet I'm using:
colorImage = imread('demo.png');
I = rgb2gray(colorImage);
% Detect MSER regions.
[mserRegions] = detectMSERFeatures(I, ...
'RegionAreaRange',[200 8000],'ThresholdDelta',4);
figure
imshow(I)
hold on
plot(mserRegions, 'showPixelList', true,'showEllipses',false)
title('MSER regions')
hold off
And here is the original image
and here is the image after the first step
[![enter image description here][2]][2]
Update
I've played around with parameters but none seem to detect textual region perfectly. Is there a better way to accomplish this than tweaking numbers? Tweaking the parameters won't work for wide array of images I might have.
Some parameters I've tried and their results:
[mserRegions] = detectMSERFeatures(I, ...
'RegionAreaRange',[30 100],'ThresholdDelta',12);
[mserRegions] = detectMSERFeatures(I, ...
'RegionAreaRange',[30 600],'ThresholdDelta',12);
Disclaimer: completely untested.
Try reducing MaxAreaVariation, since your text & background have very little variation (reduce false positives). You should be able to crank this down pretty far since it looks like the text was digitally generated (wouldn't work as well if it was a picture of text).
Try reducing the minimum value for RegionAreaRange, since small characters may be smaller than 200 pixels (increase true positives). At 200, you're probably filtering out most of your text.
Try increasing ThresholdDelta, since you know there is stark contrast between the text and background (reduce false positives). This won't be quite as effective as MaxAreaVariation for filtering, but should help a bit.

How to fix the edges in the image

I have got a result as shown in the following image. As you can see, there are some edges which are not all straight. I want this image to be similar to this one (I'm not sure why the grey shade appears. Maybe because I manually extracted it?). But, the main thing here is to be similar to the white edges. I tried using morphological operations, but with not much improvements.
Any ideas how to fix this issue?
Thanks.
I loaded your data into a variable called "toBeSolved."
rawData1 = importdata('to be solved.JPG');
[~,name] = fileparts('to be solved.JPG');
newData1.(genvarname(name)) = rawData1;
% Create new variables in the base workspace from those fields.
vars = fieldnames(newData1);
for i = 1:length(vars)
assignin('base', vars{i}, newData1.(vars{i}));
end
Now this is an indexed image so there are 3 frames, as can be seen from:
>> size(toBeSolved)
ans =
452 440 3
The data content of each frame appears to be identical, so maybe all you care about is the grayscale information from 1-frame? If thats the case lets just take the first frame:
data1 = im2double(toBeSolved(:,:,1));
And then normalize the data to the max value in the image:
data1 = data1 / max(data1(:));
Now take a look at a mesh view and we see that, as expected, there is significant noise and corruption around the edges:
The appearance about the edges suggests trying a thresholding operation to the data. I experimented with the threshold value and found that 0.13 produces some improvement:
data2 = double(data1 > 0.13);
which gives:
or the grayscale, imshow(data2):
I don't know if this is acceptable to your application, the edges are not perfect, but it does seem improved over what you started with.
By the way, I checked out your "solved" data as well and that appears to also have the same underlying level of noise and edge defects as the "toBeSolved" file, but at least visually, the corruption in that image is harder to see duo to the gray-scale values around the edges.

How can I replace the border with white color in Matlab?

This is the original image.
I changed it into 1) grayscale and apply 2) threshold.
As seen in the original image, there are some shadow that still exist after apply two method above.
But most of the image are perfect after 2 method.
I need to extract the text, so I need to get rid of the noise. I almost finish the work but the problem is some cases have a black border and I wanted to replace that into white color.
And I insist that I want just only border to turn into white
I think of create some white rectangle and fill the border with those rectangle but I don't know how to do it.
How can I achieve that using Matlab?
Any other method would be appreciate too!
If you can be sure only the borders will be black, why not simply crop the image until all isolated shapes are recognizable characters? Something along the lines of
done = false;
ii = 1;
while (~done)
% fill the outer border
img(:,ii) = 255; img(ii,:) = 255;
img(:,end-ii+1) = 255; img(end-ii+1,:) = 255;
% (run your algorithms here. It positive match, done = true)
end
That could be computationally intensive, since you have to do pattern recognition on each iteration, but you indicated it only occurs in "some cases".
Otherwise, I suspect some morphological operation can also be used, probably erosion or thinning or similar. But that has the drawback of altering the characters you want to match. But, if all images you have to process look like the one you show, I hardly suspect that'll give you any problems.
Some ways to detecting straight lines are mentioned in this question. I'd say you could detect all lines, and remove those (with a small tolerance around it) that are perfectly horizontal/vertical and on one of the edges.

artifacts in processed images

This question is related to my previous post Image Processing Algorithm in Matlab in stackoverflow, which I already got the results that I wanted to.
But now I am facing another problem, and getting some artefacts in the process images. In my original images (stack of 600 images) I can't see any artefacts, please see the original image from finger nail:
But in my 10 processed results I can see these lines:
I really don't know where they come from?
Also if they belong to the camera's sensor why can't I see them in my original images? Any idea?
Edit:
I have added the following code suggested by #Jonas. It reduces the artefact, but does not completely remove them.
%averaging of images
im = D{1}(:,:);
for i = 2:100
im = imadd(im,D{i}(:,:));
end
im = im/100;
imshow(im,[]);
for i=1:100
SD{i}(:,:)=imsubtract(D{i}(:,:),im(:,:))
end
#belisarius has asked for more images, so I am going to upload 4 images from my finger with speckle pattern and 4 images from black background size( 1280x1024 ):
And here is the black background:
Your artifacts are in fact present in your original image, although not visible.
Code in Mathematica:
i = Import#"http://i.stack.imgur.com/5hM3u.png"
EntropyFilter[i, 1]
The lines are faint, but you can see them by binarization with a very low level threshold:
Binarize[i, .001]
As for what is causing them, I can only speculate. I would start tracing from the camera output itself. Also, you may post two or three images "as they come straight from the camera" to allow us some experimenting.
The camera you're using is most likely has a CMOS chip. Since they have independent column (and possibly row) amplifiers, which may have slightly different electronic properties, you can get the signal from one column more amplified than from another.
Depending on the camera, these variability in column intensity can be stable. In that case, you're in luck: Take ~100 dark images (tape something over the lens), average them, and then subtract them from each image before running the analysis. This should make the lines disappear. If the lines do not disappear (or if there are additional lines), use the post-processing scheme proposed by Amro to remove the lines after binarization.
EDIT
Here's how you'd do the background subtraction, assuming that you have taken 100 dark images and stored them in a cell array D with 100 elements:
% take the mean; convert to double for safety reasons
meanImg = mean( double( cat(3,D{:}) ), 3);
% then you cans subtract the mean from the original (non-dark-frame) image
correctedImage = rawImage - meanImg; %(maybe you need to re-cast the meanImg first)
Here is an answer that in opinion will remove the lines more gently than the above mentioned methods:
im = imread('image.png'); % Original image
imFiltered = im; % The filtered image will end up here
imChanged = false(size(im));% To document the filter performance
% 1)
% Compute the histgrams for each column in the lower part of the image
% (where the columns are most clear) and compute the mean and std each
% bin in the histogram.
histograms = hist(double(im(501:520,:)),0:255);
colMean = mean(histograms,2);
colStd = std(histograms,0,2);
% 2)
% Now loop though each gray level above zero and...
for grayLevel = 1:255
% Find the columns where the number of 'graylevel' pixels is larger than
% mean_n_graylevel + 3*std_n_graylevel). - That is columns that contains
% statistically 'many' pixel with the current 'graylevel'.
lineColumns = find(histograms(grayLevel+1,:)>colMean(grayLevel+1)+3*colStd(grayLevel+1));
% Now remove all graylevel pixels in lineColumns in the original image
if(~isempty(lineColumns))
for col = lineColumns
imFiltered(:,col) = im(:,col).*uint8(~(im(:,col)==grayLevel));
imChanged(:,col) = im(:,col)==grayLevel;
end
end
end
imshow(imChanged)
figure,imshow(imFiltered)
Here is the image after filtering
And this shows the pixels affected by the filter
You could use some sort of morphological opening to remove the thin vertical lines:
img = imread('image.png');
SE = strel('line',2,0);
img2 = imdilate(imerode(img,SE),SE);
subplot(121), imshow(img)
subplot(122), imshow(img2)
The structuring element used was:
>> SE.getnhood
ans =
1 1 1
Without really digging into your image processing, I can think of two reasons for this to happen:
The processing introduced these artifacts. This is unlikely, but it's an option. Check your algorithm and your code.
This is a side-effect because your processing reduced the dynamic range of the picture, just like quantization. So in fact, these artifacts may have already been in the picture itself prior to the processing, but they couldn't be noticed because their level was very close to the background level.
As for the source of these artifacts, it might even be the camera itself.
This is a VERY interesting question. I used to deal with this type of problem with live IR imagers (video systems). We actually had algorithms built into the cameras to deal with this problem prior to the user ever seeing or getting their hands on the image. Couple questions:
1) are you dealing with RAW images or are you dealing with already pre-processed grayscale (or RGB) images?
2) what is your ultimate goal with these images. Is the goal to simply get rid of the lines regardless of the quality in the rest of the image that results, or is the point to preserve the absolute best image quality. Are you to perform other processing afterwards?
I agree that those lines are most likely in ALL of your images. There are 2 reasons for those lines ever showing up in an image, one would be in a bright scene where OP AMPs for columns get saturated, thus causing whole columns of your image to get the brightest value camera can output. Another reason could be bad OP AMPs or ADCs (Analog to Digital Converters) themselves (Most likely not an ADC as normally there is essentially 1 ADC for th whole sensor, which would make the whole image bad, not your case). The saturation case is actually much more difficult to deal with (and I don't think this is your problem). Note: Too much saturation on a sensor can cause bad pixels and columns to arise in your sensor (which is why they say never to point your camera at the sun). The bad column problem can be dealt with. Above in another answer, someone had you averaging images. While this may be good to find out where the bad columns (or bad single pixels, or the noise matrix of your sensor) are (and you would have to average pointing the camera at black, white, essentially solid colors), it isn't the correct answer to get rid of them. By the way, what I am explaining with the black and white and averaging, and finding bad pixels, etc... is called calibrating your sensor.
OK, so saying you are able to get this calibration data, then you WILL be able to find out which columns are bad, even single pixels.
If you have this data, one way that you could erase the columns out is to:
for each bad column
for each pixel (x, y) on the bad column
pixel(x, y) = Average(pixel(x+1,y),pixel(x+1,y-1),pixel(x+1,y+1),
pixel(x-1,y),pixel(x-1,y-1),pixel(x-1,y+1))
What this essentially does is replace the bad pixel with a new pixel which is the average of the 6 remaining good pixels around it. The above is an over-simplified version of an algorithm. There are certainly cases where a singly bad pixel could be right next the bad column and shouldn't be used for averaging, or two or three bad columns right next to each other. One could imagine that you would calculate the values for a bad column, then consider that column good in order to move on to the next bad column, etc....
Now, the reason I asked about the RAW versus B/W or RGB. If you were processing a RAW, depending on the build of the sensor itself, it could be that only one sub-pixel (if you will) of the bayer filtered image sensor has the bad OP AMP. If you could detect this, then you wouldn't necessarily have to throw out the other good sub-pixel's data. Secondarily, if you are using an RGB sensor, to take a grayscale photo, and you shot it in RAW, then you may be able to calculate your own grayscale pixels. Many sensors when giving back a grayscale image when using an RGB sensor, will simply pass back the Green pixel as the overall pixel. This is due to the fact that it really serves as the luminescence of an image. This is why most image sensors implement 2 green sub-pixels for every r or g sub-pixel. If this is what they are doing (not ALL sensors do this) then you may have better luck getting rid of just the bad channel column, and performing your own grayscale conversion using.
gray = (0.299*r + 0.587*g + 0.114*b)
Apologies for the long winded answer, but I hope this is still informational to someone :-)
Since you can not see the lines in the original image, they are either there with low intensity difference in comparison with original range of image, or added by your processing algorithm.
The shape of the disturbance hints to the first option... (Unless you have an algorithm that processes each row separately.)
It seems like your sensor's columns are not uniform, try taking a picture without the finger (background only) using the same exposure (and other) settings, then subtracting it from the photo of the finger (prior to other processing). (Make sure the background is uniform before taking both images.)