I would like to align video frames to center and reorient an animal walking across the frames. I binarized each frame and fit an ellipse to the animal's body.
Binarized with ellipse fit
I used regionprops to find the ellipse, which outputs orientation from -90 to 90 degrees. I rotate each frame depending on the difference between orientation and the desired alignment orientation. The problem I'm having is that sometimes the orientation is aligned to the head and sometimes to the tail, so the alignment is occasionally 180 degrees flipped.
Upright position
Flipped position
I think this can be resolved by putting the orientation output in 0-360 space. Does anyone have a solution for this?
Here is my code:
for i = 1000
frame = read(v,i);
BW = im2bw(frame,0.95);
imshowpair(frame,BW,'montage')
% Extract the maximum area
BW = imclearborder(BW);
BW = bwareafilt(BW,1);
% Calculate centroid, orientation and major/minor axis length of the ellipse
s = regionprops(BW,{'Centroid','Orientation','MajorAxisLength','MinorAxisLength'});
centroid(i,:) = s.Centroid;
orientation(i) = s.Orientation;
fixed_orientation(i) = s.Orientation;
delta_angle(i) = -90 - orientation(i);
if i >=2
if diff(sign( fixed_orientation(i-1:i) )) == 2 && abs(fixed_orientation(i-1))-abs(fixed_orientation(i))>=20
fixed_orientation(i) = -fixed_orientation(i);
delta_angle(i) = -(-90 - fixed_orientation(i));
end
end
% Calculate the ellipse line
theta = linspace(0,2*pi);
col = (s.MajorAxisLength/2)*cos(theta);
row = (s.MinorAxisLength/2)*sin(theta);
M = makehgtform('translate',[s.Centroid, 0],'zrotate',deg2rad(-1*s.Orientation));
D = M*[col;row;zeros(1,numel(row));ones(1,numel(row))];
% Visualize the result
figure
imshow(BW)
hold on
plot(D(1,:),D(2,:),'r','LineWidth',2)
end
figure
for i = 1:1000
rotate_filler = imrotate(read(v,i), delta_angle(i), 'crop');
rotated_image(:,:,:,i) = rotate_filler;
imshowpair(read(v,i), rotated_image(:,:,:,i), 'montage')
pause(0.001)
end
Any suggestions are appreciated. Thanks!
Related
I have detected a circle as shown below:
As later I want to detect what speed limit is in the detected sign, how do I crop it out so that I am left with an image like below?
When program finishes it, it shows me where the center is and the radii in terminal.
centers =
248.4873 170.4811
radii =
24.5024
I know how to use imcrop but how do I use the values that are returned instead of writing in them myself, as there might be more than 1 circle detected?
Code:
I = imread('p1.tif');
subplot(3,3,1); imshow(I); title('Original Image');
%sharpen edges
B = imsharpen(I);
subplot(3,3,2); imshow(B); title('sharpened edges');
%find circles
Img = im2bw(B(:,:,3));
minRad = 20;
maxRad = 90;
[centers, radii] = imfindcircles(Img, [minRad maxRad], ...
'ObjectPolarity','bright','sensitivity',0.87)
imagesc(Img);
viscircles(centers, radii,'Color','green');
Assuming you have one centre and one radius. This should do it
rect = [centers(1)-radii,centers(2)-radii,2*radii,2*radii]
I2 = imcrop(I,rect)
For multiple circles you can do this process for each of the circle returned in a loop.
I have created a synthetic image that consists of a circle at the centre of a box with the code below.
%# Create a logical image of a circle with image size specified as follows:
imageSizeY = 400;
imageSizeX = 300;
[ygv, xgv] = meshgrid(1:imageSizeY, 1:imageSizeX);
%# Next create a logical mask for the circle with specified radius and center
centerY = imageSizeY/2;
centerX = imageSizeX/2;
radius = 100;
Img = double( (ygv - centerY).^2 + (xgv - centerX).^2 <= radius.^2 );
%# change image labels from double to numeric
for ii = 1:numel(Img)
if Img(ii) == 0
Img(ii) = 2; %change label from 0 to 2
end
end
%# plot image
RI = imref2d(size(Img),[0 size(Img, 2)],[0 size(Img, 1)]);
figure, imshow(Img, RI, [], 'InitialMagnification','fit');
Now, i need to create a rectangular mask (with label == 3, and row/col dimensions: 1 by imageSizeX) across the image from top to bottom and at known angles with the edges of the circle (see attached figure). Also, how can i make the rectangle thicker than 1 by imageSizeX?. As another option, I would love to try having the rectangle stop at say column 350. Lastly, any ideas how I can improve on the resolution? I mean is it possible to keep the image size the same while increasing/decreasing the resolution.
I have no idea how to go about this. Please i need any help/advice/suggestions that i can get. Many thanks!.
You can use the cos function to find the x coordinate with the correct angle phi.
First notice that the angle between the radius that intersects the vertex of phi has angle with the x-axis given by:
and the x coordinate of that vertex is given by
so the mask simply needs to set that row to 3.
Example:
phi = 45; % Desired angle in degrees
width = 350; % Desired width in pixels
height = 50; % Desired height of bar in pixels
theta = pi-phi*pi/180; % The radius angle
x = centerX + round(radius*cos(theta)); % Find the nearest row
x0 = max(1, x-height); % Find where to start the bar
Img(x0:x,1:width)=3;
The resulting image looks like:
Note that the max function is used to deal with the case where the bar thickness would extend beyond the top of the image.
Regarding resolution, the image resolution is determined by the size of the matrix you create. In your example that is (400,300). If you want higher resolution simply increase those numbers. However, if you would like to link the resolution to a higher DPI (Dots per Inch) so there are more pixels in each physical inch you can use the "Export Setup" window in the figure File menu.
Shown here:
I want to draw a circle with perspective tansformation in Matlab. Here I have used sample of code to draw a circle. If I rotate this circle using perspective tansform this must be ellipse. I want to draw both axis rotation(X & Y) of a circle. Now I am struggled to draw a perspetive rotation of this circle.
I need to use radious of the circle, x axis roation angle and y axis roation angle as input parameters.
Rimg = zeros(600,600);
ri = 100;
xcentre = 300;
ycentre = 300;
for r = 0:ri
for rad = 0:(pi/720):(2*pi)
xi = round(xcentre+(r*cos(rad)));
yi = round(ycentre+(r*sin(rad)));
Rimg(xi,yi) = 1;
end
end
imshow(Rimg,[]);
The first image shows my circle and the second image shows the expected result. This is only rotated in x axis. But I need both axis rotation in a single image.
I have a movie file, in which I am interested in recording the movement of a point; center of a circular feature to be specific. I am trying to perform this using edge detection and corner detection techniques in Matlab.
To perform this, how do I specify a region of interest in the video? Is subplot a good idea?
I was trying to perform this using the binary masks as below,
hVideoSrc = vision.VideoFileReader('video.avi','ImageColorSpace', 'Intensity');
hEdge = vision.EdgeDetector('Method', 'Prewitt','ThresholdSource', 'Property','Threshold', 15/256, 'EdgeThinning', true);
hAB = vision.AlphaBlender('Operation', 'Highlight selected pixels');
WindowSize = [190 150];
hVideoOrig = vision.VideoPlayer('Name', 'Original');
hVideoOrig.Position = [10 hVideoOrig.Position(2) WindowSize];
hVideoEdges = vision.VideoPlayer('Name', 'Edges');
hVideoEdges.Position = [210 hVideoOrig.Position(2) WindowSize];
hVideoOverlay = vision.VideoPlayer('Name', 'Overlay');
hVideoOverlay.Position = [410 hVideoOrig.Position(2) WindowSize];
c = [123 123 170 170];
r = [160 210 210 160];
m = 480; % height of pout image
n = 720; % width of pout image
BW = ~poly2mask(c,r,m,n);
while ~isDone(hVideoSrc)
dummy_frame = step(hVideoSrc) > 0.5; % Read input video
frame = dummy_frame-BW;
edges = step(hEdge, frame);
composite = step(hAB, frame, edges); % AlphaBlender
step(hVideoOrig, frame); % Display original
step(hVideoEdges, edges); % Display edges
step(hVideoOverlay, composite); % Display edges overlayed
end
release(hVideoSrc);
but it turns out that the mask applied on frame is good only for the original video. The edge detection algorithm detects the edges those are masked by binary mask. How can I mask other features permanently and perform edge detection?
Is this what you mean?
BW = poly2mask(c,r,m,n);
frame = dummy_frame .* BW;
I have two images. One is the original, and the another is rotated.
Now, I need to discover the angle that the image was rotated. Until now, I thought about discovering the centroids of each color (as every image I will use has squares with colors in it) and use it to discover how much the image was rotated, but I failed.
I'm using this to discover the centroids and the color in the higher square in the image:
i = rgb2gray(img);
bw = im2bw(i,0.01);
s = regionprops(bw,'Centroid');
centroids = cat(1, s.Centroid);
colors = impixel(img,centroids(1),centroids(2));
top = max(centroids);
topcolor = impixel(img,top(1),top(2));
You can detect the corners of one of the colored rectangles in both the image and the rotated version, and use these as control points to infer the transformation between the two images (like in image registration) using the CP2TFORM function. We can then compute the angle of rotation from the affine transformation matrix:
Here is an example code:
%# read first image (indexed color image)
[I1 map1] = imread('http://i.stack.imgur.com/LwuW3.png');
%# constructed rotated image
deg = -15;
I2 = imrotate(I1, deg, 'bilinear', 'crop');
%# find blue rectangle
BW1 = (I1==2);
BW2 = imrotate(BW1, deg, 'bilinear', 'crop');
%# detect corners in both
p1 = corner(BW1, 'QualityLevel',0.5);
p2 = corner(BW2, 'QualityLevel',0.5);
%# sort corners coordinates in a consistent way (counter-clockwise)
p1 = sortrows(p1,[2 1]);
p2 = sortrows(p2,[2 1]);
idx = convhull(p1(:,1), p1(:,2)); p1 = p1(idx(1:end-1),:);
idx = convhull(p2(:,1), p2(:,2)); p2 = p2(idx(1:end-1),:);
%# make sure we have the same number of corner points
sz = min(size(p1,1),size(p2,1));
p1 = p1(1:sz,:); p2 = p2(1:sz,:);
%# infer transformation from corner points
t = cp2tform(p2,p1,'nonreflective similarity'); %# 'affine'
%# rotate image to match the other
II2 = imtransform(I2, t, 'XData',[1 size(I1,2)], 'YData',[1 size(I1,1)]);
%# recover affine transformation params (translation, rotation, scale)
ss = t.tdata.Tinv(2,1);
sc = t.tdata.Tinv(1,1);
tx = t.tdata.Tinv(3,1);
ty = t.tdata.Tinv(3,2);
translation = [tx ty];
scale = sqrt(ss*ss + sc*sc);
rotation = atan2(ss,sc)*180/pi;
%# plot the results
subplot(311), imshow(I1,map1), title('I1')
hold on, plot(p1(:,1),p1(:,2),'go')
subplot(312), imshow(I2,map1), title('I2')
hold on, plot(p2(:,1),p2(:,2),'go')
subplot(313), imshow(II2,map1)
title(sprintf('recovered angle = %g',rotation))
If you can identify a color corresponding to only one component it is easier to:
Calculate the centroids for each image
Calculate the mean of the centroids (in x and y) for each image. This is the "center" of each image
Get the red component color centroid (in your example) for each image
Subtract the mean of the centroids for each image from the red component color centroid for each image
Calculate the ArcTan2 for each of the vectors calculated in 4), and subtract the angles. That is your result.
If you have more than one figure of each color, you need to calculate all possible combinations for the rotation and then select the one that is compatible with the other possible rotations.
I could post the code in Mathematica, if you think it is useful.
I would take a variant to the above mentioned approach:
% Crude binarization method to knock out background and retain foreground
% features. Note one looses the cube in the middle
im = im > 1
Then I would get the 2D autocorrelation:
acf = normxcorr2(im, im);
From this result, one can easily detect the peaks, and as rotation carries into the autocorrelation function (ACF) domain, one can ascertain the rotation by matching the peaks between the original ACF and the ACF from the rotated image, for example using the so-called Hungarian algorithm.