# The Specs

• Your program must read the image from one file and write the results to another file, the name of at least the first of which should be taken from the input

• You may accept and return any image format or formats, as long as you specify in your answers which ones you are using

• An exception should be thrown if the specified source file does not exist

• The final image must be as close as possible to the initial image (by per-pixel euclidean metric on RGB values) while using only colors from a list read from the file "colors.txt" (it will not necessarily use all of them)

• When determing the nearest color, you should use the euclidean metric on RGB values, Δc=sqrt((Δr)2+(Δg)2+(Δb)2), when two or more colors on the list are equally close, any of those colors is valid
• The list of colors given will be formatted with one rgb color code on each line, with spaces seperating the red, green, and blue values

• You may assume that at least three but no more than 1,000 colors will be listed

This is code golf, so the shortest program wins

# Some test cases:

(if RGB values in the output images do not exactly match those in the list, this is due to a problem with Java's ImageIO.write)

input:

colors.txt:

255 0 0
250 150 0
255 50 0
0 0 200
0 0 80
0 0 0


output:

input:

colors.txt:

100 60 0
0 0 20
0 0 70
0 20 90
15 15 15
100 100 100
120 120 120
150 150 150


output:

input:

colors.txt:

0 0 0
10 20 15
25 25 20
35 30 20
40 35 25
55 55 60
70 60 50
130 100 80
130 140 145
140 100 70
165 130 90
200 160 130
230 210 220
250 250 250


output:

input:

colors.txt:

0 0 0
70 50 40
80 0 0
120 100 90
120 120 120
170 150 90
170 170 170
170 200 195
190 140 100
190 205 165
210 170 120


output:

Again, I put together a java program to generate the sample output. I'll golf it down a little further, and then I may post it as an answer later.

• Is there any reason to limit file formats to JPG and requiring I/O to be through files, other than punishing/eliminating languages without image and file system manipulation libraries? Why not allow any true-colour raster format (so that one could use PPM for instance), and I/O as byte streams through STDIN/STDOUT? – Martin Ender May 17 '15 at 0:04
• @MartinBüttner: Is that better? – SuperJedi224 May 17 '15 at 0:05
• Yeah much better, although you still require I/O to be through files. – Martin Ender May 17 '15 at 0:06
• Per-pixel, it's supposed to use the nearest color in the list by the euclidean metric on RGB values. – SuperJedi224 May 17 '15 at 0:09
• Note that this is called posterization or color quantization. – Calvin's Hobbies May 17 '15 at 1:05

# Mathematica 109 100 97 111

Update: now outputs the image as required.

The function, f, replaces a single pixel with the closest pixel found in the input file (in this case, "colors.txt").

t is the output file.

The function, g, applies f to each pixel in the input image.

g[i_,h_,t_]:=Export[t,(c=Import[h,"Data"]/255;
f@p_:=Sort[{p~EuclideanDistance~#,#}&/@c][[1,2]];f~ImageApply~i)]


Test Cases

Example 1: Seascape

(*creating "colors.txt"*)
c = "255 0 0
250 150 0
255 50 0
0 0 200
0 0 80
0 0 0";
Export["colors.txt", c]


Example 2: Earth from moon

I'll skip the part about storing the color info into "colors.txt". It closely follows example 1.

Example 3: Jupiter

Example 4: American Gothic

# Python [3] + Pillow, 276272 267

Takes 2 parameters: name of source image file and name of file with colors list. Tested on Py3, may work on Py2 too.

My first attempt in code golf, so probably not very impressive (and similar to dieter's code).

import sys
import PIL.Image as P
i,c=sys.argv[1:]
c,z=[tuple(int(i)for i in e.split())for e in open(c).readlines()],P.open(i)
x,y=z.size
for _ in range(x*y):
j,k=_//y,_%y
d={sum((i-j)**2for i,j in zip(p[j,k],a)):a for a in c};p[j,k]=d[min(d)]
z.save(i*2)


Ungolfed:

(displays output image instead of saving, and is a bit safer, because removes empty lines from colors file)

# coding: utf-8

import sys
from PIL import Image

def similarImage(img, colors):
if isinstance(img, str):
img = Image.open(img)
x, y = img.size
for i in range(x):
for j in range(y):
pixels[i,j] = getClosestColor(pixels[i,j], colors)
img.show()

def getColorDistanceSq(c1, c2):
#root not needed
return sum((i-j)**2 for i,j in zip(c1, c2))

def getClosestColor(col, colBase):
colDist = {getColorDistanceSq(col, c) : c for c in colBase}
return  colDist[min(colDist)]

def parseColors(desc):
desc = desc.split('\n')
desc = [i for i in desc if i != '']
return [tuple(int(i) for i in d.split()) for d in desc]

def main():
imgFile   = sys.argv[1]
with open(sys.argv[2]) as f:
similarImage(imgFile, colors)

if __name__ == "__main__": main()

• You are not allowed to overwrite the source image. – SuperJedi224 Jul 16 '15 at 17:02

# Python 2.7, 279

Reads the name of the input image and the name of the color map for commandline arguments. usage is

python script.py <image file> <colors.txt>


It saves the new image as a file with the same name, preceded by an extra tilde :

from sys import*;from PIL.Image import*;f=argv[1]
c,I,X=[[int(x)for x in l.split()]for l in file(argv[2])],open(f),range
for j in X(h):
for i in X(w):
r,g,b=P[i,j];C={(R,G,B):(R-r)**2+(G-g)**2+(B-b)**2for R,G,B in c};P[i,j]=min(C,key=C.get)
I.save('~'+f)

• I had to find and install PIL first, but it wound up working. – SuperJedi224 May 18 '15 at 10:05

# Java, 726 bytes

Too bad all the imports and main class eat up some many bytes, but it's a standalone program after all. Nevertheless, it was a nice and fun challenge to work on.

This golf can be run by issuing java M.class inputImage.png outputImage.png colours.txt, or equivalent.

import java.awt.*;import java.awt.image.*;import java.io.*;import java.nio.file.*;import javax.imageio.*;class M {public static void main(String[]a)throws Exception{BufferedImage i=ImageIO.read(new File(a[0]));int w,h,k,r,g,b,t,z,q;w=i.getWidth();h=i.getHeight();BufferedImage o=new BufferedImage(w,h,2);for(int x=0;x<w;x++){for(int y=0;y<h;y++){Color v=null;k=i.getRGB(x,y);r=k>>16&0xff;g=k>>8&0xff;b=k&0xff;double e=999;for(String n:Files.readAllLines(Paths.get(a[2]))){String[]m=n.split(" ");t=Integer.parseInt(m[0]);z=Integer.parseInt(m[1]);q=Integer.parseInt(m[2]);double d=Math.sqrt((t-r)*(t-r)+(z-g)*(z-g)+(q-b)*(q-b));if(d<e){e=d;v=new Color(t,z,q);}}o.setRGB(x,y,v.getRGB());}}ImageIO.write(o,"png",new File(a[1]));}}

• Actually, I believe the .class extension can be omitted for the java command prompt instruction. – SuperJedi224 Jun 12 '15 at 20:15
• if you want to golf it a little further, use a static block and compile it with java 6 – Fabinout Jul 22 '15 at 12:21

# Java, 861849842 836 bytes

I think there's probably room for improvement here.

import java.awt.*;import java.awt.image.*;import java.io.*;import java.util.*;import javax.imageio.*;import static java.lang.Math.*;class D{static void g(Color c,ArrayList<Color>h,int x,int y,BufferedImage b){double d=450;Color e=null;for(Color f:h){double g=sqrt(pow(c.getRed()-f.getRed(),2)+pow(c.getGreen()-f.getGreen(),2)+pow(c.getBlue()-f.getBlue(),2));if(g<d){e=f;d=g;}}b.setRGB(x, y, e.getRGB());}public static void main(String[]a)throws Exception{ArrayList<Color>c=new ArrayList<Color>();Scanner b=new Scanner(new File("colors.txt"));while(b.hasNext())c.add(new Color(b.nextInt(),b.nextInt(),b.nextInt()));b=new Scanner(System.in);BufferedImage i=ImageIO.read(new File(b.next()));int x=i.getWidth(),y=i.getHeight();for(;--x>=0;)for(int z=0;z<y;z++)g(new Color(i.getRGB(x,z)),c,x,z,i);ImageIO.write(i,"jpg",new File(b.next()));}}


Takes the names of the source and target image files from user input, reads the color list from colors.txt. Supports .jpg, .png, and probably others for input. Supports only .jpg for output.