How do I detect an instance of an object in an image? - matlab

I have an image containing several specific objects. I would like to detect the positions of those objects in this image. To do that I have some model images containing the objects I would like to detect. These images are well cropped around the object instance I want to detect.
Here is an example:
In this big image,
I would like to detect the object represented in this model image:

Since you originally posted this as a 'gimme-da-codez' question, showing absolutely no effort, I'm not going to give you the code. I will describe the approach in general terms, with hints along the way and it's up to you to figure out the exact code to do it.
Firstly, if you have a template, a larger image, and you want to find instances of that template in the image, always think of cross-correlation. The theory is the same whether you're processing 1D signals (called a matched filter in signal processing) or 2D images.
Cross-correlating an image with a known template gives you a peak wherever the template is an exact match. Look up the function normxcorr2 and understand the example in the documentation.
Once you find the peak, you'll have to account for the offset from the actual location in the original image. The offset is related to the fact that cross-correlating an N point signal with an M point signal results in an N + M -1 point output. This should be clear once you read up on cross-correlation, but you should also look at the example in the doc I mentioned above to get an idea.
Once you do these two, then the rest is trivial and just involves cosmetic dressing up of your result. Here's my result after detecting the object following the above.
Here's a few code hints to get you going. Fill in the rest wherever I have ...
%#read & convert the image
imgCol = imread('http://i.stack.imgur.com/tbnV9.jpg');
imgGray = rgb2gray(img);
obj = rgb2gray(imread('http://i.stack.imgur.com/GkYii.jpg'));
%# cross-correlate and find the offset
corr = normxcorr2(...);
[~,indx] = max(abs(corr(:))); %# Modify for multiple instances (generalize)
[yPeak, xPeak] = ind2sub(...);
corrOffset = [yPeak - ..., xPeak - ...];
%# create a mask
mask = zeros(size(...));
mask(...) = 1;
mask = imdilate(mask,ones(size(...)));
%# plot the above result
h1 = imshow(imgGray);
set(h1,'AlphaData',0.4)
hold on
h2 = imshow(imgCol);
set(h2,'AlphaData',mask)

Here is the answer that I was about to post when the question was closed. I guess it's similar to yoda's answer.
You can try to use normalized cross corelation:
im=rgb2gray(imread('di-5Y01.jpg'));
imObj=rgb2gray(imread('di-FNMJ.jpg'));
score = normxcorr2(imObj,im);
imagesc(score)
The result is: (As you can see, the whitest point corresponds to the position of your object.)

The Mathworks has a classic demo of image registration using the same technique as in #yoda's answer:
Registering an Image Using Normalized Cross-Correlation

Related

How to get region with lowest y value in the centroid

