cut away the black frame of images using MatLab - image

For my master thesis I have to examine several similarly structured images with Matlab. I have already coded the actual evaluation. My problem is that the images have a black frame of different thickness. This will cause the evaluation to be falsified. So far I cut the border by hand. So I wanted to ask if someone could help me cut this frame using a Matlab code.
Below is an example picture,
I tried to solve this problem with the edge-plug in (https://de.mathworks.com/help/images/ref/edge.html) but it didn´t worked.
Maybe you can give me some advice

I wouldn't be using the edge detection as you have linked to in your question.
Rather, I would focus on reducing the dimensions of the image and analyzing the "reduced image". In the following code you can see an example of reducing the image into two representative lines, and analyzing the line to detect the specimen.
% Load the image.
Img = imread('pfi36.png');
% Level to pass for the "edge" detection.
lvl = 50;
% Add a buffer around the edges.
buf = 10;
% Reduce the Image to 1D along the x and y driections respectively.
lrData = mean(Img,1);
tdData = mean(Img,2);
% Find the indices of the edges.
indL = find(lrData>lvl,1,'first');
indR = find(lrData>lvl,1,'last');
indB = find(tdData>lvl,1,'last');
% Show the Cropped image.
newImg = Img(1:(indB+buf),(indL-buf):(indR+buf));
imshow(newImg);

Related

Draw a curve in the segmented image using matlab

I have a segmented image as shown here
i want to fit a curve along the top pixels of the segmented image(show as red curve) and i want to find the top point along the curve show in blue. I have already worked on basic idea like traversing through the top to bottom and collecting the top point along each column. i want to know is there any easy solution for this problem like directly taking out the boundary pixels and find the top point.I am using MATLAB for this problem
%download the image
img = logical(imread('http://i.stack.imgur.com/or2iX.png'));
%for some reason it appeared RGB with big solid borders.
%to monochrome
img = img(:,:,1);
%remove borders
img = img(~all(img,2), ~all(img,1));
%split into columns
cimg = num2cell(img,1);
%find first nonzero element per column
ridx = cellfun(#(x) find(x,1,'first'), cimg);
figure, imshow(img)
hold on
%image dim1 is Y, dim2 is X
plot(1:size(img,2),ridx-1,'r','linewidth',2)
%find top point
[yval, xval] = min(ridx);
If you want a smoother curve, try polyfit/polyval
#EDIT
If we want the line to have break at gaps between connected components, we should change the code to something like
bord_idx = sub2ind(size(img), ridx, 1:size(img,2));
regs=regionprops(bwlabel(img),'pixelidxlist');
regs_idx = struct2cell(regs);
split_step = cellfun(#(x) sum(ismember(bord_idx,x)), regs_idx);
split_step = split_step(split_step>0);
split_yvals = mat2cell(ridx',split_val);
split_xvals = mat2cell([1:size(img,2)]',split_val);
figure, imshow(img)
hold on
for k = 1:length(split_step),
plot(split_xvals{k}, split_yvals{k}, 'r', 'linewidth', 2),
end
However, the result is not ideal if one region is positioned over the other. If the "shadowed" points are needed, you should try bwtraceboundary or convexhull and find where the border turns down
As far as "simplest matlab solution" by which I think you mean built in matlab functions: imclose()->edge()->bwboundaries()->findpeaks()'on each boundary'->'filter results based on width and magnitude of peaks'. *you will need to tune all the parameters in these functions, I am just listing what would get you there if appropriately applied.
As far as processing speed is concerned, I think I would have done exactly what you did, basically collecting the top edge from a top down column search and then looking for the point of highest inflection. As soon as you start doing processing of any type, you start doing several operations per pixel which will quickly become more expensive than your initial search (just requires that your image and target are simple enough)
That being said, here are some ideas that may help:
1:If you run a sufficiently heavy closing (dilate->erode), that should fill in all that garbage at the bottom.
2: If you know that your point of interest is not at left or right of picture (boundaries), you could take the right and left edge points and calculate a slope to be applied as an offset to flatten the whole image.
3: If your image always has the large dark linear region below the peak as seen here, you could locate those edges with houghlines looking for verticals and then search only the columns between them.
4: If speed is a concern, you could do a more sophisticated search pattern than left to right, as your peak has a pretty good distribution around it which could help with faster localization of maxima.

Detect black dots from color background

My short question
How to detect the black dots in the following images? (I paste only one test image to make the question look compact. More images can be found →here←).
My long question
As shown above, the background color is roughly blue, and the dots color is "black". If pick one black pixel and measure its color in RGB, the value can be (0, 44, 65) or (14, 69, 89).... Therefore, we cannot set a range to tell the pixel is part of the black dot or the background.
I test 10 images of different colors, but I hope I can find a method to detect the black dots from more complicated background which may be made up of three or more colors, as long as human eyes can identify the black dots easily. Some extremely small or blur dots can be omitted.
Previous work
Last month, I have asked a similar question at stackoverflow, but have not got a perfect solution, some excellent answers though. Find more details about my work if you are interested.
Here are the methods I have tried:
Converting to grayscale or the brightness of image. The difficulty is that I can not find an adaptive threshold to do binarization. Obviously, turning a color image to grayscale or using the brightness (HSV) will lose much useful information. Otsu algorithm which calculates adaptive threshold can not work either.
Calculating RGB histogram. In my last question, natan's method is to estimate the black color by histogram. It is time-saving, but the adaptive threshold is also a problem.
Clustering. I have tried k-means clustering and found it quite effective for the background that only has one color. The shortage (see my own answer) is I need to set the number of clustering center in advance but I don't know how the background will be. What's more, it is too slow! My application is for real time capturing on iPhone and now it can process 7~8 frames per second using k-means (20 FPS is good I think).
Summary
I think not only similar colors but also adjacent pixels should be "clustered" or "merged" in order to extract the black dots. Please guide me a proper way to solve my problem. Any advice or algorithm will be appreciated. There is no free lunch but I hope a better trade-off between cost and accuracy.
I was able to get some pretty nice first pass results by converting to HSV color space with rgb2hsv, then using the Image Processing Toolbox functions imopen and imregionalmin on the value channel:
rgb = imread('6abIc.jpg');
hsv = rgb2hsv(rgb);
openimg = imopen(hsv(:, :, 3), strel('disk', 11));
mask = imregionalmin(openimg);
imshow(rgb);
hold on;
[r, c] = find(mask);
plot(c, r, 'r.');
And the resulting images (for the image in the question and one chosen from your link):
You can see a few false positives and missed dots, as well as some dots that are labeled with multiple points, but a few refinements (such as modifying the structure element used in the opening step) could clean these up some.
I was curios to test with my old 2d peak finder code on the images without any threshold or any color considerations, really crude don't you think?
im0=imread('Snap10.jpg');
im=(abs(255-im0));
d=rgb2gray(im);
filter=fspecial('gaussian',16,3.5);
p=FastPeakFind(d,0,filter);
imagesc(im0); hold on
plot(p(1:2:end),p(2:2:end),'r.')
The code I'm using is a simple 2D local maxima finder, there are some false positives, but all in all this captures most of the points with no duplication. The filter I was using was a 2d gaussian of width and std similar to a typical blob (the best would have been to get a matched filter for your problem).
A more sophisticated version that does treat the colors (rgb2hsv?) could improve this further...
Here is an extraodinarily simplified version, that can be extended to be full RGB, and it also does not use the image procesing library. Basically you can do 2-D convolution with a filter image (which is an example of the dot you are looking for), and from the points where the convolution returns the highest values, are the best matches for the dots. You can then of course threshold that. Here is a simple binary image example of just that.
%creating a dummy image with a bunch of small white crosses
im = zeros(100,100);
numPoints = 10;
% randomly chose the location to put those crosses
points = randperm(numel(im));
% keep only certain number of points
points = points(1:numPoints);
% get the row and columns (x,y)
[xVals,yVals] = ind2sub(size(im),points);
for ii = 1:numel(points)
x = xVals(ii);
y = yVals(ii);
try
% create the crosses, try statement is here to prevent index out of bounds
% not necessarily the best practice but whatever, it is only for demonstration
im(x,y) = 1;
im(x+1,y) = 1;
im(x-1,y) = 1;
im(x,y+1) = 1;
im(x,y-1) = 1;
catch err
end
end
% display the randomly generated image
imshow(im)
% create a simple cross filter
filter = [0,1,0;1,1,1;0,1,0];
figure; imshow(filter)
% perform convolution of the random image with the cross template
result = conv2(im,filter,'same');
% get the number of white pixels in filter
filSum = sum(filter(:));
% look for all points in the convolution results that matched identically to the filter
matches = find(result == filSum);
%validate all points found
sort(matches(:)) == sort(points(:))
% get x and y coordinate matches
[xMatch,yMatch] = ind2sub(size(im),matches);
I would highly suggest looking at the conv2 documentation on MATLAB's website.

matlab template matching only for 0 (or 1) in matrix

I'm a beginner in matlab programming and i'm having some troubles with template matching. I have a few white boxes with a black border (link for pic below) along with some text and I want to extract all the boxes,and there's also one that has an X in it(it's a multiple choice answer). In the beginning I used normxcorr2 , but the problem is that due to the many white pixels of the template, I get allot of irrelevant templates like only white spaces. I was looking for an algorithm that does template matching only on 0's , so I could get templates that have black squares only. I hope I made myself clear, thanks :)
http://i91.photobucket.com/albums/k284/Chris2401/sqrTemplate.png
EDIT: May 2nd, 2014
Now understanding what you are trying to achieve, I can now help you solve your problem. As you are a beginner to MATLAB, I will use a more simpler approach (even though there are more sophisticated methods to help you figure this out) as I want to demonstrate how the algorithm works.
You basically want to implement normxcorr2, but you only want to include pixels that are marked as black in the template. You also want to skip locations in the search image that are not black. In that case, I will layout the algorithm for you step by step, then write some code.
Read in the template and extract those pixel locations that are black
In a sliding window fashion, for each image patch of the same size as the template that is in the search image...
If the entire image patch is white, skip
Extract the same locations that are black
Compute the NCC using those locations only.
The output will be a map that contains the NCC for each location in the search image
I will not handle the case of the border pixels in the search image, as I am assuming that you want to be able to match something in the search image with the full size of the template.
Here are some assumptions. Let's assume the following variables:
imTemplate - The template image, such as the one that you have showed me
imSearch - The image we wish to search for when doing this NCC stuff
I am also assuming that each of the images are binary, as the title of your post has "0 or 1" in it.
As such, I have the following code for you:
[rowsSearch colsSearch] = size(imSearch); % Get dimensions of search image
[rowsTemp colsTemp] = size(imTemplate); % Get dimensions of template image
mapBlack = imSearch == 0; % Obtain a map of where the black pixels are in the template
numPixelsCompare = sum(mapBlack(:)); % Need to know how many pixels are valid for comparison
% Obtain area of searching within the search image
startSearchRows = 1 + floor(rowsSearch/2);
endSearchRows = rowsSearch - floor(rowsSearch/2);
startSearchCols = 1 + floor(colsSearch/2);
endSearchCols = colsSearch - floor(colsSearch/2);
% Need half the dimensions of each for the template dimensions... you will
% see why we need this later
rowsTempHalf = floor(rowsTemp/2);
colsTempHalf = floor(colsTemp/2);
% Where we will store our NCC calculations
NCCMap = zeros(rowsSearch, colsSearch);
% Because you want to include all of the black pixels in your
% calculations, and these are all the same, the signal you are comparing
% to basically consists of all white pixels.
% Create a vector that consists of all 1s that is the same size as how
% many black pixels exist in the template
compareSignal = ones(numPixelsCompare, 1);
% For each location in the search image (ensuring that the full template
% is inside the image)
for i = startSearchRows : endSearchRows
for j = startSearchCols : endSearchCols
% Grab an image patch that is the same size as the template
% At each location (i,j) this serves as the CENTRE of the image
% patch, and we are grabbing pixels surrounding this centre that
% will create a patch that is the same size as the template
searchBlock = imSearch(i-rowsTempHalf:i+rowsTempHalf, ...
j-colsTempHalf:j+colsTempHalf);
% If all of the pixels are white, skip
if (all(searchBlock == 1))
continue;
end
% Extract only those pixels that are valid in the template
searchPixels = searchBlock(mapBlock);
% Must invert so that black pixels become white
% You mentioned that white pixels are "background"
searchPixels = ~searchPixels;
% Compute NCC for this patch
NCCMap(i,j) = compareSignal'*searchPixels / ...
(sqrt(compareSignal'*compareSignal) * sqrt(searchPixels'*searchPixels));
end
end
If you're a bit confused with the way I calculated the NCC, it is basically what you're used to, but I used vector algebra to compute it instead. This should hopefully give you what you want. To find the best location of where the template matched, you can do the following to extract the row and column of this location:
[r,c] = find(NCCMap == max(NCCMap(:)));
I hope this solves your question. It is a tad inefficient, and it will really start to suffer with higher resolution images, but I wanted to give you a good start so you're not sitting around idle trying to figure it out on your own.
NB: I have not tested this code yet as I don't have an example search image that you are using to solve this problem. Hopefully this will work. Leave a comment and let me know how it goes.

Find coordinates of partial image of an image

I am searching for a way to find the location of a button on my screen using matlab. Now i have created the following code for this:
With this code i make a screenshot of my computer, load it in matlab and make it double.
Then do the same with a target image, the image that needs to be found on my screen, and then try to match this to find matrix to the total matrix and display the coordinates of the top left corner of this image.
The problem is that with this method and a screen resolution of 1920*1080 it takes about 15 min to go trough this full proces.
So my question is, is there an easier/faster way to find the coördinates(or the center of) an image on my screen, or within another image? So far i have found none, even when using the image processing toolbox.
clear all
close all
robo = java.awt.Robot;
t = java.awt.Toolkit.getDefaultToolkit();
rectangle = java.awt.Rectangle(t.getScreenSize());
image = robo.createScreenCapture(rectangle);
filehandle = java.io.File('screencapture.png');
javax.imageio.ImageIO.write(image,'png',filehandle);
scrimg=im2double(imread('screencapture.png')); % screenshot
sfimg=im2double(imread('searchfor.png')); % image to look for
[mA,nA,zA] = size(sfimg);
[mB,nB,zB] = size(scrimg);
F = zeros((mB-mA+1)*(nB-nA+1),3);
k = 0;
for p = 1:mB-mA+1
for q = 1:nB-nA+1
for r = 1:zB-zA+1
iets=[p q r];
disp(iets)
if all(all(sfimg==scrimg(p:p+mA-1,q:q+nA-1,r:r+zA-1)))
k = k + 1;
F(k,:) = [p,q,r];
end
end
end
end
F(k+1:end,:) = [];
Check out the following links:
http://repository.asu.edu/items/15110
http://repository.asu.edu/attachments/94025/content//tmp/package-fN0ldX/Velocimeter.pdf
http://repository.asu.edu/attachments/94026/content//tmp/package-fN0ldX/ThesisPresentationr2.pdf
I did 2d tracking from videos in matlab against some very particular geometries. It was radically faster than 15 minutes. The part you are interested in is "measure on image".
The basic idea is that you can use fourier-domain 2d operations, a "max" and some local polynomial fitting to get reliable sub-pixel alignment.
Best of luck
A quicker way is to instead of searching for the entire image is to take a smaller subset of pixels say:
n=4;
SegmentOfImage=sfimg(1:(mA/n),1:(nA/n));
and search for that segment. Obviously as n increases the algorithm would be quicker.
If you want to then make sure that it was actually that segment you can make another comparison on the whole image but now you know the index to start with.
Note:
1) This algorithm can actually take longer if there are a lot of objects with identical pixels to
SegmentOfImage.
2) Also, I believe that this code in general would work only if the matrices are exactly the same. For example if the size of sfimg is not the same as scrimg it would fail.
Hope this helps

Find and crop defined image areas automatically

I want to process an image in matlab
The image consists out of a solid back ground and two specimens (top and bottom side). I already have a code that separate the top and bottom and make it two images. But the part what I don't get working is to crop the image to the glued area only (red box in the image, I've only marked the top one). However, the cropped image should be a rectangle just like the red box (the yellow background, can be discarded afterwards).
I know this can be done with imcrop, but this requires manual input from the user. The code needs to be automated such that it is possible to process more images without user input. All image will have the same colors (red for glue, black for material).
Can someone help me with this?
edit: Thanks for the help. I used the following code to solve the problem. However, I couldn't get rid of the black part right of the red box. This can be fix by taping that part off before making pictures. The code which I used looks a bit weird, but it succeeds in counting the black region in the picture and getting a percentage.
a=imread('testim0.png');
level = graythresh(a);
bw2=im2bw(a, level);
rgb2=bw2rgb(bw2);
IM2 = imclearborder(rgb2,4);
pic_negative = ait_imgneg(IM2);
%% figures
% figure()
% image(rgb2)
%
% figure()
% imshow(pic_negative)
%% Counting percentage
g=0;
for j=1:size(rgb2,2)
for i=1:size(rgb2,1)
if rgb2(i,j,1) <= 0 ...
& rgb2(i,j,2) <= 0 ...
& rgb2(i,j,3) <= 0
g=g+1;
end
end
end
h=0;
for j=1:size(pic_negative,2)
for i=1:size(pic_negative,1)
if pic_negative(i,j)== 0
h=h+1;
end
end
end
per=g/(g+h)
If anyone has some suggestions to improve the code, I'm happy to hear it.
For a straight-forward image segmentation into 2 regions (background, foreground) based on color (yellow, black are prominent in your case), an option can be clustering image color values using kmeans algorithm. For additional robustness you can transform the image from RGB to Lab* colorspace.
The example for your case follows the MATLAB Imape Processing example here.
% read and transform to L*a*b space
im_rgb = double(imread('testim0.png'))./256;
im_lab = applycform(im_rgb, makecform('srgb2lab'));
% keep only a,b-channels and form feature vector
ab = double(lab_I(:,:,2:3));
[nRows, nCols, ~] = size(ab);
ab = reshape(ab,nRows * nCols,2);
% apply k-means for 2 regions, repeat c times, e.g. c = 5
nRegions = 2;
[cluster_idx cluster_center] = kmeans(ab,nRegions, 'Replicates', 5);
% get foreground-background mask
im_regions = reshape(cluster_idx, nRows, nCols);
You can use the resulting binary image to index the regions of interest (or find the boundary) in the original reference image.
Images are saved as matrices. If you know the bounds in pixels of the crop box want to crop you can execute the crop using indexing.
M = rand(100); % create a 100x100 matrix or load it from an image
left = 50;
right = 75;
top = 80;
bottom = 10;
croppedM = M(bottom:top, left:right);
%save croppedm
You can easily get the unknown bounded crop by
1) Contour plotting the image,
2) find() on the result for the max/min X/ys,
3) use #slayton's method to perform the actual crop.
EDIT: Just looked at your actual image - it won't be so easy. But color enhance/threshold your image first, and the contours should work with reasonable accuracy. Needless to say, this requires tweaking to your specific situation.
since you've already been able to seperate the top and bottom, and also able to segment the region you want (including the small part of the right side that you don't want), I propose you just add a fix at the end of the code via the following.
after segmentation, sum each column Blue intensity value, so that you are compressing the image from 2d to 1d. so if original region is width=683 height=59, the new matrix/image will be simply width=683 height=1.
now, you can apply a small threshold to determine where the edge should lie, and apply a crop to the image at that location. Now you get your stats.

Resources