Note: a 1000 point bounty is still available for this challenge, which I will create and award if anyone takes the top score without using built-in compression.
Below is a 386x320
png representation of van Gogh's Starry Night.
Your goal is to reproduce this image as closely as possible, in no more than 1024 bytes of code. For the purposes of this challenge, the closeness of images is measured by the squared differences in RGB pixel values, as explained below.
This is code-challenge. Scores are calculated using the validation script below. The lowest score wins.
Your code must obey the following restrictions:
- It must be a complete program
- It must output an image in a format that can be read by the validation script below, running on my machine. The script uses Python's PIL library, which can load a wide variety of file formats, including png, jpg and bmp.
- It must be completely self-contained, taking no input and loading no files (other than importing libraries, which is allowed)
- If your language or library includes a function that outputs Starry Night, you are not allowed to use that function.
- It should run deterministically, producing the same output every time.
- The dimensions of the output image must be
386x320
- For the avoidance of doubt: valid answers must use programming languages as per the usual PPCG rules. It must be a program that outputs an image, not just an image file.
It is likely that some submissions will themselves be generated by code. If this is the case, please include in your answer the code that was used to produce your submission, and explain how it works. The above restrictions only apply to the 1kB image-generating program that you submit; they don't apply to any code used to generate it.
Scoring
To calculate your score, take your output image and the original above and convert the RGB pixel values to floating point numbers ranging from 0 to 1. The score of a pixel is (orig_r-img_r)^2 +(orig_g-img_g)^2 + (orig_b-img_b)^2
, i.e. the squared distance in RGB space between the two images. The score of an image is the sum of the scores of its pixels.
Below is a Python script that performs this calculation - in the case of any inconsistency or ambiguity, the definitive score is the one calculated by that script running on my machine.
Note that the score is calculated based on the output image, so if you use a lossy format that will affect the score.
The lower the score the better. The original Starry Night image would have a score of 0. In the astronomically unlikely event of a tie, the answer with the most votes will determine the winner.
Bonus objectives
Because the answers were dominated by solutions using built-in compression, I awarded a series of bounties to answers that use other techniques. The next one will be a bounty of 1000 points, to be awarded if and when an answer that does not use built-in compression takes the top place overall.
The previously awarded bonus bounties were as follows:
A 100 point bounty was awarded to nneonneo's answer, for being the highest-scoring answer that did not use built-in compression at the time. It had 4852.87 points at the time it was awarded. Honourable mentions go to 2012rcampion, who made a valiant attempt to beat nneonneo using an approach based on Voronoi tesselation, scoring 5076 points, and to Sleafar, whose answer was in the lead until near the end, with 5052 points, using a similar method to nneonneo.
A 200 point bounty was awarded to Strawdog's entry. This was awarded for being an optimization-based strategy that took the lead among non-built-in-compression answers and held it for a week. It scored 4749.88 points using an impressively clever method.
Scoring/validation script
The following Python script should be placed in the same folder as the image above (which should be named ORIGINAL.png
) and run using a command of the form python validate.py myImage.png
.
from PIL import Image
import sys
orig = Image.open("ORIGINAL.png")
img = Image.open(sys.argv[1])
if img.size != orig.size:
print("NOT VALID: image dimensions do not match the original")
exit()
w, h = img.size
orig = orig.convert("RGB")
img = img.convert("RGB")
orig_pix = orig.load()
img_pix = img.load()
score = 0
for x in range(w):
for y in range(h):
orig_r, orig_g, orig_b = orig_pix[x,y]
img_r, img_g, img_b = img_pix[x,y]
score += (img_r-orig_r)**2
score += (img_g-orig_g)**2
score += (img_b-orig_b)**2
print(score/255.**2)
Technical note: Objective measures of image similarity are a tricky thing. In this case I've opted for one that's easy for anyone to implement, in full knowledge that much better measures exist.
Leaderboard
var QUESTION_ID=69930,OVERRIDE_USER=21034;function answersUrl(e){return"https://api.stackexchange.com/2.2/questions/"+QUESTION_ID+"/answers?page="+e+"&pagesize=100&order=desc&sort=creation&site=codegolf&filter="+ANSWER_FILTER}function commentUrl(e,s){return"https://api.stackexchange.com/2.2/answers/"+s.join(";")+"/comments?page="+e+"&pagesize=100&order=desc&sort=creation&site=codegolf&filter="+COMMENT_FILTER}function getAnswers(){jQuery.ajax({url:answersUrl(answer_page++),method:"get",dataType:"jsonp",crossDomain:!0,success:function(e){answers.push.apply(answers,e.items),answers_hash=[],answer_ids=[],e.items.forEach(function(e){e.comments=[];var s=+e.share_link.match(/\d+/);answer_ids.push(s),answers_hash[s]=e}),e.has_more||(more_answers=!1),comment_page=1,getComments()}})}function getComments(){jQuery.ajax({url:commentUrl(comment_page++,answer_ids),method:"get",dataType:"jsonp",crossDomain:!0,success:function(e){e.items.forEach(function(e){e.owner.user_id===OVERRIDE_USER&&answers_hash[e.post_id].comments.push(e)}),e.has_more?getComments():more_answers?getAnswers():process()}})}function getAuthorName(e){return e.owner.display_name}function process(){var e=[];answers.forEach(function(s){var r=s.body;s.comments.forEach(function(e){OVERRIDE_REG.test(e.body)&&(r="<h1>"+e.body.replace(OVERRIDE_REG,"")+"</h1>")});var a=r.match(SCORE_REG);a&&e.push({user:getAuthorName(s),size:+a[2],language:a[1],link:s.share_link})}),e.sort(function(e,s){var r=e.size,a=s.size;return r-a});var s={},r=1,a=null,n=1;e.forEach(function(e){e.size!=a&&(n=r),a=e.size,++r;var t=jQuery("#answer-template").html();t=t.replace("{{PLACE}}",n+".").replace("{{NAME}}",e.user).replace("{{LANGUAGE}}",e.language).replace("{{SIZE}}",e.size).replace("{{LINK}}",e.link),t=jQuery(t),jQuery("#answers").append(t);var o=e.language;/<a/.test(o)&&(o=jQuery(o).text()),s[o]=s[o]||{lang:e.language,user:e.user,size:e.size,link:e.link}});var t=[];for(var o in s)s.hasOwnProperty(o)&&t.push(s[o]);t.sort(function(e,s){return e.lang>s.lang?1:e.lang<s.lang?-1:0});for(var c=0;c<t.length;++c){var i=jQuery("#language-template").html(),o=t[c];i=i.replace("{{LANGUAGE}}",o.lang).replace("{{NAME}}",o.user).replace("{{SIZE}}",o.size).replace("{{LINK}}",o.link),i=jQuery(i),jQuery("#languages").append(i)}}var ANSWER_FILTER="!t)IWYnsLAZle2tQ3KqrVveCRJfxcRLe",COMMENT_FILTER="!)Q2B_A2kjfAiU78X(md6BoYk",answers=[],answers_hash,answer_ids,answer_page=1,more_answers=!0,comment_page;getAnswers();var SCORE_REG=/<h\d>\s*([^\n,]*[^\s,]),.*?(\d+(?:\.\d+))(?=[^\n\d<>]*(?:<(?:s>[^\n<>]*<\/s>|[^\n<>]+>)[^\n\d<>]*)*<\/h\d>)/,OVERRIDE_REG=/^Override\s*header:\s*/i;
body{text-align:left!important}#answer-list,#language-list{padding:10px;width:400px;float:left}table thead{font-weight:700}table td{padding:5px}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> <link rel="stylesheet" type="text/css" href="//cdn.sstatic.net/codegolf/all.css?v=83c949450c8b"> <div id="answer-list"> <h2>Leaderboard</h2> <table class="answer-list"> <thead> <tr><td></td><td>Author</td><td>Language</td><td>Score</td></tr></thead> <tbody id="answers"> </tbody> </table> </div><div id="language-list"> <h2>Winners by Language</h2> <table class="language-list"> <thead> <tr><td>Language</td><td>User</td><td>Score</td></tr></thead> <tbody id="languages"> </tbody> </table> </div><table style="display: none"> <tbody id="answer-template"> <tr><td>{{PLACE}}</td><td>{{NAME}}</td><td>{{LANGUAGE}}</td><td>{{SIZE}}</td><td><a href="{{LINK}}">Link</a></td></tr></tbody> </table> <table style="display: none"> <tbody id="language-template"> <tr><td>{{LANGUAGE}}</td><td>{{NAME}}</td><td>{{SIZE}}</td><td><a href="{{LINK}}">Link</a></td></tr></tbody> </table>
pip unistall PIL
, thenpip install pillow
) and change the first line tofrom PIL import Image
. \$\endgroup\$