Projecting an image onto a cylindrical surface in a way where the image appears flat - matlab

I'm trying to print out QR codes onto the surface of water bottles using matlab. However, the QR bottles cannot be recognized because of the curved surface of the bottle, and the therefore deformed shape of the QR codes.
I was wondering what mathematical equation I might have to apply to deform the picture (for both the general geometry of the picture, and the spacing deformation inside the picture), so that when printed onto a cylindrical surface, it seems like its on a flat surface to a camera.
Thanks!

I had some success with this and though it doesn't use Matlab, it may give you, or someone else an idea on a possible way to get started.
I generated a QR code with qrencode in Terminal like this:
qrencode -o qr.png -d 300 'http://www.thesetchells.com'
I resized that to 500x500 and then generated a "displacement map" which is just a linear gradient with a contrast stretch created to match the size of the QR code. I used ImageMagick in Terminal, but obviously you can use Matlab or other tools:
I then applied the displacement map to my QR code like this:
convert qr.png map.png -fx 'p{v*w,j}' result.png
And got a distorted QR code, which I printed and wrapped round a large bottle and my iPhone was able to read it.
Anthony Thyssen has some very useful information on "displacement maps" here.

So I've tried an approach to my problem using the basic "car.jpg" image included in Matlab.
So far, I've taken a square part of the car picture, and wrapped it around the surface of part of a cylinder.
Here is my code:
close all, clf, clear all
%% Creating cylinder
r = 6; %centimeter
h = 25; %centimeter
[X,Y,Z] = cylinder(r,100);
x = X(:,1:ceil(length(X)/3));
y = Y(:,1:ceil(length(Y)/3));
z = Z(:,1:ceil(length(Z)/3));
%% Plotting cylinder surface
figure(1), clf
h = surf(x,y,z); hold on
axis([-r r -r r]*2.2)
plot3([-r -r], get(gca,'ylim'), [0 0]);
plot3([r r], get(gca,'ylim'), [0 0]);
plot3(get(gca,'xlim'), [-r -r], [0 0]);
plot3(get(gca,'xlim'), [r r], [0 0]);
xlabel('x');
ylabel('y');
zlabel('z');
rotate3d on
axis vis3d
%% Car image
img = imread('car.jpg');
img = imrotate(img,180);
figure(2), clf
%imshow(img(1:340, 1:340, :));
imagesc(img(1:340, 1:340, :));
figure(), clf
warped_plot = warp(x,y,z,img(1:340, 1:340, :))
The next step could be to now project that warped image onto a flat surface (but I'm not sure how to do that mathematically, nor have I found a in-built Matlab function to do this).
Another possibility I was hoping to get feedback on, is to find a mathematical relation between corresponding horizontal points on the flat and cylindrical image, and then to apply the inverse of that equation to the flat image, so that once it is printed out and stuck onto a cylindrical surface it appears flat.

Related

Convert gridded sphere to dots cloud and back

I have a problem with surface generation in matlab. Below is small code. This code looks nonsense, but if It will works I would be happy with my main code :).
% create gridded 3D object
[X Y Z]=sphere(1000);
subplot(1,2,1);
sphere(50) % just for example
% transform it to 3D cloud
X=X(:);
Y=Y(:);
Z=Z(:);
idx=randperm(numel(X),10^3);
X=X(idx);
Y=Y(idx);
Z=Z(idx);
% try to restore surface
F = scatteredInterpolant(X,Y,Z);
[gX,gY]=ndgrid(linspace(min(X),max(X),100),...
linspace(min(Y),max(Y),100));
gZ=F(gX,gY);
subplot(1,2,2)
surface(gX,gY,gZ)
The problem is: if I have gridded data I may plot sphere (left picture) if my data are just Nx3 array with coordinates of some random dots on the sphere I have right picture.
I had similar question already, but the data were well organised. Could You help me with this? If you have solution on other languages or OpenGL You are welcome!

Create 2D Spectrogram in Matlab

