Take the 2-minute tour ×
Mathematica Stack Exchange is a question and answer site for users of Mathematica. It's 100% free, no registration required.

I wrote a simple application(I put the code in the end) to detect the parallel line pairs in the following picture: enter image description here

It returned three pairs of parallel line:

{{{{584., 222.175}, {39.6747, 0.}}, {{0., 377.639}, {390.613, 472.}}}, 
{{{0., 403.894}, {306.413, 0.}}, {{0., 336.953}, {238.095, 0.}}}, 
{{{584., 186.489}, {127.104, 0.}}, {{0., 377.639}, {390.613, 472.}}}}

And then I want to show every result individually. Here's the way I output the result, where img is the test picture and parLines is the detected parallel line pairs.

For[i = 1, i <= Length[parLines], i++,
Show[img, Graphics[{Thick, Orange, Line /@ parLines[[i]]}]]]

I expected it to output several images and a pair of parallel lines on each. The number of images depend on the number of pairs of parallel line. However, it didn't draw anything. But if I printed them one by one, it worked fine.

Show[img, Graphics[{Thick, Orange, Line /@ parLines[[1]]}]]

enter image description here

Show[img, Graphics[{Thick, Orange, Line /@ parLines[[2]]}]]

enter image description here

Show[img, Graphics[{Thick, Orange, Line /@ parLines[[3]]}]]

enter image description here

I can't understand where the problem is. Thanks for any help.


Here's the code:

img = Import["http://www.zechini.it/webtv/upload/book.jpg"];
edge = DeleteBorderComponents[EdgeDetect[img]];
lines = ImageLines[edge, 0.1];

NumofLines = Length[lines];
flag = False;
parLines = {};
For[i = 1, i <= NumofLines, i++,
  For[j = i + 1, j <= NumofLines, j++,
    line1 = lines[[i]];
    line2 = lines[[j]];
    vec1 = line1[[1]] - line1[[2]];
    vec2 = line2[[1]] - line2[[2]];
    para = 1 - ArcCos[Abs[(vec1.vec2)/Norm[vec1]/Norm[vec2]]]/(Pi/2);
    If[para > 0.9, flag = True; AppendTo[parLines, {line1, line2}];];
    ];
  ];

If[flag == True,
 For[i = 1, i <= Length[parLines], i++,
  Show[img, Graphics[{Thick, Orange, Line /@ parLines[[i]]}]]], 
 Print["Cannot detect parallel line"]]
share|improve this question
add comment

4 Answers

up vote 5 down vote accepted

Or perhaps:

Show[img, Graphics[{Thick, Orange, Line[#]}]] & /@ parLines
  // GraphicsRow

books

share|improve this answer
add comment

This is probably what you want (whenever you're using For loops to generate a static result like a combined Graphics, you have to be suspicious that you're doing it wrong):

Show[img, 
 Table[Graphics[{Thick, Orange, Line /@ parLines[[i]]}], {i, 1, 3}]]

book

In words: replace the For loop by a Table inside the Show if you want all lines to be shown in the same Graphics.

In case I misunderstood: if you want to really use a For loop despite all this, you can force output of the Show result at every step by replacing Show by Print@Show. This is needed because Graphics is only shown when it's the last statement in a compound expression (and not followed by a ; which stands for CompoundExpression). But in the For loop, the Show is not the last command that gets executed.

share|improve this answer
    
Actually I would like to use for loop to output every result individually, no in the same picture. Therefore I think Print@Show will suit my purpose –  AlbertK Aug 16 '13 at 10:20
add comment

Though this isn't an extension to the other answers you've received it might give you some insight into the Mathematica/Functional style of problem solving.

img = Import["http://www.zechini.it/webtv/upload/book.jpg"];
lines = ImageLines[DeleteBorderComponents[EdgeDetect[img]], 0.1];

By removing many of the redundant intermediate variables and concentrating on manipulating the data as a sequence of transformations the main body of code can be reduced to one line, plus one function definition.

Define a function that returns true or false if a pair of vectors are parallel:

parallel[{vec1_, vec2_}] := (1-ArcCos[Abs[(vec1.vec2)/Norm[vec1]/Norm[vec2]]]/(Pi/2)) > 0.9

Then by creating a set of all the pairs of parallel lines we can then apply that to the result of building vectors from the pairs of lines:

parLines = With[{l=Subsets[lines, {2}]}, Pick[l, parallel/@Apply[Subtract, l, {2}] & /@ l)]];

Then display the results as per cormullion's answer:

Show[img, Graphics[{Thick, Orange, Line[#]}]] & /@ parLines // GraphicsRow
share|improve this answer
    
(+1) Is VectorAngle any use here? –  cormullion Aug 16 '13 at 14:36
    
I think you could use Apply[Subtract, l, {2}] in place of ((Subtract @@ # & /@ #) & /@ l). Perhaps it is even clearer to use Select instead of Pick: Select[l, parallel[Subtract @@@ #] &] –  Simon Woods Aug 16 '13 at 15:39
    
Impressive! That is very conise. I'm a beginner to Mathematica, and since I'm more familiar with C++ programming, my code looks C++-style. However, as a beginner, it is difficult for me to understand the parLines line you write. I can't imagine the output of every operation performed. I have to take that line apart and output every segments to understand how it works. Sometimes I even don't know how to decompose it! I would like to know what the core spirit it is to write the code so concise, and how? Well, I admit that my code up there is ugly :) –  AlbertK Aug 18 '13 at 15:46
    
Well...I don't know if worth a new thread, if so I'll post it as a new question later. –  AlbertK Aug 18 '13 at 15:52
add comment

Another approach is to use Manipulate to choose which lines to display:

Manipulate[Show[img, Graphics[{Thick, Orange, Line /@ parLines[[i]]}]], {i, 1, 3, 1}]

By moving the slider you can see each of the pairs of parallel lines in succession.

enter image description here

share|improve this answer
    
Or ListAnimate or FlipView ... –  Jens Aug 16 '13 at 21:31
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.