I have a binary image that has two connected components. Both are fairly horizontal and one is on the top of the image and the other at the bottom. What I need to do is to extract only the top component which I want to do (or at least what I think is a good method) by taking the component with the lowest y value for the centroid (because MATLAB uses Java to show images, so the origin is at the top left) and erasing the other component. So far I've been able to use regionprops to find which region has the lowest y value for the centroid, but from there I'm not sure how to get a binary image back again with the component I want.
I've read in the documentation that bwconncomp, labelmatrix, and ismember are useful, but I'm not very sure how to use them well (or at all very much).
This is the solution I just made up, but if there's a better or more elegant one I'd love to know about it!
P.S. filtered is my image
connComp = bwconncomp(filtered);
props = regionprops(filtered, 'Centroid');
justTop = zeros(size(filtered,1), size(filtered,2));
if props(1).Centroid(2) > props(2).Centroid(2)
justTop(connComp.PixelIdxList{2}) = 1;
else
justTop(connComp.PixelIdxList{1}) = 1;
end`

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.

DCT filter image in Matlab

I used the below function to filter an image. Basically it sets coefficients of DCT to 0 except for top-left 8x8 elements, which means it filter out all high frequency part and only left the low frequency part.
function I_out = em_DCT_filter(I_in,N)
I_trim = double(I_in)-128;
MYDCT=dctmtx(N);
dct = #(block_struct)MYDCT*block_struct.data*MYDCT';
B=blockproc(I_trim,[N,N],dct);
mask = zeros(N,N);
mask(1:N/4,1:N/4)= 1;
AnselmMask = #(block_struct)block_struct.data.*mask;
BMask=blockproc(B,[N N],AnselmMask);
InverseDct = #(block_struct)MYDCT'*block_struct.data*MYDCT;
BReversedl = blockproc(BMask,[N N],InverseDct);
I_out= uint8(BReversedl+128);
After processing, an image looks like this:
I need the function removes the details in the image (e.g. patterns on the sweater, shadow on the pants), which it seems working fine. However, the function also makes the image very fuzzy. How can I remove the details, as well as keeping the region structure clear? For example, the sweater/pants region will be more uniform coloured region than before.
You basically applied "Local Low Pass Filter".
No wonder "Fuzzy" look is the result, you removed data in the High Frequency we usually interpret as details and "Sharpness".
What you really should do is remove High Frequency details yet keep large edges in tact.
A good way to do is use something like Anisotropic Diffusion.
By using the optimized parameters you'll be able to achieve the look you're after.
In general those methods are called image abstractions.
Here's a great Open Source code for advanced Anisotropic Diffusion:
https://github.com/RoyiAvital/Fast-Anisotropic-Curvature-Preserving-Smoothing
Work with, if you can contribute, it would be amazing.

VL_SLIC MATLAB Output for VL_FEAT

I am using the VL_SLIC function in MATLAB and I am following the tutorial for the function here: http://www.vlfeat.org/overview/slic.html
This is the code I have written so far:
im = imread('slic_image.jpg');
regionSize = 10 ;
regularizer = 10;
vl_setup;
segments = vl_slic(single(im), regionSize, regularizer);
imshow(segments);
I just get a black image and I am not able to see the segmented image with the superpixels. Is there a way that I can view the result as shown in the webpage?
The reason why is because segments is actually a map that tells you which regions of your image are superpixels. If a pixel in this map belongs to ID k, this means that this pixel belongs to superpixel k. Also, the map is of type uint32 and so when you try doing imshow(segments); it really doesn't show anything meaningful. For that image that is seen on the website, there are 1023 segments given your selected parameters. As such, the map spans from 0 to 1023. If want to see what the segments look like, you could do imshow(segments,[]);. What this will do is that the region with the ID of 1023 will get mapped to white, while the pixels that don't belong to any superpixel region (ID of 0), gets mapped to black. You would actually get something like this:
Not very meaningful! Now, to get what you see on the webpage, you're going to have to do a bit more work. From what I know, VLFeat doesn't have built-in functionality that shows you the results like what is seen on their webpage. As such, you will have to write code to do it yourself. You can do this by following these steps:
Create a map that is true that is the same size as the image
For each superpixel region k:
Create another map that marks true for any pixel belonging to the region k, and false otherwise.
Find the perimeter of this region.
Set these perimeter pixels to false in the map created in Step #1
Repeat Step #2 until we have finished going through all of the regions.
Use this map to mask out all of the pixels in the original image to get what you see in the website.
Let's go through that code now. Below is the setup that you have established:
vl_setup;
im = imread('slic_image.jpg');
regionSize = 10 ;
regularizer = 10 ;
segments = vl_slic(single(im), regionSize, regularizer);
Now let's go through that algorithm that I just mentioned:
perim = true(size(im,1), size(im,2));
for k = 1 : max(segments(:))
regionK = segments == k;
perimK = bwperim(regionK, 8);
perim(perimK) = false;
end
perim = uint8(cat(3,perim,perim,perim));
finalImage = im .* perim;
imshow(finalImage);
We thus get:
Bear in mind that this is not exactly the same as what you get on the website. I simply went to the website and saved that image, then proceeded with the code I just showed you. This is probably because the slic_image.jpg image is not the exact original that was given in their example. There seems to be superpixels in areas where there are some bad quantization artifacts. Also, I'm using a relatively old version of VLFeat - Version 0.9.16. There may have been improvements to the algorithm since then, so I may not be using the most up to date version. In any case, this is something for you that you can start with.
Hope this helps!
I found these lines in vl_demo_slic.m may be useful.
segments = vl_slic(im, regionSize, regularizer, 'verbose') ;
% overaly segmentation
[sx,sy]=vl_grad(double(segments), 'type', 'forward') ;
s = find(sx | sy) ;
imp = im ;
imp([s s+numel(im(:,:,1)) s+2*numel(im(:,:,1))]) = 0 ;
It generates edges from the gradient of the superpixel map (segments).
While I want to take nothing away from ~ rayryeng's ~ beautiful answer.
This could also help.
http://www.vlfeat.org/matlab/demo/vl_demo_slic.html
Available in: toolbox/demo

MATLAB - Restore central sub-section of an image

So, I have a 512x512 distorted image, but what I'm trying to do is restore only a 400x400 centrally-positioned subsection of the image while it is still distorted outside of it. How do I go about implementing something like that?
I was thinking to have a for loop within a for loop like
for row = 57:457
for col = 57:457
%some filter in here
end
end
But I'm not quite sure what to do next...
As a general rule, you can do a lot of things in MATLAB without loops using vectorization instead. As discussed in the comments below your question, there are filtering functions included with MATLAB such as medfilt2, wiener2 or imfilter which all work on two-dimensional images directly without the need for any loops.
To restore only the center part of your image, you apply the filter to the full image, store the result in a temporary variable and then copy over the part that you want into your distored image:
tmpimage = medfilt2(distortedimage);
finalimage = distortedimage;
finalimage(57:456,57:456)=tmpimage(57:456,57:456);
Of course if you don't care about edge effects during the reconstruction, you can just call the reconstruction for the part that interests you and avoid the tmpimage:
finalimage = distortedimage;
finalimage(57:456,57:456)=medfilt2(distortedimage(57:456,57:456));
Note how the sizes in an assignment need to match: you can't assign finalimage(57:456,57:456)=medfilt2(distortedimage) since the right-hand-size produces a 512-by-512 matrix which doesn't fit into the 400-by-400 center of finalimage.