What is that angle?

12

2

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'

J Atkin

Posted 2015-11-26T23:10:19.483

Reputation: 4 846

1Did I get a downvote? If so could the voter explane why? – J Atkin – 2015-11-28T05:12:18.667

Can we just display it, not save it to a file? – ev3commander – 2015-11-28T14:40:49.773

Sure, that's how all the other answers do it. Just print to the console the answer your program generates. – J Atkin – 2015-11-28T14:48:00.560

1@JAtkin I wouldn't worry about downvotes on a generally upvoted post. c: We all get that. – Addison Crump – 2015-11-28T15:29:03.097

Oh, I see. I wonder why I got one though... – J Atkin – 2015-11-28T15:30:44.917

Answers

7

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

Maltysen

Posted 2015-11-26T23:10:19.483

Reputation: 25 023

Wow, this is cool but I don't speak pyth. Would you mind adding an explanation? – J Atkin – 2015-11-27T00:20:58.940

1I wish JavaScript would have the same byte count on the other hand. – insertusernamehere – 2015-11-27T00:22:42.963

@insertusernamehere I wish groovy or scala could do this kind of golfing as well. – J Atkin – 2015-11-27T00:30:38.933

@JAtkin explanation added. Feel free to message me on chat if you have any questions. – Maltysen – 2015-11-27T01:00:53.800

9

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.

insertusernamehere

Posted 2015-11-26T23:10:19.483

Reputation: 4 551

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/180PI;)getImageData(50+48cos(r),50+48*sin(r),1,1).data[1]||alert((450-a)%360)}` – user81655 – 2015-11-27T00:56:36.277

1This 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. – Ismael Miguel – 2015-11-27T18:17:56.760

@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? – insertusernamehere – 2015-11-28T09:59:46.063

@insertusernamehere Yeah, you can ignore the byte count. But you have to specify that it is an anonymous function – Ismael Miguel – 2015-11-28T14:57:33.223

5

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))

flawr

Posted 2015-11-26T23:10:19.483

Reputation: 40 560

1Would it be shorter to find the argument of the mean of all of the points on the line? – lirtosiast – 2015-11-26T23:57:04.907

Wow, this is far shorter than I expected answers to be, nice job! – J Atkin – 2015-11-26T23:58:28.210

@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! – flawr – 2015-11-26T23:58:36.387

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. – lirtosiast – 2015-11-27T00:00:05.730

@JAtkin Thanks! That was a nice just-before-going-to-bed challenge=P – flawr – 2015-11-27T00:00:17.400

1@ThomasKwa Great idea, thanks! The accuracy is even better now and the code is a few bytes shorter=) – flawr – 2015-11-27T00:06:11.950

5

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!

David

Posted 2015-11-26T23:10:19.483

Reputation: 1 316

1Your code can be reduced to 77 bytes by replacing the part before mode(...) with [I,J]=find(~im2bw(imread(input('')))); – slvrbld – 2015-11-27T10:34:48.093

Nice one! Thanks I was sure there was a way to do that more easily but couldn't remember it. – David – 2015-11-28T01:48:12.230

3

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.

Eumel

Posted 2015-11-26T23:10:19.483

Reputation: 2 487

1Nice, this is interesting. You may want to ask on meta how to score labview programs. – J Atkin – 2015-11-27T15:35:41.640

there already is a thread on how to score but unfortunatly there is no answer yet – Eumel – 2015-11-27T15:36:50.280

Oh, I see. I just edited your post to make the byte count more understandable to us in the US of A. – J Atkin – 2015-11-27T15:39:51.560

@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? – Aaron – 2015-11-27T17:04:19.380

Hehehe, I forgot you guys have , for decimal places. – J Atkin – 2015-11-27T17:48:59.220

I have edited your post to use a space bar instead of the comma. I also edited the capitalisation because my edit will only get accepted if it is more than 6 characters. – user41805 – 2015-11-28T10:02:55.583