How to align images based on a pixel in Matlab? - image

I have two similar images: [A] and [B] (please see images). They are offset in X and Y. How to align A over B, using an pixel from A as reference? In other words, locating the indicated pixel from A on B, and make A and B centralized in this pixel.
Thank you.
Final result make manually

you can do it manually:
img1 = 255-mean(imread('a1.png'),3);
img2 = 255-mean(imread('a2.png'),3);
subplot(221);imagesc(img1);axis image
[x1 y1] = ginput(1);
subplot(222);imagesc(img2);axis image
[x2 y2] = ginput(1);
x = x1-x2;
y = y1-y2;
T = maketform('affine',[1 0 x;0 1 y; 0 0 1]');
img2N = imtransform(img2,T,'xdata',[1 size(img1,2)],'ydata',[1 size(img1,1)]);
subplot(2,2,[3 4]);
imagesc(max(img1,img2N));axis image
for doing it automaticly, you can do this::
%size(img2) <= size(img1)
img1 = 255-mean(imread('a1.png'),3);
img2 = 255-mean(imread('a2.png'),3);
subplot(221);imagesc(img1);axis image
subplot(222);imagesc(img2);axis image
colormap(gray(256))
c = normxcorr2(img2,img1);
[y x] = find(c==max(c(:)));
y = y-size(img2,1);
x = x-size(img2,2);
T = maketform('affine',[1 0 x;0 1 y; 0 0 1]');
img2N = imtransform(img2,T,'xdata',[1 size(img1,2)],'ydata',[1 size(img1,1)]);
subplot(2,2,[3 4]);
imagesc(max(img1,img2N));axis image

I think what you want is image registration, which requires, in your case, at least 2 control points, because it's affine transformation without reflect. Given the similarity of those 2 images, I think it's easy to find another referral point. After that you can use imtransform or simply cp2tform to perform the registration.

You will need to fine tune the 'XData' and 'YData' properties but you could do this...
rgbA = imread('A.jpg'):
rgbB = imread('B.jpg');
alpha(.2)
image(rgbA,'XData',2)
alpha(.2)
hold on
image(rgbB,'XData',2)
alpha(.2)

Related

Interp2 of image with transformed coordinates

I have 2 greyscale images that i am trying to align using scalar scaling 1 , rotation matrix [2,2] and translation vector [2,1]. I can calculate image1's transformed coordinates as
y = s*R*x + t;
Below the resulting images are shown.
The first image is image1 before transformation,
the second image is image1 (red) with attempted interpolation using interp2 shown on top of image2 (green)
The third image is when i manually insert the pixel values from image1 into an empty array (that has the same size as image2) using the transformed coordinates.
From this we can see that the coordinate transformation must have been successful, as the images are aligned although not perfectly (which is to be expected since only 2 coordinates were used in calculating s, R and t) .
How come interp2 is not producing a result more similar to when i manually insert pixel values?
Below the code for doing this is included:
Interpolation code
function [transformed_image] = interpolate_image(im_r,im_t,s,R,t)
[m,n] = size(im_t);
% doesn't help if i use get_grid that the other function is using here
[~, grid_xr, grid_yr] = get_ipgrid(im_r);
[x_t, grid_xt, grid_yt] = get_ipgrid(im_t);
y = s*R*x_t + t;
yx = reshape(y(1,:), m,n);
yy = reshape(y(2,:), m,n);
transformed_image = interp2(grid_xr, grid_yr, im_r, yx, yy, 'nearest');
end
function [x, grid_x, grid_y] = get_ipgrid(image)
[m,n] = size(image);
[grid_x,grid_y] = meshgrid(1:n,1:m);
x = [reshape(grid_x, 1, []); reshape(grid_y, 1, [])]; % X is [2xM*N] coordinate pairs
end
The manual code
function [transformed_image] = transform_image(im_r,im_t,s,R,t)
[m,n] = size(im_t);
[x_t, grid_xt, grid_yt] = get_grid(im_t);
y = s*R*x_t + t;
ymat = reshape(y',m,n,2);
yx = ymat(:,:,1);
yy = ymat(:,:,2);
transformed_image = zeros(m,n);
for i = 1:m
for j = 1:n
% make sure coordinates are inside
if (yx(i,j) < m & yy(i,j) < n & yx(i,j) > 0.5 & yy(i,j) > 0.5)
transformed_image(round(yx(i,j)),round(yy(i,j))) = im_r(i,j);
end
end
end
end
function [x, grid_x, grid_y] = get_grid(image)
[m,n] = size(image);
[grid_y,grid_x] = meshgrid(1:n,1:m);
x = [grid_x(:) grid_y(:)]'; % X is [2xM*N] coordinate pairs
end
Can anyone see what i'm doing wrong with interp2? I feel like i have tried everything
Turns out i got interpolation all wrong.
In my question i calculate the coordinates of im1 in im2.
However the way interpolation works is that i need to calculate the coordinates of im2 in im1 such that i can map the image as shown below.
This means that i also calculated the wrong s,R and t since they were used to transform im1 -> im2, where as i needed im2 -> im1. (this is also called the inverse transform). Below is the manual code, that is basically the same as interp2 with nearest neighbour interpolation
function [transformed_image] = transform_image(im_r,im_t,s,R,t)
[m,n] = size(im_t);
[x_t, grid_xt, grid_yt] = get_grid(im_t);
y = s*R*x_t + t;
ymat = reshape(y',m,n,2);
yx = ymat(:,:,1);
yy = ymat(:,:,2);
transformed_image = zeros(m,n);
for i = 1:m
for j = 1:n
% make sure coordinates are inside
if (yx(i,j) < m & yy(i,j) < n & yx(i,j) > 0.5 & yy(i,j) > 0.5)
transformed_image(i,j) = im_r(round(yx(i,j)),round(yy(i,j)));
end
end
end
end

How to blur an image in one specific direction in Matlab?

I have an image and I would like to blur it in one specific direction and distance using Matlab.
I found out there is a filter called fspecial('motion',len,theta).
Here there is an example:
I = imread('cameraman.tif');
imshow(I);
H = fspecial('motion',20,45);
MotionBlur = imfilter(I,H,'replicate');
imshow(MotionBlur);
However the blurred picture is blurred in 2 directions! In this case 225 and 45 degrees.
What should it do in order to blur it just in a specific direction (e.g. 45) and not both?
I think you want what's called a "comet" kernel. I'm not sure what kernel is used for the "motion" blur, but I'd guess that it's symmetrical based on the image you provided.
Here is some code to play with that applies the comet kernel in one direction. You'll have to change things around if you want an arbitrary angle. You can see from the output that it's smearing in one direction, since there is a black band on only one side (due to the lack of pixels there).
L = 5; % kernel width
sigma=0.2; % kernel smoothness
I = imread('cameraman.tif');
x = -L:1.0:L;
[X,Y] = meshgrid(x,x);
H1 = exp((-sigma.*X.^2)+(-sigma.*Y.^2));
kernel = H1/sum((H1(:)));
Hflag = double((X>0));
comet_kernel = Hflag.*H1;
comet_kernel=comet_kernel/sum(comet_kernel(:));
smearedImage = conv2(double(I),comet_kernel,'same');
imshow(smearedImage,[]);
Updated code: This will apply an arbitrary rotation to the comet kernel. Note also the difference between sigma in the previous example and sx and sy here, which control the length and width parameters of the kernel, as suggested by Andras in the comments.
L = 5; % kernel width
sx=3;
sy=10;
theta=0;
I = imread('cameraman.tif');
x = -L:1.0:L;
[X,Y] = meshgrid(x,x);
rX = X.*cos(theta)-Y.*sin(theta);
rY = X.*sin(theta)+Y.*cos(theta);
H1 = exp(-((rX./sx).^2)-((rY./sy).^2));
Hflag = double((0.*rX+rY)>0);
H1 = H1.*Hflag;
comet_kernel = H1/sum((H1(:)))
smearedImage = conv2(double(I),comet_kernel,'same');
imshow(smearedImage,[]);
Based on Anger Density's answer I wrote this code that solves my problem completely:
L = 10; % kernel width
sx=0.1;
sy=100;
THETA = ([0,45,90,135,180,225,270,320,360])*pi/180;
for i=1:length(THETA)
theta=(THETA(i)+pi)*-1;
I = imread('cameraman.tif');
x = -L:1.0:L;
[X,Y] = meshgrid(x,x);
rX = X.*cos(theta)-Y.*sin(theta);
rY = X.*sin(theta)+Y.*cos(theta);
H1 = exp(-((rX./sx).^2)-((rY./sy).^2));
Hflag = double((0.*rX+rY)>0);
H1 = H1.*Hflag;
comet_kernel = H1/sum((H1(:)));
smearedImage = conv2(double(I),comet_kernel,'same');
% Fix edges
smearedImage(:,[1:L, end-L:end]) = I(:,[1:L, end-L:end]); % Left/Right edge
smearedImage([1:L, end-L:end], :) = I([1:L, end-L:end], :); % Top/bottom edge
% Keep only inner blur
smearedImage(L:end-L,L:end-L) = min(smearedImage(L:end-L,L:end-L),double(I(L:end-L,L:end-L)));
figure
imshow(smearedImage,[]);
title(num2str(THETA(i)*180/pi))
set(gcf, 'Units', 'Normalized', 'OuterPosition', [0 0 1 1]);
end

Constrain the drag of rectangular ROI (Matlab)

I have a simple program which replaces a selected region of one image with the corresponding region in another image. I am trying to use imrect() in conjunction with makeConstrainToRectFcn to select a rectangular ROI which cannot be extended beyond the boundaries of the image.
However, when I run the code, the ROI can initially be drawn to include the areas outside the image frame. This leads to the error: Index exceeds matrix dimensions.
Is there any way that the rectangle cannot be drawn outside the image from the outset? Alternatively, is it possible to ensure that the operation does not execute unless the rectangle is constrained within the axes limits?
Any suggestions would be greatly appreciated.
My code:
% Sample images:
X=imread('office_1.jpg');
Y=imread('office_5.jpg');
figure, imshow(X)
h = imrect;
api = iptgetapi(h);
fcn = makeConstrainToRectFcn('imrect',get(gca,'XLim'),...
get(gca,'YLim'));
api.setPositionConstraintFcn(fcn);
wait(h);
rect = getPosition(h);
x1 =rect(1);
x2 = x1 + rect(3);
y1 =rect(2);
y2 = y1 + rect(4);
Z = X; % Initialize
Z(y1:y2, x1:x2, :) = Y(y1:y2, x1:x2, :);
imshow(Z)
This should do the job:
% Sample images:
X = imread('office_1.jpg');
Y = imread('office_5.jpg');
% Show image X:
figure, imshow(X);
% Define the ROI constraint:
h = imrect();
h.setPositionConstraintFcn(#(p) roi_constraint(p,size(X)));
% Wait for the ROI to be confirmed:
roi = round(wait(h));
x1 = roi(1);
x2 = x1 + roi(3);
y1 = roi(2);
y2 = y1 + roi(4);
% Create the final image Z and display it:
Z = X;
Z(y1:y2,x1:x2,:) = Y(y1:y2,x1:x2,:);
imshow(Z);
% Auxiliary function for ROI constraint:
function p_adj = roi_constraint(p,img_size)
p_adj(1) = max([1 p(1)]);
p_adj(2) = max([1 p(2)]);
p_adj(3) = min([(img_size(2) - 1) p(3)]);
p_adj(4) = min([(img_size(1) - 1) p(4)]);
end
The script has been tested under Matlab 2017a and works as expected. As you can see, the main difference is the way the size constraint is being handled: in your case, it wasn't properly applied before wait was hit, thus returning an invalid rectangle. Also, in order to avoid a wrong offsetting, the round function has been applied to the rectangle.

2D Image transformations by pixel

I am trying to do a translation, euclidian, similarity, affine and projective transformation on an image pixel by pixel. the input for my program is the image name and the transformation matrix.
This is my code
function imagetrans(name, m)
Image = imread(name);
[rows, cols] = size(Image);
newImage(1:rows,1:cols) = 1;
for row = 1 : rows
for col = 1 : cols
if(Image(row,col) == 0)
point = [row;col;1];
answer = m * point;
answer = int8(answer);
newx = answer(1,1);
newy = answer(2,1);
newImage(newx,newy) = 0;
end
end
end
imshow(newImage);
end
This is the image
Right now I am testing just a translation matrix.
matrix =
1 0 7
0 1 2
0 0 1
when I pass the image and matrix through the function my result is just a little black line
What am I doing wrong?
Using the matlab debugger, I noticed that that you are casting to int8, which is too small too represent all indices. So, you should use int32/int64 or uint32/uint64 instead, i.e.
answer = uint8(answer);
should be
answer = uint32(answer);
Please try to use the Matlab debugger before asking the question: why does it not work?

Matlab image interpolation interp2()

let img1, and img2 represent two images with the same dimensions; and let v = (tx,ty) be a vector representing shifting (translation) of the img1 toward img2.
how can I use interp2() to warp img2 towards img1?
Define the grid for which the images are defined
>> sz = size(img1);
>> [y x] = ndgrid( 1:sz(1), 1:sz(2) );
Use the grid to define the interpolation
>> timg2 = interp( x, y, img2, x + tx, y + ty );
PS,
You might want to take a look at tformarray to do the same.

Resources