## QR Code in shopping cart handle

198

84

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.

Here is some test QR code:

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]


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]


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.

– Jacob Akkerboom – 2013-11-27T14:04:33.797

2

This is a nice question. I believe guys doing street art in 3D kind of have the same problems ;-)

– halirutan – 2013-06-02T03:10:36.173

@halirutan Yes! Maybe they could use MMA too! :) – Murta – 2013-06-02T03:13:27.357

1Related question. – J. M.'s ennui – 2013-06-02T03:13:36.263

1

Another, arguably related question: (5676)

– Oleksandr R. – 2013-06-02T05:25:42.940

There are technologies that can encode different images for different angles. I think you can apply them locally, so you can see correct QR squares for a reasonable angle range.

– Silvia – 2013-06-02T06:12:06.593

1"I believe that it's equivalent to projecting the texture onto the surface, and then using the projected image." I think you want the inverse: you desire the image such that when it has been projected onto the cylinder, it look square. This should be the inverse of the mapping that takes the square to the cylinder. – bill s – 2013-06-02T09:01:01.947

5

I used Mathematica to find this http://i.stack.imgur.com/fZJe0.jpg

– Dr. belisarius – 2013-06-02T13:53:28.667

@belisarius hehe.. Too simple, no fun!.. – Murta – 2013-06-02T14:08:54.517

1

I think it falls into Mirror Anamorphosis. Also see this demo: Cylindrical Mirror Anamorphosis - not exactly what you need but in the same realm.

– Vitaliy Kaurov – 2013-06-02T17:34:23.693

8You should marry a package designer. My wife's response to this was, "The shopper's gotta work out just the right angle to photograph the code this way. Why not just introduce a little plastic structural element to make a flat enough surface for it? Something you can clip onto any shopping cart handle?" – Fordi – 2013-06-03T01:24:15.417

1

Could you not just attach a flat advertising section to the handle? Like this

– Tim Dorr – 2013-06-03T02:00:09.350

1This is definitely nitpicking, but even with proper projection, you couldn't make a tubular design like one illustrated on your question work! This is because QR codes, like may other codes, require "quiet zone" (essentially background colour) around the actual code. Typically this is explicitly included in encoder output. In the case of QR code, this zone is as wide and high as large alignment blocks on the code.

I hope the best for getting this work, but projections and real-world usage can be tricky. Choose as low "version" (pixel count) code as possible! – kirma – 2013-06-04T21:01:57.973

Not to take away from the awesome underlying problem, but I've seen shopping carts that aren't truly cylindrical (there'll be a flat edge on the side where people pushing the cart can read). – SheetJS – 2013-06-09T15:12:11.867

Yes!.. We know. See @belisarius comment joke link. :) – Murta – 2013-06-09T15:19:42.623

192

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:

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

## 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:

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

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}]}]


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}]
]


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}}]


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

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

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

## 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.

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

awesome! Here from another perspective

+1000 for the bloody thesis – Jakob Bowyer – 2014-01-02T21:35:23.743

Very cool real photo! In the first cylindric picture, how can I get the flat projected red square (or QR)? In some way I could use it as a texture again. Do you know how can I project and take the image back to reuse it? – Murta – 2013-06-02T14:03:27.327

The video example is perfect. Can you post the code of the projected texture? – Murta – 2013-06-02T14:20:40.060

@Murta OK, was a bit of work but finally I'm finished editing my answer. – halirutan – 2013-06-02T14:51:34.507

One last question! How can I unfold the last cylinder picture for a "print version" image? – Murta – 2013-06-02T15:03:16.270

@Murta Please see the last part again. I had a bug in creating the print-version. I fixed that and it looks great now. – halirutan – 2013-06-02T17:57:32.940

4Great work! Beautiful post! Thanks. – Murta – 2013-06-02T19:10:45.667

Yay! Anamorphic all out! – Yves Klett – 2013-06-02T21:00:40.317

I played with the code, and my projection worked just fine with ipf[2 projection[{u, v}] + {1/2, 1/2}] instead of ipf[3 projection[{u, v}] + {1/3, 0}]. Tks again! – Murta – 2013-06-02T22:14:58.677

This is the best answer I've ever seen on a .SE site. – Chris Gregg – 2013-06-03T10:09:06.900

