I'm a fresher to Matlab. I'm working on
vision.CascadeObjectDetector on Mat-lab and is used
twice to find-out two different objects(separately
trained), say E and K from a video. bbox and bbox2
are respective ROIs. part of code in while loop is given below:
videoFrame=step(videoFileReader);
bbox=step(letterDetector_E,videoFrame);
bbox2=step(letterDetector_K,videoFrame);
C = vertcat(bbox,bbox2);
videoOut=insertObjectAnnotation(videoFrame, 'rectangle', C, 'E&K');
step(videoPlayer, videoOut);
I want to ignore the area denoted by bbox while finding out bbox2. That is, no two object should be find out from same part of image(obviously there are similiarities in both type of objects). So I wish to mask or ignore bbox area of videoFrame while
bbox2 is assigned with step(letterDetector_K, videoFrame)
is executing. Format of bbox is [xUpperLeft, yUpperLeft, width,
height].
How can I do that.
That's pretty easy to do. Once you detect the first shape, use the bounding box detected for the first object E, then insert a filled rectangle in that spot using insertShape. Make sure you set the Opacity to 1.0 so that it doesn't mix any pixels from the background into this rectangle, and choose a colour of the filled rectangle that is completely different from the object you're trying to detect. Perhaps choose black. Also, I would recommend you turn off anti-aliasing when drawing the rectangle, because it actually takes more time to draw a shape with this turned on. Anti-aliasing essentially smoothes edges for any shapes. Because you don't want to include information with this region, there's really no need for anti-aliasing and so set this to false. This is done with the SmoothEdges option.
Once you're finished filling the shape in, use this modified frame and detect the next object K. As such, you only need one more line of code, so do this:
videoFrame=step(videoFileReader);
bbox=step(letterDetector_E,videoFrame);
%// NEW - Insert filled rectangle
videoFrame = insertShape(videoFrame, 'FilledRectangle', bbox, ...
'Opacity', 1.0, 'Color', 'black', ...
'SmoothEdges', false);
%// Now detect next shape on modified frame
bbox2=step(letterDetector_K,videoFrame);
C = vertcat(bbox,bbox2);
videoOut=insertObjectAnnotation(videoFrame, 'rectangle', C, 'E&K');
step(videoPlayer, videoOut);
How insertShape works is that it takes in an image (videoFrame in your case), then you specify the FilledRectangle flag and the bounding box location, which precisely coincides with a 4 element array in the way you described, which is the x and y of top-left corner, followed by width and height of the rectangle. We will place a filled in rectangle at this location. We then specify further options for the rectangle before drawing it, such as setting the opacity to 1.0, the colour to black and anti-aliasing turned off. We output the modified frame with this rectangle inserted, where I will mutate videoFrame so that there is minimal editing required for your code. You would then use videoFrame to detect the next shape.
Related
I was working on my image processing problem with detecting coins.
I have some images like this one here:
and wanted to separate the falsely connected coins.
We already tried the watershed method as stated on the MATLAB-Homepage:
the-watershed-transform-strategies-for-image-segmentation.html
especially since the first example is exactly our problem.
But instead we get a somehow very messed up separation as you can see here:
We already extracted the area of the coin using the regionprops Extrema parameter and casting the watershed only on the needed area.
I'd appreciate any help with the problem or even another method of getting it separated.
If you have the Image Processing Toolbox, I can also suggest the Circular Hough Transform through imfindcircles. However, this requires at least version R2012a, so if you don't have it, this won't work.
For the sake of completeness, I'll assume you have it. This is a good method if you want to leave the image untouched. If you don't know what the Hough Transform is, it is a method for finding straight lines in an image. The circular Hough Transform is a special case that aims to find circles in the image.
The added advantage of the circular Hough Transform is that it is able to detect partial circles in an image. This means that those regions in your image that are connected, we can detect them as separate circles. How you'd call imfindcircles is in the following fashion:
[centers,radii] = imfindcircles(A, radiusRange);
A would be your binary image of objects, and radiusRange is a two-element array that specifies the minimum and maximum radii of the circles you want to detect in your image. The outputs are:
centers: A N x 2 array that tells you the (x,y) co-ordinates of each centre of a circle that is detected in the image - x being the column and y being the row.
radii: For each corresponding centre detected, this also gives the radius of each circle detected. This is a N x 1 array.
There are additional parameters to imfindcircles that you may find useful, such as the Sensitivity. A higher sensitivity means that it is able to detect circular shapes that are more non-uniform, such as what you are showing in your image. They aren't perfect circles, but they are round shapes. The default sensitivity is 0.85. I set it to 0.9 to get good results. Also, playing around with your image, I found that the radii ranged from 50 pixels to 150 pixels. Therefore, I did this:
im = im2bw(imread('http://dennlinger.bplaced.net/t06-4.jpg'));
[centers,radii] = imfindcircles(im, [50 150], 'Sensitivity', 0.9);
The first line of code reads in your image directly from StackOverflow. I also convert this to logical or true black and white as the image you uploaded is of type uint8. This image is stored in im. Next, we call imfindcircles in the method that we described.
Now, if we want to visualize the detected circles, simply use imshow to show your image, then use the viscircles to draw the circles in the image.
imshow(im);
viscircles(centers, radii, 'DrawBackgroundCircle', false);
viscircles by default draws the circles with a white background over the contour. I want to disable this because your image has white circles and I don't want to show false contouring. This is what I get with the above code:
Therefore, what you can take away from this is the centers and radii variables. centers will give you the centre of each detected circle while radii will tell you what the radii is for each circle.
Now, if you want to simulate what regionprops is doing, we can iterate through all of the detected circles and physically draw them onto a 2D map where each circle would be labeled by an ID number. As such, we can do something like this:
[X,Y] = meshgrid(1:size(im,2), 1:size(im,1));
IDs = zeros(size(im));
for idx = 1 : numel(radii)
r = radii(idx);
cen = centers(idx,:);
loc = (X - cen(1)).^2 + (Y - cen(2)).^2 <= r^2;
IDs(loc) = idx;
end
We first define a rectangular grid of points using meshgrid and initialize an IDs array of all zeroes that is the same size as the image. Next, for each pair of radii and centres for each circle, we define a circle that is centered at this point that extends out for the given radius. We then use these as locations into the IDs array and set it to a unique ID for that particular circle. The result of IDs will be that which resembles the output of bwlabel. As such, if you want to extract the locations of where the idx circle is, you would do:
cir = IDs == idx;
For demonstration purposes, this is what the IDs array looks like once we scale the IDs such that it fits within a [0-255] range for visibility:
imshow(IDs, []);
Therefore, each shaded circle of a different shade of gray denotes a unique circle that was detected with imfindcircles.
However, the shades of gray are probably a bit ambiguous for certain coins as this blends into the background. Another way that we could visualize this is to apply a different colour map to the IDs array. We can try using the cool colour map, with the total number of colours to be the number of unique circles + 1 for the background. Therefore, we can do something like this:
cmap = cool(numel(radii) + 1);
RGB = ind2rgb(IDs, cmap);
imshow(RGB);
The above code will create a colour map such that each circle gets mapped to a unique colour in the cool colour map. The next line applies a mapping where each ID gets associated with a colour with ind2rgb and we finally show the image.
This is what we get:
Edit: the following solution is more adequate to scenarios where one does not require fitting the exact circumferences, although simple heuristics could be used to approximate the radii of the coins in the original image based on the centers found in the eroded one.
Assuming you have access to the Image Processing toolbox, try imerode on your original black and white image. It will apply an erosion morphological operator to your image. In fact, the Matlab webpage with the documentation of that function has an example strikingly similar to your problem/image and they use a disk structure.
Run the following code (based on the example linked above) assuming the image you submitted is called ima.jpg and is local to the code:
ima=imread('ima.jpg');
se = strel('disk',50);
eroded = imerode(ima,se);
imshow(eroded)
and you will see the image that follows as output. After you do this, you can use bwlabel to label the connected components and compute whatever properties you may want, for example, count the number of coins or detect their centers.
How do I separate the two connected circles in the image below, using MATLAB? I have tried using imerode, but this does not give good results. Eroding does not work, because in order to erode enough to separate the circles, the lines disappear or become mangled. In other starting pictures, a circle and a line overlap, so isolating the overlapping objects won't work either.
The image shows objects identified by bwboundaries, each object painted a different color. As you can see, the two light blue circles are joined, and I want to disjoin them, producing two separate circles. Thanks
I would recommend you use the Circular Hough Transform through imfindcircles. However, you need version 8 of the Image Processing Toolbox, which was available from version R2012a and onwards. If you don't have this, then unfortunately this won't work :(... but let's go with the assumption that you do have it. However, if you are using something older than R2012a, Dev-iL in his/her comment above linked to some code on MATLAB's File Exchange on an implementation of this, most likely created before the Circular Hough Transform was available: http://www.mathworks.com/matlabcentral/fileexchange/9168-detect-circles-with-various-radii-in-grayscale-image-via-hough-transform/
This is a special case of the Hough Transform where you are trying to find circles in your image rather than lines. The beauty with this is that you are able to find circles even when the circle is partially completed or overlapping.
I'm going to take the image that you provided above and do some post-processing on it. I'm going to convert the image to binary, and remove the border, which is white and contains the title. I'm also going to fill in any holes that result so that all of the objects are filled in with solid white. There is also some residual quantization noise after I do this step, so I'm going to a small opening with a 3 x 3 square element. After, I'm going to close the shapes with a 3 x 3 square element, as I see that there are noticeable gaps in the shapes. Therefore:
Therefore, directly reading in your image from where you've posted it:
im = imread('http://s29.postimg.org/spkab8oef/image.jpg'); %// Read in the image
im_gray = im2double(rgb2gray(im)); %// Convert to grayscale, then [0,1]
out = imclearborder(im_gray > 0.6); %// Threshold using 0.6, then clear the border
out = imfill(out, 'holes'); %// Fill in the holes
out = imopen(out, strel('square', 3));
out = imclose(out, strel('square', 3));
This is the image I get:
Now, apply the Circular Hough Transform. The general syntax for this is:
[centres, radii, metric] = imfindcircles(img, [start_radius, end_radius]);
img would be the binary image that contains your shapes, start_radius and end_radius would be the smallest and largest radius of the circles you want to find. The Circular Hough Transform is performed such that it will find any circles that are within this range (in pixels). The outputs are:
centres: Which returns the (x,y) positions of the centres of each circle detected
radii: The radius of each circle
metric: A measure of purity of the circle. Higher values mean that the shape is more probable to be a circle and vice-versa.
I searched for circles having a radius between 30 and 60 pixels. Therefore:
[centres, radii, metric] = imfindcircles(out, [30, 60]);
We can then demonstrate the detected circles, as well as the radii by a combination of plot and viscircles. Therefore:
imshow(out);
hold on;
plot(centres(:,1), centres(:,2), 'r*'); %// Plot centres
viscircles(centres, radii, 'EdgeColor', 'b'); %// Plot circles - Make edge blue
Here's the result:
As you can see, even with the overlapping circles towards the top, the Circular Hough Transform was able to detect two distinct circles in that shape.
Edit - November 16th, 2014
You wish to ensure that the objects are separated before you do bwboundaries. This is a bit tricky to do. The only way I can see you do this is if you don't even use bwboundaries at all and do this yourself. I'm assuming you'll want to analyze each shape's properties by themselves after all of this, so what I suggest you do is iterate through every circle you have, then place each circle on a new blank image, do a regionprops call on that shape, then append it to a separate array. You can also keep track of all of the circles by having a separate array that adds the circles one at a time to this array.
Once you've finished with all of the circles, you'll have a structure array that contains all of the measured properties for all of the measured circles you have found. You would use the array that contains only the circles from above, then use these and remove them from the original image so you get just the lines. You'd then call one more regionprops on this image to get the information for the lines and append this to your final structure array.
Here's the first part of the procedure I outlined above:
num_circles = numel(radii); %// Get number of circles
struct_reg = []; %// Save the shape analysis per circle / line here
%// For creating our circle in the temporary image
[X,Y] = meshgrid(1:size(out,2), 1:size(out,1));
%// Storing all of our circles in this image
circles_img = false(size(out));
for idx = 1 : num_circles %// For each circle we have...
%// Place our circle inside a temporary image
r = radii(idx);
cx = centres(idx,1); cy = centres(idx,2);
tmp = (X - cx).^2 + (Y - cy).^2 <= r^2;
% // Save in master circle image
circles_img(tmp) = true;
%// Do regionprops on this image and save
struct_reg = [struct_reg; regionprops(tmp)];
end
The above code may be a bit hard to swallow, but let's go through it slowly. I first figure out how many circles we have, which is simply looking at how many radii we have detected. I keep a separate array called struct_reg that will append a regionprops struct for each circle and line we have in our image. I use meshgrid to determine the (X,Y) co-ordinates with respect to the image containing our shapes so that I can draw one circle onto a blank image at each iteration. To do this, you simply need to find the Euclidean distance with respect to the centre of each circle, and set the pixels to true only if that location has its distance less than r. After doing this operation, you will have created only one circle and filtered all of them out. You would then use regionprops on this circle, add it to our circles_img array, which will only contain the circles, then continue with the rest of the circles.
At this point, we will have saved all of our circles. This is what circles_img looks like so far:
You'll notice that the circles drawn are clean, but the actual circles in the original image are a bit jagged. If we tried to remove the circles with this clean image, you will get some residual pixels along the border and you won't completely remove the circles themselves. To illustrate what I mean, this is what your image looks like if I tried to remove the circles with circles_img by itself:
... not good, right?
If you want to completely remove the circles, then do a morphological reconstruction through imreconstruct where you can use this image as the seed image, and specify the original image to be what we're working on. The job of morphological reconstruction is essentially a flood fill. You specify seed pixels, and an image you want to work on, and the job of imreconstruct is from these seeds, flood fill with white until we reach the boundaries of the objects that the seed pixels resided in. Therefore:
out_circles = imreconstruct(circles_img, out);
Therefore, we get this for our final reconstructed circles image:
Great! Now, use this and remove the circles from the original image. Once you do this, run regionprops again on this final image and append to your struct_reg variable. Obviously, save a copy of the original image before doing this:
out_copy = out;
out_copy(out_circles) = false;
struct_reg = [struct_reg; regionprops(out_copy)];
Just for sake of argument, this is what the image looks like with the circles removed:
Now, we have analyzed all of our shapes. Bear in mind I did the full regionprops call because I don't know exactly what you want in your analysis... so I just decided to give you everything.
Hope this helps!
erosion is the way to go. You should probably use a larger structuring element.
How about
1 erode
2 detect your objects
3 dilate each object for itself using the same structuring element
How to identify boundaries of a binary image to crop in matlab?
ie. the input binary image has no noises. only has one black object in white background.
You can use the edge command in MATLAB.
E = edge(I);
I would be an input grayscale or binary image. This will return a binary image with only the edges.
This can provide further assistance:
http://www.mathworks.com/help/images/ref/edge.html
If your image is just black-and-white and has a single object, you can likely make use of the Flood fill algorithm, for which Matlab has built-in support!
Try the imfill function (ref).
This should give you the extents of the object, which would allow you to crop at will.
You can also invert the image, then do regionprops to extract all of the properties for separate objects. You need to invert the image as regionprops assumes that the objects are white while the background is black. A good thing about this approach is that it generalizes for multiple objects and you only need about a few lines of code to do it.
As an example, let's artificially create a circle in the centre of an image that is black on a white background as you have suggested. Let's assume this is also a binary image.
im = true(200, 200);
[X,Y] = meshgrid(1:200, 1:200);
ind = (X-100).^2 + (Y-100).^2 <= 1000;
im(ind) = false;
imshow(im);
This is what your circle will look like:
Now let's go ahead and invert this so that it's a white circle on black background:
imInvert = ~im;
imshow(imInvert);
This is what your inverted circle will look like:
Now, invoke regionprops to find properties of all of the objects in our image. In this case, there should only be one.
s = regionProps(imInvert, 'BoundingBox');
As such, s contains a structure that is 1 element long, and has a single field called BoundingBox. This field is a 4 element array that is structured in the following way:
[x y w h]
x denotes the column/vertical co-ordinate while y denotes the row/horizontal co-ordinate of the top-left corner of the bounding box. w,h are the width and height of the rectangle. Our output of the above code is:
s =
BoundingBox: [68.5000 68.5000 63 63]
This means that the top-left corner of our bounding box is located at (x,y) = (68.5,68.5), and has a width and height of 63 each. Therefore, the span of our bounding box goes from rows (68.5,131.5) and columns (68.5,131.5). To make sure that we have the right bounding box, you can draw a rectangle around our shape by using the rectangle command.
imshow(im);
rectangle('Position', s.BoundingBox);
This is what your image will look like with a rectangle drawn around the object. As you can see, the bounding box given from regionprops is the minimum spanning bounding box required to fully encapsulate the object.
If you wish to crop the object, you can do the following:
imCrop = imcrop(imInvert, s.BoundingBox);
This should give you the cropped image that is defined by the bounding box that we talked about earlier.
Hope this is what you're looking for. Good luck!
Simple rounded corner rectangle code in Matlab can be written as follows.
rectangle('Position',[0,-1.37/2,3.75,1.37],...
'Curvature',[1],...
'LineWidth',1,'LineStyle','-')
daspect([1,1,1])
How to get the x and y coordinates arrays of this figure?
To get the axes units boundaries, do:
axisUnits = axis(axesHandle) % axesHandle could be gca
axisUnits will be an four elements array, with the following syntax: [xlowlim xhighlim ylowlim yhighlim], it will also contain the zlow and zhigh for 3-D plots.
But I think that is not what you need to know. Checking the matlab documentation for the rectangle properties, we find:
Position four-element vector [x,y,width,height]
Location and size of rectangle. Specifies the location and size of the
rectangle in the data units of the axes. The point defined by x, y
specifies one corner of the rectangle, and width and height define the
size in units along the x- and y-axes respectively.
It is also documented on the rectangle documentation:
rectangle('Position',[x,y,w,h]) draws the rectangle from the point x,y
and having a width of w and a height of h. Specify values in axes data
units.
See if this illustrate what you want. You have an x axis that goes from −100 to 100 and y axis that goes from 5 to 15. Suppose you want to put a rectangle from −30 to −20 in x and 8 to 10 in y.
rectangle('Position',[-30,8,10,2]);
As explained by the comments there appears to be no direct way to query the figure created by rectangle and extract x/y coordinates. On the other hand, I can think of two simple strategies to arrive at coordinates that will closely reproduce the curve generated with rectangle:
(1) Save the figure as an image (say .png) and process the image to extract points corresponding to the curve. Some degree of massaging is necessary but this is relatively straightforward if blunt and I expect the code to be somewhat slow at execution compared to getting data from an axes object.
(2) Write your own code to draw a rectangle with curved edges. While recreating precisely what matlab draws may not be so simple, you may be satisfied with your own version.
Whether you choose one of these approaches boils down to (a) what speed of execution you consider acceptable (b) how closely you need to replicate what rectangle draws on screen (c) whether you have image processing routines, say for reading an image file.
Edit
If you have the image processing toolbox you can arrive at a set of points representing the rectangle as follows:
h=rectangle('Position',[0,-1.37/2,3.75,1.37],...
'Curvature',[1],...
'LineWidth',1,'LineStyle','-')
daspect([1,1,1])
axis off
saveas(gca,'test.png');
im = imread('test.png');
im = rgb2gray(im);
figure, imshow(im)
Note that you will still need to apply a threshold to pick the relevant points from the image and then transform the coordinate system and rearrange the points in order to display properly as a connected set. You'll probably also want to tinker with resolution of the initial image file or apply image processing functions to get a smooth curve.
I have an image loaded from disk as a texture, and a same-sized matrix d which has the corresponding depths.
How can I use surf to show me the image as a 3d-model? Simply taking
surf(depthMatrix, img);
doesn't give a nice result since
the camera looks not from the x-y plane in z-direction
It looks fairly black
It doesn't look that smooth although my depth matrix is actually smoothed out when I show it using imshow(depthMatrix, []);
You can use texture mapping to display your image on your surface like so:
surf(depthMatrix,img,... %# depthMatrix is z data, img is an image
'FaceColor','texturemap',... %# Use texture mapping
'EdgeColor','none'); %# Turn off edge coloring
And to address your 3 points:
You can adjust your camera angle with the mouse by pressing the button on the figure, which turns on interactive 3-D rotation. You can also turn interactive rotation on using the function ROTATE3D, or you can change the camera view without the mouse using the function VIEW.
Your plot was looking black because edges are drawn as black lines by default, and there were probably a lot of them.
You can adjust the axis scaling and limits to make your surface appear smoother. For example, axis equal will make data units the same for all 3 axes, so your z axis (which ranges from 0 to 25) will be flattened significantly since your other two axes span ranges in the hundreds. Alternatively, in your call to SURF you can specify x and y data to use for the values on those axes, which can ultimately help you to better adjust the relative scaling between those axes and the z axis.