i am trying to apply SURF algorithm, which is the algo to find the key points and matches corresponding two images, but the problem is that in my case , i want to apply that algo with two images, but they are of different dimension and so it fails to work, could you please tell what shall i do so that both images become equal dimensions.
That depends on your problem. If the images have the same resolution, I suggest you either crop one image or extend one image (with zeros?) to match the number of pixels.
Otherwise I suggest you interpolate the images so that they are defined at the same points, for example with interp2.
Can you give a minimum working code example of what you are trying to do?
Edit:
The code example you gave starts with loading to images.
% Example 2, Corresponding points
% Load images
I1=imread('TestImages/lena1.png');
I2=imread('TestImages/testc2.png');
Here are a few examples of how you can make the images match. You have to decide which one of these makes sense in your case.
Method 1: crop both images
nrows = min(size(I1,1), size(I2,1));
ncols = min(size(I1,2), size(I2,2));
% The + floor( ... ) is only for centering the bounding box
croppedI1 = I1( (1:nrows) + floor((size(I1,1)-nrows)/2), ...
(1:ncols)+floor((size(I1,2)-ncols)/2), ...
:);
croppedI2 = I2( (1:nrows) + floor((size(I2,1)-nrows)/2, ...
(1:ncols) + floor((size(I2,2)-ncols)/2), ...
:);
I1=croppedI1;
I2=croppedI2;
Method 2: Extend the images with zeros until they match
nrows = max(size(I1,1), size(I2,1));
ncols = max(size(I1,2), size(I2,2));
nchannels = size(I1,3);
extendedI1 = [ I1, zeros(size(I1,1), ncols-size(I1,2), nchannels); ...
zeros(nrows-size(I1,1), ncols, nchannels)];
extendedI2 = [ I2, zeros(size(I2,1), ncols-size(I2,2), nchannels); ...
zeros(nrows-size(I2,1), ncols, nchannels)];
I1=extendedI1;
I2=extendedI2;
Method 3: Scale the second image to the size of the first one (which destroys the aspect ratio)
% Scale the coordinates so that they range from 0 to 1 each.
[X1, Y1]=meshgrid( linspace(0, 1, size(I1,2)), linspace(0, 1, size(I1,1)));
[X2, Y2]=meshgrid( linspace(0, 1, size(I2,2)), linspace(0, 1, size(I2,1)));
nchannels = size(I1,3);
% interpolate each color plane separately
for k=1:nchannels
scaledI2(:,:,k)=interp2(X2, Y2, double(I2(:,:,k)), X1, Y1);
end
%I1=I1;
I2=scaledI2;
% code for to play the images or to take same size images into array
r=0;c=0;
a1=imread(filename1);
[r c]=size(a1);
b=imread(filename2);
a2= imresize(b, [r c/3]);
% re-sized second image according to 1st one if u have specific
row col number then you can give like that
a=cat(4,a1,a2); % "a" array having a1 a2 images
implay(a,framerate ); % to play images
Related
This question already has answers here:
MATLAB: Drawing a line over a black and white image
(5 answers)
Closed 4 years ago.
I have a set of points that I want to connect sequentially. Suppose the points are (A1,A2,A3,...A9); I want to connect A1 to A2, A2 to A3 and so on and finally connect A9 to A1.
All I need is to know a function that would help me connect A1 to A2, I could do the rest using for loops.
I know connecting two points is a question that has been asked here several times before but I couldn't find the answer I required. Several of the solutions suggest using "plot" and "line" but these functions overlay the results on the image and don't actually make any changes to the original image.
I did try them out and managed to save the resulting figure using the "saveas" and "print" functions but the image doesn't get saved in the proper format and there are a lot of problems using the parameters for these functions. Besides, I don't really want to save the image, it's just an unnecessary overhead I was willing to add if I could get the desired image with the lines.
I've also tried "imline" to draw lines but it seems to be interactive.
This particular question reflects my problem perfectly but when I ran the code snippets given as solutions, all of them gave a set of dots in the resulting image.
I tried the above mentioned codes in the link with this image that I found here.
A dotted line was an output for all three code snippets in the link above.
For example, I ran the first code like this:
I = imread('berries_copy.png');
grayImage=rgb2gray(I);
img =false(size(grayImage,1), size(grayImage,2));
I wrote the above piece of code just to get a black image for the following operations:
x = [500 470]; % x coordinates
y = [300 310]; % y coordinates
nPoints = max(abs(diff(x)), abs(diff(y)))+1; % Number of points in line
rIndex = round(linspace(y(1), y(2), nPoints)); % Row indices
cIndex = round(linspace(x(1), x(2), nPoints)); % Column indices
index = sub2ind(size(img), rIndex, cIndex); % Linear indices
img(index) = 255; % Set the line points to white
imshow(img); % Display the image
This is the resulting image for the above code as well as the other two, which as you can see, is just a few points on a black background which isn't the desired output.
I changed the code and used the "plot" function for the same to get this output which is what I want. Is there anyway I can change the dotted output into a solid line?
Or if could anyone suggest a simple function or a method that would draw a line from A1 to A2 and would actually make a change in the input image, I'd be grateful. (I really hope this is just me being a novice rather than Matlab not having a simple function to draw a line in an image.)
P.S. I don't have the Computer Vision toolbox and if possible, I'd like to find a solution that doesn't involve it.
Your first problem is that you are creating a blank image the same size as the image you load with this line:
img =false(size(grayImage,1), size(grayImage,2));
When you add the line to it, you get a black image with a white line on it, as expected.
Your second problem is that you are trying to apply a solution for grayscale intensity images to an RGB (Truecolor) image, which requires you to modify the data at the given indices for all three color planes (red, green, and blue). Here's how you can modify the grayscale solution from my other answer:
img = imread('berries_copy.png'); % Load image
[R, C, D] = size(img); % Get dimension sizes, D should be 3
x = [500 470]; % x coordinates
y = [300 310]; % y coordinates
nPoints = max(abs(diff(x)), abs(diff(y)))+1; % Number of points in line
rIndex = round(linspace(y(1), y(2), nPoints)); % Row indices
cIndex = round(linspace(x(1), x(2), nPoints)); % Column indices
index = sub2ind([R C], rIndex, cIndex); % Linear indices
img(index) = 255; % Modify red plane
img(index+R*C) = 255; % Modify green plane
img(index+2*R*C) = 255; % Modify blue plane
imshow(img); % Display image
imwrite(img, 'berries_line.png'); % Save image, if desired
And the resulting image (note the white line above the berry in the bottom right corner):
You can use the Bresenham Algorithm. Of course it has been implemented and you can find it here: Bresenham optimized for Matlab. This algorithm selects the pixels approximating a line.
A simple example, using your variable name could be:
I = rgb2gray(imread('peppers.png'));
A1 = [1 1];
A2 = [40 40];
[x y] = bresenham(A1(1),A1(2),A2(1),A2(2));
ind = sub2ind(size(I),x,y);
I(ind) = 255;
imshow(I)
You can use imshow to display a image and then use plot to plot lines and save the figure. Check the below code:
I = imread('peppers.png') ;
imshow(I)
hold on
[m,n,p] = size(I) ;
%% Get random points A1, A2,..A10
N = 9 ;
x = (n-1)*rand(1,N)+1 ;
y = (m-1)*rand(1,N)+1 ;
P = [x; y]; % coordinates / points
c = mean(P,2); % mean/ central point
d = P-c ; % vectors connecting the central point and the given points
th = atan2(d(2,:),d(1,:)); % angle above x axis
[th, idx] = sort(th); % sorting the angles
P = P(:,idx); % sorting the given points
P = [P P(:,1)]; % add the first at the end to close the polygon
plot( P(1,:), P(2,:), 'k');
saveas(gcf,'image.png')
I have a sequence of nFrames images from a video, and I have computed the SIFT keypoints and descriptors from all of them and stored them in two cell arrays. What I need to do now is to compute the vector between the 1st keypoint on image 1 and the corresponding keypoint on image 2 (the corresponding keypoint will be the one with the same -or most similar- descriptors), and do so for all other keypoints in order to plot a graph showing how the keypoints move on the scene, which reflects the movement of the objects on the scene.
The keypoints are stored as a 4-by-n array (the first row are the x components, the second row are the y components -the other 2 rows are the scale and angle, which I don't need-, and each column corresponds to a different keypoint), so I thought of subtracting the corresponding points from the first image to the second image, but wouldn't I end up having just another point in the middle of those two? How can I store the difference as a vector in order to plot all of them later on on the same graph?
At the moment all I have is this:
% Clear all and add all folders to the current path
clear; close all; clc;
addpath(genpath('.'));
% Set path, nFrames and threshold values
path = 'img/record_tennis';
d = dir([ path,'\*.png' ]);
nFrames = length( d( not([ d.isdir ]) ) );
th = 0.01;
step = 20;
keypts = cell(1,nFrames);
desc = cell(1,nFrames);
% Main loop
for i = 1:step:nFrames
disp([ 'Processing frame number ',num2str(i),' of ',num2str(nFrames),'...' ]);
% Read the current image
imgRGB = imread([ path,'/',d(i).name ]);
img = sum( double(imgRGB),3 ) / 3 / 255;
% Perform SIFT on the current image and plot the keypoints
[ keypts{1,i},desc{1,i} ] = sift( img,'Threshold',th );
imshow( img ), hold on
plot( keypts{1,i}(1,:),keypts{1,i}(2,:),'.' );
pause(1); clc;
end
% Remove empty cells
keypts = keypts( ~cellfun( #isempty,keypts ) );
desc = desc( ~cellfun( #isempty,desc ) );
With this I compute the SIFT keypoints and descriptors from each frame (I have added a step value because the movement is very small and this way it is more noticeable between consecutive images). Then I remove the empty cells due to the step factor, and now I have to compute the vector between corresponding keypoints, but I am stuck with that.
In general, if you want the vector that goes from one set of points to another, you can simply subtract the two (as you mentioned). This will provide you a vector that is [dx, dy]. In order to display that vector field, you could then use quiver which will draw each of these vectors starting from the initial point. The inputs to quiver are:
quiver(initial_x, initial_y, dx, dy, scale)
So if we look at your variables, we can construct each of these variables from your keypts variable.
initial_x = keypts{1}(1,:);
initial_y = keypts{1}(2,:);
dx = keypts{current_frame}(1,:) - initial_x;
dy = keypts{current_frame}(2,:) - initial_y;
Now you should be able to plot this on top of your image to visualize the motion field.
hax = axes();
imshow(img, 'Parent', hax);
hold(hax, 'on')
q = quiver(initial_x, initial_y, dx, dy, 1);
Now, this requires that the points within keypts actually have correspondence between different frames (i.e. keypts{1}(:,1) is the same physical point as keypts{2}(:,1)). This may or may not be true depending on the function that you are calling.
I need to create an nth-order Hadamard matrix, row double it, within each row randomly permute the elements of the matrix, and then display it. So far, I have accomplished all of these things. What I end up with when I imshow(matrix) is a nice picture of black and white boxes. But I haven't figured out how to insert a fine line to divide each row. I can create something like the first image on the left, but not the image on the right (these are Figures 1 and 2 from this paper)
Any help or comments would be thoroughly appreciated.
I've found using vector approaches (e.g., patch and rectangle) for this sort of problem unnecessarily challenging. I think that it's more straightforward to build a new image. This avoids floating-point rounding issues and other things that crop up with vector graphics. My solution below relies on some functions in the Image Processing Toolbox, but is simple and fast:
% Create data similarly to #TryHard
H = hadamard(48);
C = (1+[H;-H])/2;
rng(0); % Set seed
C(:) = C(randperm(numel(C))); % For demo, just permute all values, not rows
% Scale image and lines
scl = 10; % Amount to vertically scale each row
pad = 2; % Number of pixels to add between each row
C = imresize(C,scl,'nearest');
C = blockproc(C,[scl size(C,2)],#(x)[x.data;zeros(pad,size(C,2))]);
C = C(1:end-pad,:); % Remove last line added
% Dispay image
imshow(C)
This results in an image like this
The scl and pad parameters can be easily adjusted to obtain different sizes and relative sizes. You can call imresize(...,'nearest') again after adding the lines to further scale the image if desired. The blocproc line could potentially be made more efficient with various options (see the help). It could also be replaced by calls to im2col and col2im, which possibly could be faster, if messier.
I did not try the code, but I think that something like that should work:
sizeOfACube = 6;
numberOfRows = 47;
RGB = imread('image.png');
RGB = imresize(A, [(numRows+numberOfRows) numCols]);
for i=1:1:NumberOfRows
RGB(i*6,:,:) = 0;
end
imagesc(RGB);
imwrite(RGB,'newImage.png');
with:
sizeOfAcube the size of one cube on the QRcode.
numRows and numCols the number of Rows and Column of the original image.
One solution is to use patches, for instance as follows:
% set up example array
xl = 24; yl = xl;
[X Y] = find(hadamard(xl)==1);
% generate figure
figure, hold on
for ii=1:length(X)
patch(X(ii) + [0 0 1 1],Y(ii) + [0.1 0.9 0.9 0.1],[1 1 1],'Edgecolor',[1 1 1])
end
axis([0 xl+1 0 yl+1])
axis('square')
The patch command patch(x,y, color) accepts the vertices of the polygon element as x and y. In this example you can modify the term [0.1 0.9 0.9 0.1] to set the thickness of the bounding black line.
This generates
Edited
For the particular instance provided by the OP:
H=Hadamard(48); %# now to row-double the matrix
A=(1+H)/2;
B=(1-H)/2;
C=[A; B]; %# the code below randomly permutes elements within the rows of the matrix
[nRows,nCols] = size(C);
[junk,idx] = sort(rand(nRows,nCols),2); %# convert column indices into linear indices
idx = (idx-1)*nRows + ndgrid(1:nRows,1:nCols); %# rearrange whatever matrix
E = C;
E(:) = E(idx);
[X Y] = find(logical(E));
xl = length(X);
yl = length(Y);
figure, hold on
for ii=1:xl
rectangle('Position',[X(ii) Y(ii)+.2 1 0.8],'facecolor',[1 1 1],'edgecolor',[1 1 1])
end
axis([0 max(X)+1 0 max(Y)+1])
axis('square')
set(gca,'color',[0 0 0])
set(gca,'XTickLabel',[],'YTickLabel',[],'XTick',[],'YTick',[])
This example uses rectangle instead of patch to generate sharp corners.
The image:
Currently, I am using the code below to segment an image into a grid of cellSizeX pixels times cellSizeY pixels:
grid = zeros(cellSizeX, cellSizeY, ColorCount, cellTotalX, cellTotalY);
for i = 1:cellSizeX:(HorRowCount)
for j = 1:cellSizeY:(VertColumnCount)
try
grid(:,:,:,icount, jcount) = img(i:i+cellSizeX-1, j:j+cellSizeY-1, :);
catch
end
jcount = jcount + 1;
end
icount = icount + 1;
jcount = 1;
end
While this code runs fine and satisfactorily, there are things that nag me:
Via some testing with tic and toc, comparing switching index positions such as grid(:,:,:,icount,jcount) and grid(icount,jcount,:,:,:), I see that grid(:,:,:,icount,jcount) is fastest. But can anything be improved here?
The code will work only if the requested cellSizeX and cellSizeY are proportional to the image img. So requesting cellSizeX and cellSizeY of 9 x 9 on image with size 40 x 40 will result in matlab complaining about exceeding matrix's dimension. Any suggestion regarding this? I do not want to simply fill in blank area for those cells. These cells will be used further in Vlfeat SIFT.
How about converting the image into a cellarray with each cell of size CellSizeX x CellSizeY x ColorCount, then stacking all these cells to a single array grid?
ca = mat2cell( img, cellSizeY * ones(1, cellTotalY), ...
cellSizeX * ones(1, cellTotalX), ...
ColorCount );
grid = reshape( cat( 4, ca{:} ),...
cellSizeX, cellSizeY, ColorCount, cellTotalX, cellTotalY);
It is accustomed in the image processing community to pad image with non-zero values depending on the values of the image at the boundary. Look at the function padarray for more information. You may pad your input image such that its padded size will be proportional to CellSizeX and CellSizeY (padding does not have to be identical at both axes).
I am doing vlfeat in Matlab and I am following this question here.
These below are my simple testing images:
Left Image:
Right Image:
I did a simple test with 2 simple images here (the right image is just rotated version of the left), and I got the result accordingly:
It works, but I have one more requirement, which is to match the SIFT points of the two images and show them, like this:
I do understand that vl_ubcmatch returns 2 arrays of matched indices, and it is not a problem to map them for which point goes to which point on two images. However, I am currently stuck in matlab's procedure. I found this. But that only works if the subplot stays that way. When you add an image into the subplot, the size changes and the normalization failed.
Here is my code: (im and im2 are images. f, d and f2, d2 are frames and descriptors from vl_sift function from 2 images respectively)
[matches score] = vl_ubcmatch(d,d2,threshold);%threshold originally is 1.5
if (mode >= 2)%verbose 2
subplot(211);
imshow(uint8(im));
hold on;
plot(f(1,matches(1,:)),f(2,matches(1,:)),'b*');
subplot(212);
imshow(uint8(im2));
hold on;
plot(f2(1,matches(2,:)),f2(2,matches(2,:)),'g*');
end
if (mode >= 3)%verbose 3
[xa1 ya1] = ds2nfu( f(1,matches(1,:)), f(2,matches(1,:)));
[xa2 ya2] = ds2nfu( f2(1,matches(2,:)), f2(2,matches(2,:)));
for k=1:numel(matches(1,:))
xxa1 = xa1(1, k);
yya1 = ya1(1, k);
xxa2 = xa2(1, k);
yya2 = ya2(1, k);
annotation('line',[xxa1 xxa2],[yya1 yya2],'color','r');
end
end
The code above yields this:
I think subplot isn't a good way to go for something like this. Is there a better method for this in Matlab? If possible, I want something like an empty panel that I can draw my image, draw lines freely and zoom freely, just like drawing 2D games in OpenGL style.
From zplesivcak's suggestion, yes, it is possible, and not that problematic after all. Here is the code:
% After we have applied vl_sift with 2 images, we will get frames f,f2,
% and descriptor d,d2 of the images. After that, we can apply it into
% vl_ubcmatch to perform feature matching:
[matches score] = vl_ubcmatch(d,d2,threshold); %threshold originally is 1.5
% check for sizes and take longest width and longest height into
% account
if (size(im,1) > size(im2,1))
longestWidth = size(im,1);
else
longestWidth = size(im2,1);
end
if (size(im,2) > size(im2,2))
longestHeight = size(im,2);
else
longestHeight = size(im2,2);
end
% create new matrices with longest width and longest height
newim = uint8(zeros(longestWidth, longestHeight, 3)); %3 cuz image is RGB
newim2 = uint8(zeros(longestWidth, longestHeight, 3));
% transfer both images to the new matrices respectively.
newim(1:size(im,1), 1:size(im,2), 1:3) = im;
newim2(1:size(im2,1), 1:size(im2,2), 1:3) = im2;
% with the same proportion and dimension, we can now show both
% images. Parts that are not used in the matrices will be black.
imshow([newim newim2]);
hold on;
X = zeros(2,1);
Y = zeros(2,1);
% draw line from the matched point in one image to the respective matched point in another image.
for k=1:numel(matches(1,:))
X(1) = f(1, matches(1, k));
Y(1) = f(2, matches(1, k));
X(2) = f2(1, matches(2, k)) + longestHeight; % for placing matched point of 2nd image correctly.
Y(2) = f2(2, matches(2, k));
line(X,Y);
end
Here is the test case:
By modifying the canvas width and height of one of the images from the question, we see that the algorithm above will take care of that and display the image accordingly. Unused area will be black. Furthermore, we see that the algorithm can match the features of two images respectively.
EDIT:
Alternatively, suggested by Maurits, for cleaner and better implementation, check out Lowe SIFT matlab wrappers.
If you have Matlab Computer Vision Library installed on your disc already, you can simply use
M1 = [f(1, match(1, :)); f(2, match(1, :)); ones(1, length(match))];
M2 = [f2(1, match(2, :)); f2(2, match(2, :)); ones(1, length(match))];
showMatchedFeatures(im,im2,[M1(1:2, :)]',[M2(1:2, :)]','montage','PlotOptions',{'ro','g+','b-'} );