12
\$\begingroup\$

The goal of this challenge is to determine the angle of a line in a image.

Rules on the image:

  • The image background will be white (#FFFFFF)
  • The stroke of the line will be black (#000000)
  • The line will NOT be anti-aliased
  • The image will be 100x100 pixels
  • The line will start at the center of the image
  • The line will start pointing down (6-OClock)
  • The line will be 50 pixels long
  • The angle of the line will be measured going counterclockwise from the starting position
  • The image codec will be either .jpg or .png

Input format will be a file name passed by the command line arg, script input, or function arg. Output format is simple - just output the number of degrees (e.g. 90).

Answers can be ±1 degree of the stated measure. Here are a few example images:

1

A reference image at 45 degrees with gray background

1

0 degrees

2

45 degrees

3

50 degrees

4

130 degrees

6

230 degrees

7

324 degrees

Here is the code used to create the images (this is coded with Processing):

int deg = 45;

int centX = width/2, centY = height/2;

background(255);
noSmooth();
line(centX,
     centY,
     centX + sin(radians(deg))*50,
     centY + cos(radians(deg))*50);

saveFrame("line-"+deg+".png");// image codec can be changed here. use '.png' or '.jpg'
\$\endgroup\$
  • 1
    \$\begingroup\$ Did I get a downvote? If so could the voter explane why? \$\endgroup\$ – J Atkin Nov 28 '15 at 5:12
  • \$\begingroup\$ Can we just display it, not save it to a file? \$\endgroup\$ – ev3commander Nov 28 '15 at 14:40
  • \$\begingroup\$ Sure, that's how all the other answers do it. Just print to the console the answer your program generates. \$\endgroup\$ – J Atkin Nov 28 '15 at 14:48
  • 1
    \$\begingroup\$ @JAtkin I wouldn't worry about downvotes on a generally upvoted post. c: We all get that. \$\endgroup\$ – Addison Crump Nov 28 '15 at 15:29
  • \$\begingroup\$ Oh, I see. I wonder why I got one though... \$\endgroup\$ – J Atkin Nov 28 '15 at 15:30
7
\$\begingroup\$

Pyth - 28 26 bytes

Uses same sort of brute force strategy as the js answer.

f!@F+]'zm+50s*48.t.tT7d_U2

Takes input as filename from stdin.

f                     Filters from 1 till predicate is matched
 !                    Boolean not so that only pixel with zero value matched
  @F+]                Folds by indexing to get pixel value  
   'z                 Reads image filename input
   m         _U2      Maps over both trig ratios
    +50               Adds 50 to pixel value
     *48              Multiplies pixel value by 48
      .t    d         Takes trig ratio with appropriate option
        .t 7          Degrees to radians
          T           Filter var
\$\endgroup\$
  • \$\begingroup\$ Wow, this is cool but I don't speak pyth. Would you mind adding an explanation? \$\endgroup\$ – J Atkin Nov 27 '15 at 0:20
  • 1
    \$\begingroup\$ I wish JavaScript would have the same byte count on the other hand. \$\endgroup\$ – insertusernamehere Nov 27 '15 at 0:22
  • \$\begingroup\$ @insertusernamehere I wish groovy or scala could do this kind of golfing as well. \$\endgroup\$ – J Atkin Nov 27 '15 at 0:30
  • \$\begingroup\$ @JAtkin explanation added. Feel free to message me on chat if you have any questions. \$\endgroup\$ – Maltysen Nov 27 '15 at 1:00
9
\$\begingroup\$

JavaScript (ES6), 225 227 244 bytes

Let's get the ball rolling:

f=s=>{(i=new Image).src=s;c=document.createElement`canvas`.getContext`2d`;c.drawImage(i,0,0,100,100);for(a=360;a--,r=a/180*(m=Math).PI;)if(!c.getImageData(50+48*m.cos(r),50+48*m.sin(r),1,1).data[1]){alert((450-a)%360);break}}

Simply pass the URL of the image to the function:

f('90deg.png');

Alerts degrees within the ±1 range. Passed all test cases.

Ungolfed

f=s=>{
    // create new image and set source
    (i=new Image).src=s;
    // create canvas and get context
    c=document.createElement`canvas`.getContext`2d`;
    // set width/height to 100px and draw image on canvas
    c.drawImage(i,0,0,100,100);
    // check whether for any degree on the theoretical circle a black pixel is found
    for(a=360;a--,r=a/180*(m=Math).PI;)
        if(!c.getImageData(50+48*m.cos(r),50+48*m.sin(r),1,1).data[1]){
            // wait, it should be ccw and the board is rotated 90 degrees
            alert((450-a)%360);
            break
        }
}

Edits

  • Saved 17 bytes – figured I don't need to set the width and height of the canvas element.
  • Saved 2 bytes by negating the condition.
