Skip to main content

image processing - QR Code in shopping cart handle


This week, the market people from my work wanted to put QR codes in shopping cart handles, but when they tested it, the QR code did not work. I noted that the cylindrical curvature (even small) distorted the image, and the cell phone can't read it.


diagram


Here is some test QR code:


qrCode


I thought that this would be a nice thing to do with Mathematica, to try to figure out how I could print the QR code into some way that when attached to the cylindrical form, it would be like a normal square, at least at some angles.



I tried to simulate how it would be plotted in the handle using Texture, and I get this:


qrCode = Import["http://i.stack.imgur.com/FHvNV.png"];
RevolutionPlot3D[{1, t}, {t, 0, 20}, Mesh -> None, PlotStyle -> Texture[qrCode],
TextureCoordinateScaling -> False, Lighting -> "Neutral", ImageSize -> 100]

cylinder with QR code


Here is some code from the docs for Texture that I tried to adapt, without success:


RevolutionPlot3D[{1, t}, {t, 0, 1}, PlotStyle -> Texture[qrCode], Mesh -> None,
TextureCoordinateFunction -> ({#3, #2} &), Axes -> False,
Lighting -> "Neutral", ImageSize -> 300, AspectRatio -> 1]


texture


Does anyone know how I can distort the QR code, in a way? I believe that it's equivalent to projecting the texture onto the surface, and then using the projected image.


update: This problem is very similar to this, the difference is that we are in the cylinder, and this anamorphic illusion example is in the plane.



Answer



First of all: A comprehensive outline of the following idea without any mathematical formulas but with detailed explanations can be found here on on 2d-codes.co.uk or, if you happen to speak danish here on http://qrkoder.internet.dk/.


Teaser


The answer below works (with some modifications). Please click the image to see how the QR code projection looks, when the image is rotated:


enter image description here


And everything here can be used for real applications. At the end of this answer you'll find images of the QR code printed on a real cylinder. But applications are not restricted to this. You can easily adapt the approach to keep you up all night



red bull small 1


red bull small 2


The theory


Murta, you wrote



I thought that this would be a nice thing to do with Mathematica, to try to figure out how I could print the QR code into some way that when attached to the cylindrical form, it would be like a normal square, at least at some angles.



Exactly the viewpoint, more specifically the perspective projection, is crucial to determine how you have to transform your label so that it is squared again. Let me give an example where I drew something onto a paper-roll which obviously has nothing to do with the transformation used in bills answer:


enter image description here


If I now inspect this roll from a specific viewpoint it looks like a QR code should be recognized again:



enter image description here


The question is what happens here. The theory behind it is pretty easy and the good thing is, it explains what you have to do from any (meaningful) viewpoint. Let's use a simple cylinder graphic as example to explain what I mean


ParametricPlot3D[{Cos[u], v, Sin[u]}, {u, 0, 2 Pi}, {v, 0, 10}, 
Boxed -> False, Axes -> False, ViewAngle -> .1,
Epilog :> {FaceForm[None], EdgeForm[Red],
Rectangle[{.4, .4}, {.7, .7}]}]

enter image description here


When you finally see the image on your screen, two transformations took place. First, ParametricPlot3D used my formula to transform from cylinder coordinates {u,v} into 3D Cartesian coordinates {x,y,z}. This transformation of the {u,v} plane can easily be simulated by sampling it with Table, doing the transformation to 3D by yourself and drawing lines


Graphics3D[{Line[#], Line[Transpose@#]} &@

Table[{Cos[u], v, Sin[u]}, {u, 0, 2 Pi, 2 Pi/20.}, {v, 0, 10, .5}]
]

enter image description here


The next thing that happens is often taken for granted: The transformation of 3D points onto your final image plane you are seeing on the screen. This final ViewMatrix can (with some work) be extracted from a Mathematica graphics. It should work with AbsoluteOptions[gr3d, ViewMatrix] but it doesn't. Fortunately, Heike posted an answer how to do this.


Let's do it


OK, to say it with the words of Dr. Faust "Grau, teurer Freund, ist alle Theorie, und grün des Lebens goldner Baum". After trying it I noticed that the last two paragraphs of my first version are not necessary.


Let us first create a 3D plot of a cylinder, where we extract the matrices for viewing and keep them up to date even when we rotate the view.


{t, p} = {TransformationMatrix[
RescalingTransform[{{-2, 2}, {-2, 2}, {-3/2, 5/2}}]],

{{1, 0, 0, 0}, {0, 0, 1, 0}, {0, 1, 0, 0}, {0, 0, 0, 1}}};
ParametricPlot3D[{Cos[u], v, Sin[u]}, {u, 0, 2 Pi}, {v, 0, 10},
Boxed -> False, Axes -> False, ViewMatrix -> Dynamic[{t, p}]]

Now {t,p} always contain the current values of our projection. If you read in the documentation to ViewMatrix, you see that



The transformation matrix t is applied to the list {x,y,z,1} for each point. The projection matrix p is applied to the resulting vectors from the transformation.



and




If the result is {tx,ty,tz,tw}, then the screen coordinates for each point are taken to be given by {tx,ty}/tw.



Therefore, we can easily construct a function from {u,v} to screen-coordinates {x,y}


With[{m1 = t, m2 = p},
projection[{u_, v_}] = {#1, #2}/#4 & @@ (m2.m1.{Cos[u], v, Sin[u], 1})
]

Let's test wether our projection is correct. Rotate the cylinder graphics so that you have a nice view and execute the projection definition again.


Graphics[{Line /@ #, Line /@ Transpose[#]} &@
Table[projection[{u, v}], {u, 0, 2 Pi, .1}, {v, 0, 10}],

Axes -> True, PlotRange -> {{0, 1}, {0, 1}}]

enter image description here


Please note that this is no 3D graphics. We transform directly from {u,v} cylinder to {x,y} screen-coordinates. Those screen-coordinates are always in the range [0,1] for x and y.


Now comes the important step: This transformation can directly be used with TextureCoordinateFunction because this function provides you with {u,v} values and wants to know {x,y} texture positions. The only thing I do is, that I scale and translate the texture coordinates a bit so that the QR code is completely visible in the center of the image:


tex = Texture[Import["http://i.stack.imgur.com/FHvNV.png"]];
ParametricPlot3D[{Cos[u], v, Sin[u]}, {u, 0, 2 Pi}, {v, 0, 10},
Boxed -> False, Axes -> False, ViewMatrix -> Dynamic[{t, p}],
PlotStyle -> tex, TextureCoordinateScaling -> False,
Lighting -> "Neutral",

TextureCoordinateFunction -> (2 projection[{#4, #5}] + {1/2, 1/2} &)
]

Don't rotate this graphics directly, because although it uses specific settings for ViewMatrix, it jumps directly to default settings when rotated the first time. Instead, copy our original cylinder image to a new notebook and rotate this. The Dymamic's will make, that both graphics are rotated.


Conclusion: When I use the following viewpoint to initialize the view point


enter image description here


and then evaluate the projection definition line again and recreate the textured cylinder, I get


enter image description here


which looks as if I just added a QR code layer to the image. Rotating and scaling reveals that it is specific texture projection instead


enter image description here



Going into real life


When you want to create a printable version of this, you could do the following. Interpolate the QR code image and use the same projection function you used in the texture (note that I used a factor 3 and {1/3,0} inside ipf here. You use whatever you used as texture):


qr = RemoveAlphaChannel@
ColorConvert[Import["http://i.stack.imgur.com/FHvNV.png"],
"Grayscale"];
ip = ListInterpolation[
Reverse[ImageData[qr, "Real"]], {{0, 1}, {0, 1}}];
ipf[{x_, y_}] := ip[Mod[y, 1], Mod[x, 1]];

With[{n = 511.},

Image@Reverse@
Table[ipf[3 projection[{u, v}] + {1/3, 0}], {u, -Pi, Pi, 2 Pi/n},
{v, 0, 10, 2 Pi/n}]
]

Please note the Reverse since image matrices are always reversed and additionally, that I create now the image matrix for u from [-Pi,Pi]. This was a bug in the last version which created the back-side of the cylinder. Therefore, the perspective was not correct in the final result.


enter image description here


This can now be glued around a cylinder (after printing it with the appropriate height) and with the corrected print version, the result looks


enter image description here


awesome! Here from another perspective



enter image description here


Comments

Popular posts from this blog

plotting - Plot 4D data with color as 4th dimension

I have a list of 4D data (x position, y position, amplitude, wavelength). I want to plot x, y, and amplitude on a 3D plot and have the color of the points correspond to the wavelength. I have seen many examples using functions to define color but my wavelength cannot be expressed by an analytic function. Is there a simple way to do this? Answer Here a another possible way to visualize 4D data: data = Flatten[Table[{x, y, x^2 + y^2, Sin[x - y]}, {x, -Pi, Pi,Pi/10}, {y,-Pi,Pi, Pi/10}], 1]; You can use the function Point along with VertexColors . Now the points are places using the first three elements and the color is determined by the fourth. In this case I used Hue, but you can use whatever you prefer. Graphics3D[ Point[data[[All, 1 ;; 3]], VertexColors -> Hue /@ data[[All, 4]]], Axes -> True, BoxRatios -> {1, 1, 1/GoldenRatio}]

plotting - Filling between two spheres in SphericalPlot3D

Manipulate[ SphericalPlot3D[{1, 2 - n}, {θ, 0, Pi}, {ϕ, 0, 1.5 Pi}, Mesh -> None, PlotPoints -> 15, PlotRange -> {-2.2, 2.2}], {n, 0, 1}] I cant' seem to be able to make a filling between two spheres. I've already tried the obvious Filling -> {1 -> {2}} but Mathematica doesn't seem to like that option. Is there any easy way around this or ... Answer There is no built-in filling in SphericalPlot3D . One option is to use ParametricPlot3D to draw the surfaces between the two shells: Manipulate[ Show[SphericalPlot3D[{1, 2 - n}, {θ, 0, Pi}, {ϕ, 0, 1.5 Pi}, PlotPoints -> 15, PlotRange -> {-2.2, 2.2}], ParametricPlot3D[{ r {Sin[t] Cos[1.5 Pi], Sin[t] Sin[1.5 Pi], Cos[t]}, r {Sin[t] Cos[0 Pi], Sin[t] Sin[0 Pi], Cos[t]}}, {r, 1, 2 - n}, {t, 0, Pi}, PlotStyle -> Yellow, Mesh -> {2, 15}]], {n, 0, 1}]

plotting - Mathematica: 3D plot based on combined 2D graphs

I have several sigmoidal fits to 3 different datasets, with mean fit predictions plus the 95% confidence limits (not symmetrical around the mean) and the actual data. I would now like to show these different 2D plots projected in 3D as in but then using proper perspective. In the link here they give some solutions to combine the plots using isometric perspective, but I would like to use proper 3 point perspective. Any thoughts? Also any way to show the mean points per time point for each series plus or minus the standard error on the mean would be cool too, either using points+vertical bars, or using spheres plus tubes. Below are some test data and the fit function I am using. Note that I am working on a logit(proportion) scale and that the final vertical scale is Log10(percentage). (* some test data *) data = Table[Null, {i, 4}]; data[[1]] = {{1, -5.8}, {2, -5.4}, {3, -0.8}, {4, -0.2}, {5, 4.6}, {1, -6.4}, {2, -5.6}, {3, -0.7}, {4, 0.04}, {5, 1.0}, {1, -6.8}, {2, -4.7}, {3, -1....