4I created a mathematica SE account just so I could upvote this answer. Well done, sir. – Jason – 2013-06-03T16:57:51.063

2@Jason, Welcome. You should stick around to see the amazing power of Mathematica on display here. – RunnyKine – 2013-06-03T17:47:26.940

53

Here's a way to approach this. First import the QR coded image:

tag=Import["http://i.stack.imgur.com/SVKj3.png"]


In order to see what is happening, generate a regular grid

grid = Image[Plot3D[0, {x, 0, 1}, {y, 0, 1}, ViewPoint -> {0, 0, \[Infinity]},
Lighting -> "Neutral", Boxed -> False, Axes -> False]]


and a mapping that stretches the grid in the proper way. The function needs to map within the unit square in such a way that it has derivative = 1 in the middle and stretches the grid at the top and bottom. One way to accomplish this is with an ArcTan:

h[x_] := (ArcTan[5 (x - 1/2)] + Pi/2)/Pi;
Plot[h[x], {x, 0, 1}]


The effect of the mapping on the grid is

ImageTransformation[grid, {#[[1]], h[#[[2]]]} &, {400, 600}]


to stretch the edges and leave the middle pretty much untouched (as is required). All that remains is to transform the tag itself:

 imgS = ImageTransformation[tag, {#[[1]], h[#[[2]]]} &, {400, 600}]


If you were to cut this out and wrap it around the handle of the shopping cart, it would be much squarer overall. Of course, it will be necessary to match the exact mapping to the proper diameter of the handle, and to be a bit more careful with the top and bottom edges.

Now for the horizontal bits (as discussed in the comments). Here's the tag parameterized onto the cylinder:

ParametricPlot3D[{Cos[theta], Sin[theta], rho}, {theta, 0, Pi}, {rho, 0, 2},
PlotStyle -> Directive[Specularity[White, 30], Texture[tag]],
TextureCoordinateFunction -> ({#1, #3} &), Lighting -> "Neutral",
Mesh -> None, PlotRange -> All, TextureCoordinateScaling -> True,
ViewPoint -> {0, 10, 0}, Boxed -> False, Axes -> False]


But what we need is to flip this around so that it bulges in rather than out... this is easily done by changing the mapping slightly:

ParametricPlot3D[{Cos[theta], -Sin[theta], rho}, {theta, 0, Pi}, {rho, 0, 2},
PlotStyle -> Directive[Specularity[White, 30], Texture[tag]],
TextureCoordinateFunction -> ({#1, #3} &), Lighting -> "Neutral",
Mesh -> None, PlotRange -> All, TextureCoordinateScaling -> True,
ViewPoint -> {0, 10, 0}, Boxed -> False, Axes -> False]


Now we want to combine the vertical and horizontal stretchings, so we can apply the same idea to the stretched image from above:

ParametricPlot3D[{Cos[theta], -Sin[theta], rho}, {theta, 0, Pi}, {rho, 0, 2},
PlotStyle -> Directive[Specularity[White, 30], Texture[imgS]],
TextureCoordinateFunction -> ({#3, #1} &), Lighting -> "Neutral",
Mesh -> None, PlotRange -> All, TextureCoordinateScaling -> True,
ViewPoint -> {0, 10, 0}, Boxed -> False, Axes -> False]


With a bit of luck, it should be possible to choose the mappings so as to work with the actual physical measurements of the shopping cart handle.

Very cool bill, but the horizontal distortion is necessary too. – Murta – 2013-06-02T13:53:06.510

The above is just for the case where you look at it from straight on. But no reason you couldn't add another distortion function for the other direction in the same manner. – bill s – 2013-06-02T13:58:57.077

@murta puzzled as to why horizontal distortion is necessary? You have to stand in one location - why not square on? – cormullion – 2013-06-02T14:14:08.713

4

@cormullion even in the same location it's not ok. See this picture where I tried to illustrate it (red lines). Without horizontal distortion, the final result isn't a square.

– Murta – 2013-06-02T14:40:21.423

@murta ah I see! Could be worth adding to the question... – cormullion – 2013-06-02T14:49:25.027

the horizontal distortion has now been added – bill s – 2013-06-14T12:36:18.190