\$\endgroup\$
  • \$\begingroup\$ I think this should work (haven't tested it). 206 bytes: s=>{(i=new Image).src=s;with(Math)with(document.createElement`canvas`.getContext`2d`)for(drawImage(i,0,0,100,100),a=360;r=--a/180*PI;)getImageData(50+48*cos(r),50+48*sin(r),1,1).data[1]||alert((450-a)%360)} \$\endgroup\$ – user81655 Nov 27 '15 at 0:56
  • 1
    \$\begingroup\$ This code works because you are lucky. The canvas will be tainted almost everytime. Specially with file://. You need to set the crossOrigin property. Also, it won't work if the image loading takes 0.00001 seconds more than creating the canvas does. Also, you don't need the f=, cutting off 2 bytes. But it is a nice solution indeed!!! My upvote for it. \$\endgroup\$ – Ismael Miguel Nov 27 '15 at 18:17
  • \$\begingroup\$ @IsmaelMiguel Thanks for your detailed feedback. You're right about the canvas. In the beginning I tried to rotate and mirror the image, so that the angle doesn't need to be transformed. You can say good-bye to that! Got blurry, couldn't find the right pixel. I've skipped the onload part as I was undercut in another challenge because of that. So I thought it's ok to assume that it loads fast enough. Regarding the anonymous function I'm not sure how to count it. If I cut off f= and I want to invoke it I have to wrap it in () like (s=>{})('arg');. Can I ignore this in byte count? \$\endgroup\$ – insertusernamehere Nov 28 '15 at 9:59
  • \$\begingroup\$ @insertusernamehere Yeah, you can ignore the byte count. But you have to specify that it is an anonymous function \$\endgroup\$ – Ismael Miguel Nov 28 '15 at 14:57
5
\$\begingroup\$

Matlab, 118 104 bytes

I generate a matrix of the same size as the image with complex numbers (0 in the center) and exctract from that matrix the values which are on the line. The argument of the mean of those is then displayed.

Thanks to @ThomasKwa for suggesting an improvement in accuracy which also resulted in shorter code!!!

I=imread(input('','s'));
[y,x]=ndgrid(-50:49);
c=y+i*x;
disp(mod(angle(mean(c(~I(:,:,1))))*180/pi+360,360))
\$\endgroup\$
  • 1
    \$\begingroup\$ Would it be shorter to find the argument of the mean of all of the points on the line? \$\endgroup\$ – lirtosiast Nov 26 '15 at 23:57
  • \$\begingroup\$ Wow, this is far shorter than I expected answers to be, nice job! \$\endgroup\$ – J Atkin Nov 26 '15 at 23:58
  • \$\begingroup\$ @ThomasKwa Absolutely, but then it would not be as accurate, as the pixels close to the center are absolutely inaccurate. If you want to try, you can run this code in Octave too, I think! \$\endgroup\$ – flawr Nov 26 '15 at 23:58
  • \$\begingroup\$ Argument of the mean (which should give the argument of the center of the line to a pretty good accuracy), not mean of the arguments. I don't know if the accuracy would be acceptable. \$\endgroup\$ – lirtosiast Nov 27 '15 at 0:00
  • 1
    \$\begingroup\$ @ThomasKwa Great idea, thanks! The accuracy is even better now and the code is a few bytes shorter=) \$\endgroup\$ – flawr Nov 27 '15 at 0:06
5
\$\begingroup\$

Matlab, 86 77 bytes

Here's another way using Matlab:

[I,J]=find(~im2bw(imread(input('','s'))));mode(mod(round(atan2d(J-51,I-51)),360))

This reads the file (stolen from flawr), and finds the indices of the black pixels. Then, it works out the vector that points from the centre of the image to each black pixel, and uses atan2d to find the angle, rounding to get integer angles, and doing mod(...,360) to get results in the right range. To get the correct angle (there is a bit of error for the pixels close to the centre), take the most commonly calculated angle.

Thanks to slvrbld for the im2bw suggestion!

\$\endgroup\$
  • 1
    \$\begingroup\$ Your code can be reduced to 77 bytes by replacing the part before mode(...) with [I,J]=find(~im2bw(imread(input('')))); \$\endgroup\$ – slvrbld Nov 27 '15 at 10:34
  • \$\begingroup\$ Nice one! Thanks I was sure there was a way to do that more easily but couldn't remember it. \$\endgroup\$ – David Nov 28 '15 at 1:48
3
\$\begingroup\$

Labview, 10098 Bytes

Let's put another labview code out there.

Since there is no official way to count bytes in labview i use the size of the file when saved. Alternatively counting every wire and function as 1and the case as 2 it would come out to 71.

1

Load image, flatten to 1D, scan for 0s from both sides and take the first, calc back to point and use geometry to get angle.

\$\endgroup\$
  • 1
    \$\begingroup\$ Nice, this is interesting. You may want to ask on meta how to score labview programs. \$\endgroup\$ – J Atkin Nov 27 '15 at 15:35
  • \$\begingroup\$ there already is a thread on how to score but unfortunatly there is no answer yet \$\endgroup\$ – Eumel Nov 27 '15 at 15:36
  • \$\begingroup\$ Oh, I see. I just edited your post to make the byte count more understandable to us in the US of A. \$\endgroup\$ – J Atkin Nov 27 '15 at 15:39
  • \$\begingroup\$ @JAtkin As a european fellow it made me scratch my head, wondering how he got those fractions of byte. Wouldn't using a space please all sides? \$\endgroup\$ – Aaron Nov 27 '15 at 17:04
  • \$\begingroup\$ Hehehe, I forgot you guys have , for decimal places. \$\endgroup\$ – J Atkin Nov 27 '15 at 17:48

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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