I am in need of plotting a 2D spectrogram of a signal in Matlab. I need it for a printed assignment, hence the 3D image makes no sense. However, when the signal is plotted using Spectrogram it automatically produces a 3D plot of the signal.
My Code:
Dataset = 1; % Dataset to be analysed
N = 1024; % Window size
Beta = 12; % Kaiser window beta value (small = narrow main lope)
Overlap = 800; % Window overlap
Threshold = -150; % Minimum magnitude before threshold
spectrogram(Enclosure{Dataset}(1:end),kaiser(N,Beta),Overlap,2048,fs,'MinThreshold',Threshold,'yaxis');
which produces a graph that looks like this:
But it is seen from the top, and the graph is really showing this:
The reason why i need it to specifically be 2D (and why i don't settle with a screenshot) is because i am using Matlab2tikz to convert Matlab figures into Tikz figures in LaTex. with the 3D images i get figures of +100 Mb and 2D will reduce the size to <1Mb.
I don't know what version of Matlab you are using but in 2015a you should be able to get a handle to the figure with the 3D plot and change the view angle to 2D:
view(0,90);
I've also got an example of how you can make your own 2D plot from the outputs of spectrogram() using a similar method:
x = [0:0.01:100];
y = sin(5*x);
y = awgn(y,0.1);
[S,F,T,P] = spectrogram(y,200,0,length(y)*5,100);
[m,n] = size(P);
figure(2)
surf(F,T,zeros(n,m),P','EdgeColor','none')
view(0,90)
xlabel('Frequency')
ylabel('Time (s)')
The output looks like this:
Hopefully since there is no altitude information, the figure size might be smaller but I can't test that since I don't have Matlab2tikz.
One option is to capture whatever its plotted and then plot it as an image. You can do this using getframe
if you do
F=getframe(gca);
cla;
imshow(F.cdata);
You'll get exactly what you will be seeing before, but as an image.
However I think it defeats a bit the purpose of Matlab2Tikz, as the idea os that you have Tikz code describing your data...
You can try the following:
[~,F,T,ps]=spectrogram(Enclosure{Dataset}(1:end),kaiser(N,Beta),Overlap,2048,fs,'MinThreshold',Threshold,'yaxis').
% Output the spectrum in ps
imagesc(T,F,10*log10(ps))
% Generate a 2d image
view(270,90)
xlabel('Time [s]')
ylabel('Frequency [Hz]')
c=colorbar;
c.Label.String='Power [dB]';
% Extra setting to make the plot look like the spectrogram
Good luck

Matlab: How can I control the color of a streamtube plot?

I am currently trying to plot 3D streamtubes. I want the tubes to be colored corresponding to their respective velocity (e.g. slow = blue, fast = red).
To be more exact, I have three 3D-matrices containing the velocity in x, y and z direction. The color of the streamtubes should be sqrt(vx^2+vy^2+vz^2). When using streamtube(x,y,z,vx,vy,vz,sx,sy,sz) the tubes are colored according to their z-coordinate which is useless because it's a 3D plot anyway.
Well this wasn't easy (it ought to be a builtin option), but by modifying the CData of each tube (they are each their own graphics object), you can achieve the desired result. Here's an example
load wind
[sx,sy,sz] = meshgrid(80,20:10:50,0:5:15);
h=streamtube(x,y,z,u,v,w,sx,sy,sz);
drawnow
view(3)
axis tight
shading interp;
This gives this picture:
but then doing this:
vel=sqrt(u.^2+v.^2+w.^2); %// calculate velocities
for i=1:length(h)
%// Modify the colour data of each tube
set(h(i),'CData',interp3(x,y,z,vel,get(h(i),'XData')...
,get(h(i),'YData'),get(h(i),'ZData'),'spline'))
end
drawnow
view(3)
axis tight
shading interp;
gives this result
NOTES:
1) I don't know if this is fully correct, I don't know how to test it
2) You have to interpolate the velocity data from the points where it's known onto the vertices of the streamtubes
3) I found the spline interpolation option to work best, but the other options might work better in other cases

