How can I stretch an image non-uniformly?
21 visualizaciones (últimos 30 días)
Mostrar comentarios más antiguos
I would like to take an image, and stretch it non-uniformly such that the further from the center of the image the less stretched it is. So the areas near the edges would get condensed while the areas far from the areas would get inflated. How could I go about generating a suitable displacement field to use? Please let me know if creating a displacement field to use would be a good approach and/or how I might do this. Any help is much appreciated, thanks! :)
0 comentarios
Respuestas (3)
Image Analyst
el 10 de Ag. de 2022
2 comentarios
Image Analyst
el 28 de Ag. de 2024
See Steve's blog:
For the special case of barrel and pincushion distortion, see undistortFisheyeImage and associated companion functions.
DGM
el 28 de Ag. de 2024
Editada: DGM
el 28 de Ag. de 2024
I'm going to start this example using MIMT tools, since generating the displacement maps would otherwise be a chore. We can work back to IPT from there.
Let's start with the example from the webdocs. The center of the displacement map is neutral, and the periphery compresses the image toward the center.
% source image
inpict = imread('peppers.png');
imshow(inpict)
% generate the base displacement maps
s = size(inpict);
x1 = lingrad(s,[0 0; 0 1],[0; 1]*255); % some linear gradients
y1 = lingrad(s,[0 0; 1 0],[0; 1]*255);
radmask = radgrad(s,[1 1]*0.5,0.4,[1; 1; 0]*255,'linear',[0 0.5 1]); % a radial gradient
% generate a linear combination of the directional maps and a radial mask
xmap = replacepixels(0.5,x1,radmask);
ymap = replacepixels(0.5,y1,radmask);
% apply the displacement
mag = [1 1]*100; % displacement magnitude at full scale (px,[x y])
dpict1 = displace(inpict,mag,'xmap',xmap,'ymap',ymap,'edgetype',0);
imshow(dpict1)
In order to change the behavior, simply flip the directional maps and invert the radial mask. Now it's neutral around the periphery (mostly just the corners), and the largest displacement occurs in the center.
% reorient the base maps
x1 = fliplr(x1);
y1 = flipud(y1);
radmask = iminv(radmask);
% regenerate the displacement maps (same as before)
xmap = replacepixels(0.5,x1,radmask);
ymap = replacepixels(0.5,y1,radmask);
% apply the displacement (same as before)
dpict2 = displace(inpict,mag,'xmap',xmap,'ymap',ymap,'edgetype',0);
imshow(dpict2)
That's MIMT displace(), though. Nobody is going to use that. If you want to use the canonical IPT tool for applying displacement maps, use imwarp(). It can do many things beyond what MIMT displace() was ever intended to do, but for these two simple examples, it's fairly easy to apply with a minor change.
% translate the displacement maps into a format compatible with imwarp()
% MIMT displace() uses separate x,y maps which are
% any standard grayscale or RGB image where neutral displacement is 50% intensity,
% and displacement at full-swing is defined by an independent magnitude parameter.
% IPT imwarp() uses a 2-page stack of single-channel literal displacement fields.
D = cat(3,(im2double(xmap)-0.5)*mag(1)*2, ...
(im2double(ymap)-0.5)*mag(2)*2);
% apply the displacement map
dpict3 = imwarp(inpict,D);
imshow(dpict3)
Okay that looks simple enough, but we're still using MIMT to generate all the maps. Let's go back to the first example and start over with base tools.
% source image
inpict = imread('peppers.png');
% generate the base displacement maps
mag = [1 1]*100; % displacement magnitude at full scale (px,[x y])
s = size(inpict);
% the linear gradients (in displacement-scale)
x1 = linspace(-mag(1),mag(1),s(2));
x1 = repmat(x1,[s(1) 1]);
y1 = linspace(-mag(2),mag(2),s(1));
y1 = repmat(y1.',[1 s(2)]);
% the PWL radial gradient (in unit-scale)
center = [0.5 0.5]; % center (normalized)
breakpts = [0 0.2 0.4 1]; % breakpoint positions (normalized to diagonal)
gradvals = [1 1 0 0]; % values at breakpoints (unit-scale)
[xx yy] = meshgrid(1:s(2),1:s(1));
R = hypot(xx-center(2)*s(2),yy-center(1)*s(1)); % radius from center
R = R/sqrt(s(1)^2 + s(2)^2); % normalize to image diagonal
radmask = interp1(breakpts,gradvals,R,'linear','extrap');
% generate a linear combination of the directional maps and a radial mask
xmap = x1.*(1-radmask); % neutral condition is strictly 0, not 50% gray!
ymap = y1.*(1-radmask);
% stack the displacement maps as imwarp() expects
D = cat(3,xmap,ymap);
% apply the displacement
dpict4 = imwarp(inpict,D);
imshow(dpict4)
That was perceptibly inconvenient, but it's equivalent.
5 comentarios
DGM
el 31 de Ag. de 2024
Wait a minute. Let me back up here.
You said:
- the widths of the pixels in the x direction may not be uniform (i.e. the width of the pixel IMG(:,k) is different for different k = 1..N)
- the heights of the pixel in the y direction may not be uniform (i.e. the height of the pixel IMG(:,k) is different for different k = 1..N)
The first condition describes data on a nonuniform grid; the other describes data which is not on a grid.
Dealing with nonuniformly-gridded data is the problem that something like sanepcolor() attempts to solve.
If the data is not gridded, then we're limited further. In this scenario, there's nothing keeping pixels from overlapping each other or from having gaps between each other. If the pixels are all square, we could probably use scatter() to illustrate it. Otherwise, if N is small, we might be able to build the image using patch() to draw N^2 rectangles.
Eric Machorro
el 2 de Sept. de 2024
THANK YOU for being so diligent and super-prompt in your responses.
I had a typo in earlier –Restating from before
- The widths of the pixels in the x direction may not be uniform (i.e. the width of the pixel IMG(:,k) is different for different k = 1..N)
- the heights of the pixel in the y direction may not be uniform (i.e. the height of the pixel IMG(k,:) is different for different k = 1..N)”
Note the correctionm in bold. Again, apologies for the confusion that I caused earlier.
I have data associated with the centers of each pixel, but the pixels are not uniformly sized: in other words, I have pixels that are bounded by pixel ‘edges’ in the X and Y direction. The immediate (simplified) example that I’m playing with is
% a simple, sample image
IMG = zeros(10,10);
IMG(3,8) = 1;
% non-uniform x-spacing, but uniform y-spacing – the pixel edges
X = [-140 -130 -105 -70 -10 0 10 25 110 130 140];
Y = [ -140 -112 -84 -56 -28 0 28 56 84 112 140];
% the midpoints of each ‘pixel’
x_mid = [X(1:end-1)+diff(X)/2];
y_mid = [Y(1:end-1)+diff(Y)/2];
sanePColor(x_mid,y_mid,IMG)
The visualization tool you suggested, https://www.mathworks.com/matlabcentral/fileexchange/35601-sanepcolor-varargin, has a few good ideas w/in its code but falls short of the immediate need. When overlayed with the gird-lines stipulated by the X and Y variables above, it yields this:
which shows a mis-alignment of the grid-lines (e.g. pixel edges) with the corresponding pixels center values.
Your suggestion to use patch() is intriguing, but it isnt clear how to handle the color scale - different patches with difference values of IMG would have to have different colors reflecting the corresponding value of IMG. And then all of these colors would need to be drawn from the same palette that was then summarized in a single colorbar for the whole figure.
Again, thank you for your effort and time in looking at this. You're being super helpful.
Eric Machorro
el 2 de Sept. de 2024
Epilogue - of sorts:
I was trying to avoid this as a solution, but for small-ish plots, this issue can resolved by plotting out individual pixels as individual rectangles using matlab's patch command.
% take for example a non-uniform x-spacing, but uniform y-spacing – the
% pixel edges could be something like ...
X = [-140 -130 -105 -70 -10 0 10 25 110 130 140];
Y = X;
% the midpoints of each ‘pixel’
dX = diff(X);
dY = diff(Y);
x_mid = [X(1:end-1)+dX/2];
y_mid = [Y(1:end-1)+dY/2];
% a simple, sample image
IMG = zeros(length(x_mid),length(y_mid));
IMG(3,8) = 1;
%Establish a template for vertices of the subsequent patch plots
%used o plot individual 'pixels'
VerticesX = [-1 1 1 -1];
VerticesY = [-1 -1 1 1];
%Loop over all the mid-points to plot a single pixel
figure
for i = 1:10
for j = 1:10
Cx = x_mid(i)+VerticesX*dX(i)/2;
Cy = y_mid(j)+VerticesY*dY(j)/2;
patch(Cx, Cy,IMG(i,j)); hold on
%plot3(x_mid(i),y_mid(j),IMG(i,j),'r.') % see commentary below
end
end
colorbar
%Extraneous adjustments to plot the 'edges' of the pixels for reference
hold on
% vertical or up-down lines, X-constant lines
for i = 1:10
line(X(i) * [ 1 1],[-140 140],'Color','red','LineStyle','--')
end
% horizontal or left-right, Y-constant lines
for j = 1:10
line([-140 140],Y(j) * [ 1 1],'Color','red','LineStyle','--')
end
%Extraneous adjustments just to make tick-marks easier to read
subaxis = gca;
subaxis.XTick = X;
subaxis.XTickLabelRotation = 30;
subaxis.YTick = Y;
subaxis.YTickLabelRotation = 30;
This will yield ...
This still only identifies a pixel by its boundaries, so that some the 'Data-tip' tool still doesn't give me the value of the pixel at its center, but rather only the coordinates of the vertices of the rectangle encompassing the 'pixel'
This might by somewhat addressed by adding something akin to plot3(x_mid(i),y_mid(j),IMG(i,j),'r.') right after the call to the patch() commmand. It is a little kludgy though IMO.
0 comentarios
Ver también
Categorías
Más información sobre Image Processing Toolbox en Help Center y File Exchange.
Community Treasure Hunt
Find the treasures in MATLAB Central and discover how the community can help you!
Start Hunting!