Drawing an Image Based on a Trace of its Hue

5

4

Introduction

Local genius Calvin's Hobbies recently challenged us to trace the hue of an image. It is recommended to read his challenge beforehand, but to highlight the main points:

  • The trace of hue in an image can be found by starting at one pixel, finding its hue angle(hue = atan2(sqrt(3) * (G - B), 2 * R - G - B)), and moving one pixel's width in that direction. If this process is repeated, and a black pixel is left at each pixel passed, we produce a trace of the image.

Here is a simple example of the trace:

enter image description here

We start at pixel 250, 250, with RGB color 36, 226, 132. We observe that the hue of this color is 150. Thus, we begin drawing a black line 150 degrees counterclockwise from the positive x-axis. We run into the square to the left, with RGB color 100, 220, 7 and hue 94. This results in an almost vertical line to the next square. We continue the pattern into every square, and observe the change in slope at each new color. The length of this trace was 300 pixels. If you're wondering why the image is so ugly, it's because I randomly generated 25 colors in 100x100 squares and used my answer to trace it. On a much more complex and beautiful image, we may get a trace like this one:

HueTest3.bmp

  • Calvin created this wonderful code snippet on the original post that draws a trace over an image, starting at the point provided by the mouse cursor. This is a very useful testing tool!

<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script><style>canvas{border:1px solid black;}</style>Load an image: <input type='file' onchange='load(this)'><br><br>Max length <input id='length' type='text' value='300'><br><br><div id='coords'></div><br><canvas id='c' width='100' height='100'>Your browser doesn't support the HTML5 canvas tag.</canvas><script>function load(t){if(t.files&&t.files[0]){var e=new FileReader;e.onload=setupImage,e.readAsDataURL(t.files[0])}}function setupImage(t){function e(t){t.attr("width",img.width),t.attr("height",img.height);var e=t[0].getContext("2d");return e.drawImage(img,0,0),e}img=$("<img>").attr("src",t.target.result)[0],ctx=e($("#c")),ctxRead=e($("<canvas>"))}function findPos(t){var e=0,a=0;if(t.offsetParent){do e+=t.offsetLeft,a+=t.offsetTop;while(t=t.offsetParent);return{x:e,y:a}}return void 0}$("#c").mousemove(function(t){function e(t,e){var a=ctxRead.getImageData(t,e,1,1).data,i=a[0]/255,r=a[1]/255,o=a[2]/255;return Math.atan2(Math.sqrt(3)*(r-o),2*i-r-o)}if("undefined"!=typeof img){var a=findPos(this),i=t.pageX-a.x,r=t.pageY-a.y;$("#coords").html("x = "+i.toString()+", y = "+r.toString());var o=parseInt($("#length").val());if(isNaN(o))return void alert("Bad max length!");for(var n=[i],f=[r],h=0;n[h]>=0&&n[h]<this.width&&f[h]>=0&&f[h]<this.height&&o>h;)n.push(n[h]+Math.cos(e(n[h],f[h]))),f.push(f[h]-Math.sin(e(n[h],f[h]))),h++;ctx.clearRect(0,0,this.width,this.height),ctx.drawImage(img,0,0);for(var h=0;h<n.length;h++)ctx.fillRect(Math.floor(n[h]),Math.floor(f[h]),1,1)}});</script>

Snippet tested in Google Chrome

  • Answers to that challenge may also serve as appropriate testing tools for this challenge.

Note: I do not take any credit for Calvin's work, and have received explicit permission to use his ideas in this challenge.

The Challenge

In that challenge, we found the trace. Now, I'm asking you to find the image. You will be given a start point, an end point, and a length, and your program must output an image whose trace beginning at the start point arrives at the end point in approximately length pixels. Moreover, because there are so many options, your program's algorithm must be inherently random in some regard--thus the same input should (more often than not) produce a different output image.

Input

You will receive seven positive integers as input: w, h, x1, y1, x2, y2, len where:

  • x1, x2 < w <= 1920

  • y1, y2 < h <= 1080

  • len < 2000

