So I'm trying to create a program to warp this picture of Steve Jobs by moving the center point of the image around. I have to do it by following pseudo code given to me by my professor and I'm making very little progress. Here's the code I've got so far with some of the pseudo code replaced with my own code but I'm getting an error because I'm exceeding the built in recursion limit of 500 recursions. I'm certain this isn't supposed to be a problem so I've got an infinite loop or recursion somewhere:
function HW7
I = imread('jobs_small.jpg','jpg'); % Loads the image
figure(1); imshow(I); % Displays the image
P_new = [194; 257]; % Offset of the center point
D = Warp(I,P_new); % Displays mask to see if it's correct
figure(2); imshow(D);
imwrite(D, 'jobs_small_warper.jpg', 'jpg'); % saves the result
end
function D = Warp(I, P_new)
%image warping
%function D = Warp(I,P_new)
I = imread('jobs_small.jpg','jpg');
P_new = [194; 257];
D = Warp(I,P_new);
R = size(I,1);
C = size(I,2);
D = uint8(zeros(size(I)));
% create the original 4 triangles as 4 matrices Y1,Y2,Y3,Y4
Y1 = [1 130 1
1 173 R];
Y2 = [1 130 C
1 173 1];
Y3 = [1 130 R
R 173 C];
Y4 = [R 130 C
C 173 1];
% create the distorted 4 triangles as 4 matrices X1,X2,X3,X4
X1 = [[1;1] [P_new] [1;R]];
X2 = [[1;1] [P_new] [C;1]];
X3 = [[1;R] [P_new] [R;C]];
X4 = [[R;C] [P_new] [C;1]];
M = CreateMask(R,C,X1,X2,X3,X4);
figure(3); imshow(M./4);
A1 = SolveWarp(X1,Y1);
A2 = SolveWarp(X2,Y2);
A3 = SolveWarp(X3,Y3);
A4 = SolveWarp(X4,Y4);
% warp the 4 regions and copy the pixels from I to D
% Hint: you can use if/else statement within a double for loop to scan the
% whole matrix
% Note: be careful to round the coordinates to make them inside the image
% boundry
% D <- copy the corresponding pixels from I based on M,A1,A2,A3,A4
end
function A = SolveWarp(X,Y)
A = Y*inv([[X]; [1 1 1]]);
% following Eq(1) for the solution <- implement this!
end
function M = CreateMask(R,C,P1,P2,P3,P4)
% R -- number of Rows
% C -- number of columns
% P1,P2,P3,P4 are the 4 triangles
% M -- the mask of size (R,C)
M = zeros(R,C);
% x <- collection of x coordinates of all pixels
x = I(1:2:259,:); %VERY LIKELY INCORRECT
% y <- collection of y coordinates of all pixels
y = I(2:2:259,:); %VERY LIKELY INCORRECT
M1 = inpolygon(x,y,P1(1,:),P1(2,:));
M2 = inpolygon(x,y,P2(1,:),P2(2,:))*2;
M3 = inpolygon(x,y,P3(1,:),P3(2,:))*3;
M4 = inpolygon(x,y,P4(1,:),P4(2,:))*4;
M5 = max(max(max(M1,M2), M3), M4);
% M <- re-stack M5 to an RxC matrix
M5 = zeros(R,C);
% ^ pay attention to the order: row-by-row or column-by-column
end
% The center is shifted to (194,257)
So this is all pretty much a mess as you can see. I'm particularly confused about how to copy the pixels from matrix I (the image) to matrix D (the distorted image), how the mask is supposed to function, and what restacking M5 means near the end of the code.
I'd really appreciate any guidance and help you all could give me.
Thanks
Related
I have a 3D geometry problem and I am not certain of the best approach to solve it. I have a model with two boxes, one above the others. They have the same dimension, L (length) * p (depth) * e (thickness), and are separated by a height of h. They are perfectly superposed, with no offset between them.
For each point of my bottom box, I want to get the zenith of all lines that can cross the top box and arrive to this point. It doesn't matter if the line crosses the top box by the top or the side.
The zenith is the angle of "looking up". In our case, a zenith of 0 represents the point directly above the point P, and an angle of 90 is directly looking in front. A zenith of 180 would be looking below the point, but for our use, it's useless. The zeniths we look for are between 0 and 90°.
For a more intuitive visualization, let's say that I have a hole in the ceiling, and that I want to map the zenith of all light that crosses this hole and reaches the floor.
This is what it looks like:
For any point P of the bottom box, I want an array containing the zeniths of all "rays" that cross the top box before arriving on P. The red lines are the "edges", the last zeniths I would get for each corner.
I am working on a way to code it in MATLAB and I was wondering if there was a better algorithm that I am not seeing. My approach, in pseudocode, would be this:
bottomBox = [1:L, 1:p, 1:e];
topBox = [1:L, 1:p, 1+h:e+h];
results = zeros(L:p) * NaN; % Array of results, one per "case" on the bottom box
zeniths = zeros(L:p) * NaN; % Array of zeniths for each result case
for i = 1:L
for j = 1:p % Browsing the bottom box case by case
for k = 1:L
for l = 1:p
for m = 1:e % For each bottom box case, browsing the top box case by case
p1 = topBox(k,l,m); % p1 is each case on the top box
p2 = bottomBox(i,j,1); % p2 is the current bottom box case, z doesn't mattter
p3 = topBox(i,j,m); % p3 is the projection of p2 on the top box (zenith = 0)
v1 = p1 - p2;
v2 = p3 - p2;
zeniths(k,l) = rad2deg(atan2(norm(cross(p1, p2)), dot(p1, p2)));
end
end
end
results(i,j) = zeniths;
end
end
I tried to implement this and I couldn't get it to work. More specifically, the angle calculation doesn't seem to work, I have an error stating:
Error using cross;
A and B must be of length 3 in the dimension in which the cross product is taken.
I am looking for advice on how to build the algorithm.
Please tell me if the question is better suited for another StackExchange community, such as Math.
I'll get you started showing you one way to do it for 1 point and I'll let you build the final loop to do the calc for all your points.
As expressed in the comment, for the purpose of these calculations, you do not need to consider the thickness of your plates, you can model them simply with two parallel planes separated by a distance H.
I don't know the size of your plates nor the grid size you want so I'll keep it simple for this example:
H = 5 ; % distance between the planes
[X,Y] = meshgrid(-3:3,-2:2) ;
GridSize = size(X) ;
Zb = zeros(GridSize) ;
Zt = zeros(GridSize) + H ;
This gives you 4 matrices, defining 2 planes. The bottom plane is composed of [X,Y,Zb] and the top plane is formed by [X,Y,Zt].
If you want to visualise them, you can run the following code (optional):
%% Display planes
figure ;
ht = surf(X,Y,Zt, 'FaceColor',[.8 .8 .8],'DisplayName','Top plate') ;
hold on
hb = surf(X,Y,Zb, 'FaceColor',[.6 .6 .6],'DisplayName','Bottom plate') ;
xlabel('X') ; ylabel('Y') ; zlabel('Z') ;
axis equal ; legend show
Now for the rest of the example, I selected a point P, at coordinate [-2,1,0]. This choice is completely arbitrary, just for the example. In your final algorythm you will still have to loop over several points Pi (although remember that your problem is symetric so if your domain is too large you can reduce your computations by using the symetries of your model).
%% This will have to be embedded into a loop over the points Pi
% Assuming points P=(-2,1,0)
p = [-2;1;0] ;
zn = [0;0;1] ; % unitary vector, oriented Oz
dx = X - p(1) ; % `x` distance between all points of the plane and P
dy = Y - p(2) ; % `y` distance between all points of the plane and P
dz = zeros(size(X)) + H ; % `z` distance (all the same)
V = [dx(:) dy(:) dz(:)].' ; % to obtain list of vector V = [dx;dy;dz] ;
nv = size(V,2) ; % number of points/angle to calculate
zenith = zeros(nv,1) ; % preallocate result matrix (always good!)
for k=1:nv
% [u] is the vector going from `P` to the current point considered on the top plane
u = V(:,k) ;
% determine the angle between [u] and [zn]
zenith(k) = atan2( norm(cross(u,zn)) , dot(u,zn) ) ;
end
% Reshape "zenith" from vector to matrix so it matches the base grid system
zenith = reshape( zenith , GridSize ) ;
You now have, for this point P, a matrix of angle with every other point of the top plane:
>> rad2deg(zenith)
ans =
32.31 30.96 32.31 35.80 40.32 45.00 49.39
24.09 21.80 24.09 29.50 35.80 41.81 47.12
15.79 11.31 15.79 24.09 32.31 39.51 45.56
11.31 0 11.31 21.80 30.96 38.66 45.00
15.79 11.31 15.79 24.09 32.31 39.51 45.56
Once again, completely optionally, if you want to visualise the vectors which were used for the calculations:
for k=1:nv
hp(k) = plot3([p(1) X(k)],[p(2) Y(k)],[0 H],'Marker','o','MarkerFaceColor','k') ;
end
will yield:
Now for your final result, remember you have a 2D matrix for each point P of your bottom plane, so your final result will either be a collection of 2D matrices or a large 3D matrix.
Zenith angle is just
atan2(h, sqrt(dx^2+dy^2))
where dx, dy are coordinate differences along L and p axes (i-k and j-l in your loops)
Perhaps h+m (m as your variable for m = 1:e) instead of h if you need points inside top box
I am trying to solve a problem which I may struggle to describe, so will attempt to describe with the aid of the following picture (please bear with me!):
I have two matrices which are defined on different coordinate spaces (u,v) for matrix A and (x,y) for matrix B. They have different grid sizes and different numbers of pixels. My goal is to apply a scaling factor S to the matrix A, and then to simply add it to matrix B. (For context, this is an optical imaging problem, where matrix A is located at an object plane, matrix B is located at an image plane, and S is the magnification).
So, I would like to create a new matrix C which is the equivalent of A but brought into the new coordinates (x,y). Matrix C should have the same number of rows and columns as B.
A minimum example of A and B is shown below, where the red dashed lines on the right illustrate the effective physical regions occupied by matrix A's pixels:
This is produced by the following code:
%%% Inputs for matrix A %%%
M = 4; % num columns in matrix A
N = 4; % num rows in matrix A
du = 13; % horizontal size of a pixel in matrix A [mm]
dv = 13; % vertical size of a pixel in matrix A [mm]
%%% Set up matrix A %%%
Lu = (M-1)*du; % physical hor. coord. of centre of last pixel [mm]
Lv = (N-1)*dv; % physical ver. coord. of centre of last pixel [mm]
u = -Lu/2:du:Lu/2; % hor. coordinates for matrix A [mm]
v = -Lv/2:dv:Lv/2; % ver. coordinates for matrix A [mm]
A = zeros(N,M);
A(1,1) = 1; % Set a few values to 1 for testing
A(2,3) = 1;
A(3,4) = 1;
%%% Inputs for matrix B %%%
dx = 0.1; % grid step in matrix B [mm]
dy = 0.1; % grid step in matrix B [mm]
Lx = 6; % physical hor. coord. of centre of last pixel [mm]
Ly = 6; % physical ver. coord. of centre of last pixel [mm]
%%% Set up matrix B %%%
x = -Lx/2:dx:Lx/2;
y = -Ly/2:dy:Ly/2;
B = rand(length(y),length(x));
figure('color','w');
subplot(1,2,1);imagesc(u, v, A); axis equal tight;
subplot(1,2,2);imagesc(x, y, B); axis equal tight;
S = 1/20; % scale factor from matrix A's corrdinates to matrix B's
% C = ?
In this example, I have set the pixel size of matrix A to be 13mm, and the scaling factor to be 1/20. This means that in B's coordinates each pixel should be 13/20 = 0.65mm. This is bigger than the grid size dx=0.1mm, and so in this case the result should be that, after mapping, pixels should span multiple grid points. Any region outside the total extent of matrix A should be padded with zeros.
Is there a simple way (or built-in function) which would quickly generate matrix C in Matlab (ideally without using loops over each pixel, or interpolation)?
I can simply scale the coordinates, which matches the physical dimensions, but the matrices are still different number of rows and columns:
u_scaled = u*S;
v_scaled = v*S;
subplot(1,3,3);imagesc(u_scaled, v_scaled, A); axis equal tight;
You can use interp2 or griddedInterpolant:
C = interp2(u, v, A, x * 20, y.' * 20, 'nearest', 0);
With some modification better result can be produced:
AA = padarray(A, [1 1], 0);
uu = [-du+u(1) u u(end)+du];
vv = [-dv+v(1) v v(end)+dv];
C = interp2(uu, vv, AA, x * 20, y.' * 20, 'nearest', 0);
Currently I have been working on obtaining the length of a curve, with the following code I have managed to get the length of a curve present in an image.
test image one curve
Then I paste the code that I used to get the length of the curve of a simple image. What I did is the following:
I got the columns and rows of the image
I got the columns in x and the rows in y
I obtained the coefficients of the curve, based on the formula of the
parable
Build the equation
Implement the arc length formula to obtain the length of the curve
grayImage = imread(fullFileName);
[rows, columns, numberOfColorBands] = size(grayImage);
if numberOfColorBands > 1
grayImage = grayImage(:, :, 2); % Take green channel.
end
subplot(2, 2, 1);
imshow(grayImage, []);
% Get the rows (y) and columns (x).
[rows, columns] = find(binaryImage);
coefficients = polyfit(columns, rows, 2); % Gets coefficients of the formula.
% Fit a curve to 500 points in the range that x has.
fittedX = linspace(min(columns), max(columns), 500);
% Now get the y values.
fittedY = polyval(coefficients, fittedX);
% Plot the fitting:
subplot(2,2,3:4);
plot(fittedX, fittedY, 'b-', 'linewidth', 4);
grid on;
xlabel('X', 'FontSize', fontSize);
ylabel('Y', 'FontSize', fontSize);
% Overlay the original points in red.
hold on;
plot(columns, rows, 'r+', 'LineWidth', 2, 'MarkerSize', 10)
formula = poly2sym([coefficients(1),coefficients(2),coefficients(3)]);
% formulaD = vpa(formula)
df=diff(formula);
df = df^2;
f= (sqrt(1+df));
i = int(f,min(columns),max(columns));
j = double(i);
disp(j);
Now I have the image 2 which has n curves, I do not know how I can do to get the length of each curve
test image n curves
I suggest you to look at Hough Transformation:
https://uk.mathworks.com/help/images/hough-transform.html
You will need Image Processing Toolbox. Otherwise, you have to develop your own logic.
https://en.wikipedia.org/wiki/Hough_transform
Update 1
I had a two-hour thinking about your problem and I'm only able to extract the first curve. The problem is to locate the starting points of the curves. Anyway, here is the code I come up with and hopefully will give you some ideas for further development.
clc;clear;close all;
grayImage = imread('2.png');
[rows, columns, numberOfColorBands] = size(grayImage);
if numberOfColorBands > 1
grayImage = grayImage(:, :, 2); % Take green channel.
end
% find edge.
bw = edge(grayImage,'canny');
imshow(bw);
[x, y] = find(bw == 1);
P = [x,y];
% For each point, find a point that is of distance 1 or sqrt(2) to it, i.e.
% find its connectivity.
cP = cell(1,length(x));
for i = 1:length(x)
px = x(i);
py = y(i);
dx = x - px*ones(size(x));
dy = y - py*ones(size(y));
distances = (dx.^2 + dy.^2).^0.5;
cP{i} = [x(distances == 1), y(distances == 1);
x(distances == sqrt(2)), y(distances == sqrt(2))];
end
% pick the first point and a second point that is connected to it.
fP = P(1,:);
Q(1,:) = fP;
Q(2,:) = cP{1}(1,:);
m = 2;
while true
% take the previous point from point set Q, when current point is
% Q(m,1)
pP = Q(m-1,:);
% find the index of the current point in point set P.
i = find(P(:,1) == Q(m,1) & P(:,2) == Q(m,2));
% Find the distances from the previous points to all points connected
% to the current point.
dx = cP{i}(:,1) - pP(1)*ones(length(cP{i}),1);
dy = cP{i}(:,2) - pP(2)*ones(length(cP{i}),1);
distances = (dx.^2 + dy.^2).^0.5;
% Take the farthest point as the next point.
m = m+1;
p_cache = cP{i}(find(distances==max(distances),1),:);
% Calculate the distance of this point to the first point.
distance = ((p_cache(1) - fP(1))^2 + (p_cache(2) - fP(2))^2).^0.5;
if distance == 0 || distance == 1
break;
else
Q(m,:) = p_cache;
end
end
% By now we should have built the ordered point set Q for the first curve.
% However, there is a significant weakness and this weakness prevents us to
% build the second curve.
Update 2
Some more work since the last update. I'm able to separate each curve now. The only problem I can see here is to have a good curve fitting. I would suggest B-spline or Bezier curves than polynomial fit. I think I will stop here and leave you to figure out the rest. Hope this helps.
Note that the following script uses Image Processing Toolbox to find the edges of the curves.
clc;clear;close all;
grayImage = imread('2.png');
[rows, columns, numberOfColorBands] = size(grayImage);
if numberOfColorBands > 1
grayImage = grayImage(:, :, 2); % Take green channel.
end
% find edge.
bw = edge(grayImage,'canny');
imshow(bw);
[x, y] = find(bw == 1);
P = [x,y];
% For each point, find a point that is of distance 1 or sqrt(2) to it, i.e.
% find its connectivity.
cP =[0,0]; % add a place holder
for i = 1:length(x)
px = x(i);
py = y(i);
dx = x - px*ones(size(x));
dy = y - py*ones(size(y));
distances = (dx.^2 + dy.^2).^0.5;
c = [find(distances == 1); find(distances == sqrt(2))];
cP(end+1:end+length(c),:) = [ones(length(c),1)*i, c];
end
cP (1,:) = [];% remove the place holder
% remove duplicates
cP = unique(sort(cP,2),'rows');
% seperating curves
Q{1} = cP(1,:);
for i = 2:length(cP)
cp = cP(i,:);
% search for points in cp in Q.
for j = 1:length(Q)
check = ismember(cp,Q{j});
if ~any(check) && j == length(Q) % if neither has been saved in Q
Q{end+1} = cp;
break;
elseif sum(check) == 2 % if both points cp has been saved in Q
break;
elseif sum(check) == 1 % if only one of the points exists in Q, add the one missing.
Q{j} = [Q{j}, cp(~check)];
break;
end
end
% review sets in Q, merge the ones having common points
for j = 1:length(Q)-1
q = Q{j};
for m = j+1:length(Q)
check = ismember(q,Q{m});
if sum(check)>=1 % if there are common points
Q{m} = [Q{m}, q(~check)]; % merge
Q{j} = []; % delete the merged set
break;
end
end
end
Q = Q(~cellfun('isempty',Q)); % remove empty cells;
end
% each cell in Q represents a curve. Note that points are not ordered.
figure;hold on;axis equal;grid on;
for i = 1:length(Q)
x_ = x(Q{i});
y_ = y(Q{i});
coefficients = polyfit(y_, x_, 3); % Gets coefficients of the formula.
% Fit a curve to 500 points in the range that x has.
fittedX = linspace(min(y_), max(y_), 500);
% Now get the y values.
fittedY = polyval(coefficients, fittedX);
plot(fittedX, fittedY, 'b-', 'linewidth', 4);
% Overlay the original points in red.
plot(y_, x_, 'r.', 'LineWidth', 2, 'MarkerSize', 1)
formula = poly2sym([coefficients(1),coefficients(2),coefficients(3)]);
% formulaD = vpa(formula)
df=diff(formula);
lengthOfCurve(i) = double(int((sqrt(1+df^2)),min(y_),max(y_)));
end
Result:
You can get a good approximation of the arc lengths using regionprops to estimate the perimeter of each region (i.e. arc) and then dividing that by 2. Here's how you would do this (requires the Image Processing Toolbox):
img = imread('6khWw.png'); % Load sample RGB image
bw = ~imbinarize(rgb2gray(img)); % Convert to grayscale, then binary, then invert it
data = regionprops(bw, 'PixelList', 'Perimeter'); % Get perimeter (and pixel coordinate
% list, for plotting later)
lens = [data.Perimeter]./2; % Compute lengths
imshow(bw) % Plot image
hold on;
for iLine = 1:numel(data),
xy = mean(data(iLine).PixelList); % Get mean of coordinates
text(xy(1), xy(2), num2str(lens(iLine), '%.2f'), 'Color', 'r'); % Plot text
end
And here's the plot this makes:
As a sanity check, we can use a simple test image to see how good an approximation this gives us:
testImage = zeros(100); % 100-by-100 image
testImage(5:95, 5) = 1; % Add a vertical line, 91 pixels long
testImage(5, 10:90) = 1; % Add a horizontal line, 81 pixels long
testImage(2020:101:6060) = 1; % Add a diagonal line 41-by-41 pixels
testImage = logical(imdilate(testImage, strel('disk', 1))); % Thicken lines slightly
Running the above code on this image, we get the following:
As you can see the horizontal and vertical line lengths come out close to what we expect, and the diagonal line is a little bit more than sqrt(2)*41 due to the dilation step extending its length slightly.
I try with this post but i don´t understand so much, but the idea Colours123 sounds great, this post talk about GUI https://www.mathworks.com/matlabcentral/fileexchange/24195-gui-utility-to-extract-x--y-data-series-from-matlab-figures
I think that you should go through the image and ask if there is a '1' if yes, ask the following and thus identify the beginning of a curve, get the length and save it in a BD, I am not very good with the code , But that's my idea
I'm trying to implement a paper. In it I need to calculate the centre of gravity and second order moment of an image.
The equations of centre of gravity and second order moment are respectively given as:
Im having trouble trying to code this in Matlab ss from what I understand p(x,y) is the pixel of the image, but I'm having trouble what y represents and how would I implement in in the sum function. This is my implementation of the first equation but since I did not incorporate the y in there I'm sure the result given is wrong.
img = imread(path);
m = numel(img);
cog = sum(img(:))/m;
i think, m should be the maximum of y, because f2 is a function of x which means in Matlab it should be a vector.
try this code to implement f2:
img = magic(10)
m = 10;
temp = 0;
for y = 1:m
temp = temp+y*img(:,y);
%temp = temp+y*img(y,:); % depends on your image coordinates system
end
f2 = temp/m
Try the following code that uses vectorized anonymous functions.
% Read the image into an array (3 dimensions).
% Note: you may need to convert to doubles
img = im2double(imread(path));
% Get the size (may need to switch m and n).
[m, n, o] = size(img);
% Create y vector
y = 1:m;
% Create functions (not sure how you want to handle the RGB values).
f2 = #(x, p) sum(y.*p(x,:,1)/m);
f3 = #(x, p) sum(y.^2.*p(x,:,1)/(m^2));
% Call the functions
x = 10; % Some pixel x position
f2_result = f2(x, img);
f3_result = f3(x, img);
Note: I may have the x and y switched depending on the orientation of your image. If that's the case then switch things around like this:
[n, m, o] = size(img);
...
f2 = #(x, p) sum(y.*p(:,x)/m);
etc...
I'm not at work so I can't run the im2double function (don't have the library) but I think it will work.
Suppose i would like to draw an image like the following:
Where the pixel values are refined to 0 for black and white for 1.
These line are drawn with specific radius and angles
Now I create a 80 x 160 matrix
texturematrix = zeros(80,160);
then i want to change particular elements to be 1 according to the lines conditions
but how do i make them repeatedly with specific distance apart from each others effectively?
Thanks a lot everyone!
This might not be what you are looking for, but generating such an image could be done by plotting a set of lines, as follows:
% grid sizes
m = 6;
n = 5;
% line length and angle
len = 1;
theta = .1*pi;
[a,b] = meshgrid(1:m,1:n);
x = reshape([a(:),a(:)+len*cos(theta),nan(numel(a),1)]',[],1);
y = reshape([b(:),b(:)+len*sin(theta),nan(numel(b),1)]',[],1);
h = figure();
plot(x,y,'k', 'LineWidth', 2);
But this has nothing to do with a texture matrix. So, we construct a matrix of desired size:
set(gca, 'position',[0 0 1 1], 'units','normalized', 'YTick',[], 'XTick',[]);
frame = frame2im(getframe(h),[0 0 1 1]);
im = imresize(frame,[80 160]);
M = ~(im(2:end,2:end,1)==255);