I am writing a GUI in MATLAB (guide) where user will be shown 2 images(both images are positioned side by side in single gui window) from a series of images (but each drifted little bit) and will be allowed to select area of interest.
I want user to select working are in image 1 while simultaneously highlighting the selected area in image 2, so that it is easier to judge whether the feature of interest has drifted out of selected area or not. How to do that?
I am using following answer to select and crop area of interest(just FYI):
crop image with fixed x/y ratio
Here is a way to do it using imrect and its addNewPositionCallback method. Check here for a list of available methods.
In the following figure I create 2 axes. On the left that's the original image and on the right that's the "modified" image. By pressing the pushbutton, imrect is called and the addNewPositionCallback method executes a function, called GetROIPosition that is used to get the position of the rectangle defined by imrect. At the same time, in the 2nd axes, a rectangle is drawn with the same position as that in the 1st axes. To be even more fancy you can use the setConstrainedPosition to force the rectangle to be enclosed in a given axes. I'll let you do it :)
Here is the whole code with 2 screenshots:
function SelectROIs(~)
%clc
clear
close all
%//=========================
%// Create GUI components
hfigure = figure('Position',[300 300 900 600],'Units','Pixels');
handles.axesIm1 = axes('Units','Pixels','Position',[30,100,400 400],'XTick',[],'YTIck',[]);
handles.axesIm2 = axes('Units','Pixels','Position',[460,100,400,400],'XTick',[],'YTIck',[]);
handles.TextaxesIm1 = uicontrol('Style','Text','Position',[190 480 110 20],'String','Original image','FontSize',14);
handles.TextaxesIm2 = uicontrol('Style','Text','Position',[620 480 110 20],'String','Modified image','FontSize',14);
%// Create pushbutton and its callback
handles.SelectROIColoring_pushbutton = uicontrol('Style','pushbutton','Position',[380 500 120 30],'String','Select ROI','FontSize',14,'Callback',#(s,e) SelectROIListCallback);
%// ================================
%/ Read image and create 2nd image by taking median filter
handles.Im = imread('coins.png');
[Height,Width,~] = size(handles.Im);
handles.ModifIm = medfilt2(handles.Im,[3 3]);
imshow(handles.Im,'InitialMagnification','fit','parent',handles.axesIm1);
imshow(handles.ModifIm,'InitialMagnification','fit','parent',handles.axesIm2);
guidata(hfigure,handles);
%%
%// Pushbutton's callback. Create a draggable rectangle in the 1st axes and
%a rectangle in the 2nd axes. Using the addNewPositionCallback method of
%imrect, you can get the position in real time and update that of the
%rectangle.
function SelectROIListCallback(~)
hfindROI = findobj(handles.axesIm1,'Type','imrect');
delete(hfindROI);
hROI = imrect(handles.axesIm1,[Width/4 Height/4 Width/2 Height/2]); % Arbitrary size for initial centered ROI.
axes(handles.axesIm2)
rectangle('Position',[Width/4 Height/4 Width/2 Height/2],'EdgeColor','y','LineWidth',2);
id = addNewPositionCallback(hROI,#(s,e) GetROIPosition(hROI));
end
%// Function to fetch current position of the moving rectangle.
function ROIPos = GetROIPosition(hROI)
ROIPos = round(getPosition(hROI));
axes(handles.axesIm2)
hRect = findobj('Type','rectangle');
delete(hRect)
rectangle('Position',ROIPos,'EdgeColor','y','LineWidth',2);
end
end
The figure after pressing the button:
And after moving the rectangle around:
Yay! Hope that helps! Nota that since you're using GUIDE the syntax of the callbacks will look a bit different but the idea is exactly the same.
Related
I am struggling with some algorithm to extract the region from an image which has the maximum change in pixels. I got the following image after preprocessing.
I did following steps of pre-processing
x = imread('test2.jpg');
gray_x = rgb2gray(x);
I = medfilt2(gray_x,[3 3]);
gray_x = I;
%%
canny_x = edge(gray_x,'canny',0.3);
figure,imshow(canny_x);
%%
s = strel('disk',3);
si = imdilate(canny_x,s);
%figure5
figure; imshow(si);
se = imerode(canny_x,s);title('dilation');
%figure6
figure; imshow(se);title('Erodsion');
I = imsubtract(si,se);
%figure7
figure; imshow(I);
Basically what I am struggling for, is to make weapon detection system using Image processing. I want to localize possible area's to be weapon so that I could feed them to my classifier to identify if it is a weapon or not. Any suggestions? Thank you
A possible solution could be:
Find corner points in the image (Harris corner points, etc)
Set value of all the corner points to white while remaining image will be black
Take a rectangular window and traverse it over the whole image
sum all the white pixels in that rectangular window
select that region whose sum is maximum of all regions
I have binary images and they have semi or less circles. My aim is to find these circles, make them whole circles and remove all other objects . I found this but it is for MATLAB R2013a. I am using R2011b and it doesn't have the function centers = imfindcircles(A,radius).
How can I do that in MATLAB version R2011b?
Images:
Edit:
My aim is to get whole circle. I show this below for the last image.
Too bad about imfindcircles! One thing I can suggest is to invoke regionprops and specify the 'Area' and 'BoundingBox' flags. regionprops was available in MATLAB for as long as I can remember, so we can certainly use it here.
What this will do is that whatever distinct objects that are seen in the image that are connected, we will find both their areas and their bounding boxes that bound them. After you do this, threshold on the area so that any objects that have a very large area most likely contain circles of interest. Bear in mind that I'm only assuming that you have circles in your image. Should you have any objects that have a large area, this method will extract those out too.
As such, let's read in your image directly from Stack Overflow. When you uploaded the image, it's a RGB image, so I'll have to convert to binary:
im = imread('http://i.stack.imgur.com/wQLPi.jpg');
im_bw = im2bw(im);
Next, call regionprops:
s = regionprops(im_bw, 'Area', 'BoundingBox');
Now, collect all of the areas, and let's take a look at all of the unique areas of all objects seen in this image:
areas = [s.Area].';
unique(areas)
ans =
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
19
20
23
24
25
28
29
38
43
72
73
85
87
250
465
3127
If you take a look at the very end, you'll see that we have an object that has 3127 pixels in it. This probably contains our circle. As such, let's pick out that single element that contains this object:
s2 = s(areas == 3127);
In general, you'll probably have more than one circle in your image, so you should threshold the area to select those potential circles. Something like:
s2 = s(areas > 2000);
Now, let's create a new blank image that is the same size as the original image, then simply use the BoundingBox property to extract out the area that encompasses the circle in the original image and copy it over to the same location in the output image. The BoundingBox field is structured in the following way:
[x y w h]
x and y are the top-left corner of the bounding box. x would be the column and y would be the row. w and h are the width and height of the bounding box. As such, we can use this directly to access our image and copy those pixels over into the output image.
out = false(size(im_bw));
bb = floor(s2.BoundingBox); %// Could be floating point, so floor it
out(bb(2):bb(2)+bb(4)-1, bb(1):bb(1)+bb(3)-1) = im_bw(bb(2):bb(2)+bb(4)-1, bb(1):bb(1)+bb(3)-1);
This is what I get:
What you should probably do is loop over the circles in case we have more than one. The above code assumes that you detected just one circle. Therefore, do something like this:
out = false(size(im_bw));
for idx = 1 : numel(s2) %// For each potential circle we have...
bb = floor(s2(idx).BoundingBox); %// Could be floating point, so floor it
%// Copy over pixels from original bw image to output
out(bb(2):bb(2)+bb(4)-1, bb(1):bb(1)+bb(3)-1) = im_bw(bb(2):bb(2)+bb(4)-1, bb(1):bb(1)+bb(3)-1);
end
A small thing to note is that the bounding box encompasses the entire object, but there could also be some noisy pixels that are disconnected that are within that bounding box. You may have to apply some morphology to get rid of those pixels. A binary opening could suffice.
Here's what I get with your other images. I thresholded the area to search for those that have 2000 pixels or more (I did this above):
Just for self-containment and your copy-and-pasting pleasure, here's the code in one segment:
clear all;
close all;
%im = imread('http://i.stack.imgur.com/qychC.jpg');
%im = imread('http://i.stack.imgur.com/wQLPi.jpg');
im = imread('http://i.stack.imgur.com/mZMBA.jpg');
im_bw = im2bw(im);
s = regionprops(im_bw, 'Area', 'BoundingBox');
areas = [s.Area].';
s2 = s(areas > 2000);
out = false(size(im_bw));
for idx = 1 : numel(s2) %// For each potential circle we have...
bb = floor(s2(idx).BoundingBox); %// Could be floating point, so floor it
%// Copy over pixels from original bw image to output
out(bb(2):bb(2)+bb(4)-1, bb(1):bb(1)+bb(3)-1) = im_bw(bb(2):bb(2)+bb(4)-1, bb(1):bb(1)+bb(3)-1);
end
imshow(out);
All three images are there in the code. You just have to uncomment whichever one you want to use, comment out the rest, then run the code. It will display an image with all of your detected circles.
Edit
You would like to draw complete circles, instead of extracting the shape themselves. That isn't a problem to do. All you need to do is determine the best "radii" that can be enclosed inside each of the bounding boxes. This is simply the maximum of the width and height of each bounding box, then divide these quantities by 2.
After, create a 2D grid of co-ordinates through meshgrid that is the same size as the original image itself, then create a binary image such that the Euclidean distance between the centre of this bounding box with any point in this 2D grid less than the radius is set to logical true while the other positions are set to logical false.
In other words, do this:
clear all;
close all;
im = imread('http://i.stack.imgur.com/qychC.jpg');
%im = imread('http://i.stack.imgur.com/wQLPi.jpg');
%im = imread('http://i.stack.imgur.com/mZMBA.jpg');
im_bw = im2bw(im);
s = regionprops(im_bw, 'Area', 'BoundingBox');
areas = [s.Area].';
s2 = s(areas > 2000);
out = false(size(im_bw));
for idx = 1 : numel(s2) %// For each potential circle we have...
bb = floor(s2(idx).BoundingBox); %// Could be floating point, so floor it
%// Copy over pixels from original bw image to output
out(bb(2):bb(2)+bb(4)-1, bb(1):bb(1)+bb(3)-1) = im_bw(bb(2):bb(2)+bb(4)-1, bb(1):bb(1)+bb(3)-1);
end
figure;
imshow(out);
%// Image that contains all of our final circles
out2 = false(size(im_bw));
[X,Y] = meshgrid(1:size(im_bw,2), 1:size(im_bw,1)); %// Find a 2D grid of co-ordinates
for idx = 1 : numel(s2) %// For each circle we have...
bb = floor(s2(idx).BoundingBox); %// Could be floating point, so floor it
cenx = bb(1) + (bb(3) / 2.0); %// Get the centre of the bounding box
ceny = bb(2) + (bb(4) / 2.0);
radi = max(bb(3), bb(4)) / 2; %// Find the best radius
tmp = ((X - cenx).^2 + (Y - ceny).^2) <= radi^2; %// Draw our circle and place in a temp. image
out2 = out2 | tmp; %// Add this circle on top of our output image
end
figure;
imshow(out2);
This script now shows you the original extracted shapes, and the best "circles" that describes these shapes in two separate figures. Bear in mind that this is a bit different than what I showed you previously with one circle. What I have to do now is allocate a blank image, then incrementally add each circle to this new image. For each circle, I create a temporary binary image that has just a circle I'm looking for, then I add this on top of the new image. At the end, we will show all of the circles in the image that are fully drawn as you desire.
This is what I get for the best circle for each of your images:
Good luck!
I'm working on a gui and using GUIDE. It loads and image and has the user draw an ROI around a point (the particle ROI). I would then like to have two sliders for creating a second ROI (the Scan ROI) where the user can use sliders to set the width and height of the second roi and see it updated on the image. The sliders seem to work ok but my gui keeps drawing a new roi on top of the image so it gets messy looking really fast. I would like to remove the user sizeable roi from the image before redrawing it (while still keeping the original particle ROI on the image. I currently do it the following way :
Inside the callback for the setroi size button (this should be for the particel ROI)
handles=guidata(hObject);
particleroiSize=imrect;% - draw a rectagle around the particle to get a meausr eof ROI size
roiPoints=getPosition(particleroiSize); %-get tha parameters fo the rectanlge
partX1 = round(roiPoints(1));
partY1 = round(roiPoints(2));
partX2 = round(partX1 + roiPoints(3));
partY2 = round(partY1 + roiPoints(4)); % these are the ROi positions in pixels
roiHeight = round(roiPoints(3)); % - these are just the ROI width and height
roiWidth = round(roiPoints(4));
handles=guidata(hObject); %_ update all the handles...
handles.partX1=partX1;
handles.partX2=partX2;
handles.partY1=partY1;
handles.partY2=partY2;
handles.roicenterX = (partX1 + round(roiPoints(3))/2);
handles.roicenterY= (partY1 + round(roiPoints(4))/2);
handles.roiHeight = roiHeight;
handles.roiWidth = roiWidth;
current_slice = round(get(handles.Image_Slider,'Value'));
particleImage=handles.Image_Sequence_Data(partY1:partY2,partX1:partX2,current_slice);
handles.particleImage=particleImage;
set(handles.RoiSizeDisplay,'String',strcat('Particle ROI is ',' ',num2str(roiHeight),' ', ' by ',num2str(roiWidth)) );
guidata(hObject,handles);
And then inside the call back for the sliders that set the Scan ROI size I have (this is inside two different sliders one adjusts the width and one the height :
handles=guidata(hObject);
try
delete(handles.ScanArea);
% plus any cleanup code you want
catch
end
WidthValue = get(handles.ScanAreaSliderWidth,'value');
HeightValue = get(handles.ScanAreaSliderHeight,'value');
set(handles.ScanAreaWidthDisplay,'String',strcat('Scan Area Width is ',' ', num2str(WidthValue))); % sets the display..now to do the drawing...
%h = imrect(hparent, position);
%position = [Xmin Ymin Width Heigth];
position = [ round(handles.roicenterX-WidthValue/2) round(handles.roicenterY-HeightValue/2) WidthValue HeightValue];
handles.ScanArea = imrect(handles.Image_Sequence_Plot,position);
%h = imrect(hparent, position)
handles=guidata(hObject);
guidata(hObject, handles);
But it never deletes the scan area ROI and keeps redrawign over it..I thought the try...catch would work but it doens't seem to. Am I making extra copies of the ROI or something? Please help..
Thanks.
If you need to delete the ROI drawn with imrect, you can use findobj to look for rectangle objects (which are of type "hggroup") and delete them:
hfindROI = findobj(gca,'Type','hggroup');
delete(hfindROI)
and that should do it. Since you first draw particleroiSize, which is of the hggroup type as well, you might not want to delete all the outputs from the call to findobj. If there are multiple rectangles in your current axis, then hfindROI will contain multiple entries. As such you might want to delete all of them but the first one, which corresponds to particleroiSize.
I hope i got your question right. If not please ask for clarifications!
Thanks. This worked perfectly except that I had to use
hfindROI = findobj(handles.Image_Sequence_Plot,'Type','hggroup');
delete(hfindROI(1:end-1))
to get rid of everything but the first ROI, so I guessteh hggoup objects are added at the start ? (I thought I would use deleted(hfindROI(2:end)) to delete all but the first. Also, why does hfindROI return a list of numbers? Do they represent the hggroup objects or something like that?
thanks..
Is there any way to constraint the the imline to be always perpendicular to the other imline drawn on the same object. for ex. I draw a first line using "imline" now I want to draw second line across the first line to be perpendicular to it. if there is a way to force the second imline to be perpendicular to the first line keeping the flexibility of extending the length it will solve my problem some extent.
I want something like a flexible cross hair(which can rotate along the axis and have flexible sides) on my image to measure the height and width of the certain object.
Code:
function perpline()
imshow(rand(200),[]);
line1 = imline(gca,[50 50; 150 150]);
setColor(line1,'r');
line2 = imline(gca,[50 150; 150 50]);
setColor(line2,'g');
addNewPositionCallback(line2,#(pos)callback_line(pos));
function callback_line(pos)
% Must update line1 based on line2's position
pos_line1 = getPosition(line1);
pos_line2 = getPosition(line2);
% Get middle
pos_center = [(pos_line2(1,1)+pos_line2(2,1))/2 (pos_line2(1,2)+pos_line2(2,2))/2];
% Find displacement
vec_disp = [pos_line2(2,1)-pos_line2(1,1) pos_line2(2,2)-pos_line2(1,2)];
% Get normal unit vector
vec_perp = [-vec_disp(2) vec_disp(1)]/norm(vec_disp);
% Preserve length of line2
length_line1 = norm([pos_line1(2,1)-pos_line1(1,1) pos_line1(2,2)-pos_line1(1,2)]);
pos_line1_update = [-vec_perp*length_line1/2+pos_center;
vec_perp*length_line1/2+pos_center];
% Set position
setPosition(line1,pos_line1_update);
end
end
Save it as a function then call it. You can drag the green line around and the red line remains perpendicular. Note that you have to define how you want it to preserve the perpendicularity. I chose to preserve the length of the red line and keep it in the center of the green line.
By following this post Using ismember with the output of regionprops I am able to selectively isolate the connected component I want. For example using my code below:
img = rgb2gray(imread('W1\Writer1_01_02.jpg'));
bw_normal2 = im2bw(img, graythresh(img));
bw22 = imcomplement(bw_normal2);
bw3 = bwmorph(bw22, 'dilate');
[label2,n2] = bwlabel(bw3);
stats2 = regionprops(label2, {'Area', 'BoundingBox'});
area2 = [stats2.Area];
idx = find((28 <= area2) & (area2 <= 40));
BW2 = ismember(label2,idx);
figure, imshow(BW2)
I can easily display the output that contains ONLY the connected component whose area is between 28 and 40. As so
But instead can I make a bounding box around this connected component in the original image. I mean if this is the original image:
Can I make a bounding box around my desired component on the original image? I know that this is the code for making a bounding box around all of my connected components
imshow(img);
for j=1:n2
hold on
rectangle('Position',[stats(j).BoundingBox(1),stats(j).BoundingBox(2),stats(j).BoundingBox(3),stats(j).BoundingBox(4)],...
'EdgeColor','r','LineWidth',2 );
end
But how do I only make a bounding box only around the element having area between 28 and 40? Rather than producing a completely different image as shown above.
Keep a if condition in your second half of the code...
imshow(img);
for j=1:n2
hold on
area2 = stats2(j).Area;
if((28 <= area2) & (area2 <= 40))
rectangle('Position',[stats2(j).BoundingBox(1),stats2(j).BoundingBox(2),stats2(j).BoundingBox(3),stats2(j).BoundingBox(4)],...'EdgeColor','r','LineWidth',2 );
end
end