These guidelines are minimum requirements. Your program may accept higher values.

You may receive input through stdin, a file, or as arguments to a function. You will ONLY receive these 5 integers as input.

Output

As output, produce an image of width w and height h with a continuous hue trace from x1,y1 to x2, y2 in approximately len pixel-width steps.

  • Output should be random.
  • Output may be in any format--whether it be .bmp, .png, PPM, or an image shown on-screen at runtime makes no difference.
  • You may assume it is possible to move from x1, y1 to x2, y2 in under len pixels.
  • Output should NOT show the path of the trace from x1, y1 to x2, y2--though an image showing this path in your answer is appreciated.

Rules

  • This is a popularity-contest! Try to make your output beautiful or interesting. More interesting hue traces than straight lines are also recommended.
  • This is not code-golf. As such, readable code is appreciated. Still, try to keep size below ~2KB. This rule will not be strictly enforced.
  • Values are approximate. If your code's trace reaches its destination within ~5-10% of len pixels, this is acceptable. Along this same vein, do not worry about slightly incorrect hue angle values. Some error is reasonable.
  • This is graphical-output. Please include output images of your solution in your answer.
  • Standard Loopholes apply.

Questions, comments, concerns? Let me know!

BrainSteel

Posted 2015-03-17T21:41:01.047

Reputation: 5 132

I think you should explain a bit more about how the direction of the black line in the image is generated (perhaps with a simpler image?). Explain that the line starts out going left because the hue of light blue is 180°, and curves down because as the blue gets darker the hue angle increases. You are also welcome to repost and even edit the snippet from my question. – Calvin's Hobbies – 2015-03-17T22:27:38.067

1This might work better as a [tag:code-golf], though then you'd need to be more specific on what constitutes "randomness". However a pop-con may let people show off some neater ways to make these images. I also think it'd be "purer" to give the image dimensions as part of the input, though this is really a matter of opinion. – Calvin's Hobbies – 2015-03-17T22:33:49.583

@Calvin'sHobbies Thanks for the feedback! I will definitely try to think of a way to concisely explain the hue. Let me work on it a bit, and I'll see if I can put together an explanation. On making it code-golf, I have one pretty serious problem with that. If you were to take atan((y1-y2)/(x1-x2)) you could quite easily find a "random" color whose hue is this angle. You could then draw a solid line of this color between the points on a white background, divert a bit to account for length, and boom. I really want to promote creativity with this question, and I don't think code-golf does that. – BrainSteel – 2015-03-17T22:54:08.467

@Calvin'sHobbies I think you're right about width and height. I'll make it take 7 arguments. – BrainSteel – 2015-03-17T22:56:45.400

Answers

7

All the code is on bitbucket(it is completely unoptimised). The hue path tracing logic is my own so might differ slightly from that in the original question. Instead of trying to work out a function to generate a line and then an image from that, it makes multiple sets of random changes to a starting image and then picks the set where the end of the path is closest to the target end, then repeats. No Path enter image description here

I think it looks kinda artistic sometimes. The current randomisation method just draws randomly coloured rectangles, if you run the program yourself you can watch it iterating. With 60 generations (and 70 children per generation) it basically always gets within a pixel of the target. I tried swapping individual pixels but that was too slow. Plain rectangles was the best (simple) way of changing the image that I tried which still created interesting output. I might see how well circles work later.

bhh

Posted 2015-03-17T21:41:01.047

Reputation: 186

Thank you! This is an awesome answer! I like the idea of iteratively testing random rectangles. Do you have an estimate of how close this usually gets to the exact pixel? (Also, if you could put what input you gave it for the output shown, that'd be great!) – BrainSteel – 2015-03-22T17:54:23.770

Inputs were (800, 650), (500,50), (60,310) ,700. I didn't seed the rng in that commit though so you probably aren't able to recreate it (although I have in a newer commit). See edit for how close it gets. – bhh – 2015-03-22T18:20:12.653