plotting is displayed upside down at MATLAB GUI

I am working on video surveillance on crowd density estimation. I am implementing a corner detection method for this project. However, I have a problem when my code is combined in GUI MATLAB, the display of plotting corner is upside down and it is not overlay on the original image. Here my code on plotting the image in GUI Matlab.
The problem is solved already and here is the correct code:
% Find row,col coords.
[r,c] = find(cim2);
%After getting row and column coordinate, I plot them together in the original image which is
% overlay image and plotting
imshow(inFrame, 'Parent', handles.axes8);
hold on;
p1 = plot([c],[r],'r.');
set(p1, 'Parent', handles.axes8);
Thank you for #Lokesh for his suggestion.
The explanation of your problem is very well given by floris.But here due to lack of full your code we are not able to perform it on our system. you can aslo try for
axis ij
which is some what similar to above answer. you can also try the below code.
imshow(inFrame,'Parent',handles.axes8);
hold on;
p1 = plot([c],[r],'r.');
set(p1,'Parent',handles.axes8);
hope this works for you..let me know about the result..
imshow inverts the Y axis. Conventionally, in image display the top left corner of the screen is (0,0) with i (the first coordinate) running down the screen, and j (the second coordinate) from left to right.
If you want the image axis to look like a conventional plot axis you can do
axis xy
This will "invert" your image, putting 0,0 in the bottom left.

project a sphere to a plane using matlab

This is probably very basic matlab, so forgive me.
I use the command sphere to create a 3D sphere and have the x,y,z matrices that will produce it using surf. For example:
[x,y,z]=sphere(64);
I'd like to project (or sum) this 3D sphere into one of the Cartesian 2D planes (for example X-Y plane) to obtain a 2D matrix that will be the projection of that sphere. Using imshow or imagesc on the output should look something like this:
simple summing obviously doesn't work, how can I accomplish that in Matlab?
It is possible that I completely misunderstand your question, in which case I apologize; but I think one of the following three methods may in fact be what you need. Note that method 3 gives an image that looks a lot like the example you provided... but I got there with a very different route (not using the sphere command at all, but computing "voxels inside" and "voxels outside" by working directly with their distance from the center). I inverted the second image compared to the third on since it looked better that way - filling the sphere with zeros made it look almost like a black disk.
%% method 1: find the coordinates, and histogram them
[x y z]=sphere(200);
xv = linspace(-1,1,40);
[xh xc]=histc(x(:), xv);
[yh yc]=histc(y(:), xv);
% sum the occurrences of coordinates using sparse:
sm = sparse(xc, yc, ones(size(xc)));
sf = full(sm);
figure;
subplot(1,3,1);
imagesc(sf); axis image; axis off
caxis([0 sf(19,19)]) % add some clipping
title 'projection of point density'
%% method 2: fill a sphere and add its volume elements:
xv = linspace(-1,1,100);
[xx yy zz]=meshgrid(xv,xv,xv);
rr = sqrt(xx.^2 + yy.^2 + zz.^2);
vol = zeros(numel(xv)*[1 1 1]);
vol(rr<1)=1;
proj = sum(vol,3);
subplot(1,3,2)
imagesc(proj); axis image; axis off; colormap gray
title 'projection of volume'
%% method 3: visualize just a thin shell:
vol2 = ones(numel(xv)*[1 1 1]);
vol2(rr<1) = 0;
vol2(rr<0.95)=1;
projShell = sum(vol2,3);
subplot(1,3,3);
imagesc(projShell); axis image; axis off; colormap gray
title 'projection of a shell'
You can project on the X-Y plane in Matlab by using:
[x,y,z] = sphere(64);
surf(x,y,zeros(size(z)));
But I think you should not use Matlab for this, because the problem is so simple you can do this analytically...
I would look at map projections, which are designed for this purpose.
A search on "map projections matlab" yields documentation on a Matlab mapping toolbox. However, if you want or need to roll your own, there is a good summary at the USGS website, as well as a wikipedia article.