Average color of an image
Scientists have been able to determine the average color of the universe but in how many bytes can we find the average color on an image?
Your task
Your input will be a single image which you will need to find the average of the colors in the image and output a hex color-string (#??????
). The image can be any of the following formats
- JPEG/JFIF
- JPEG 2000
- TIFF
- GIF
- BMP
- PNG
- PNM
- PPM
The input can also be taken as a URL/URI to the image.
Build-in functions which calculate averages or sample the image at once such as ImageMeasurements
are not allowed.
Examples
Results will differ slightly depending on how you calculate the average and which color models you use. I've added RGB and LCH (HSV) values for the images below.
Sample 1 output: #53715F
RGB, may also be #3B7D3D
LCH (HSV)
Sample 2 output: #8B7D41
RGB, #96753C
LCH (HSV)
10 Answers 10
Bash, 46 bytes
ImageMagick scales image to one pixel which contains average of the colors in the image then outputs it as text.
convert 1ドル -scale 1x1\! txt:-|egrep -o '#\w+'
-
4\$\begingroup\$ That's smart! +1 \$\endgroup\$Maltysen– Maltysen2015年07月22日 19:34:27 +00:00Commented Jul 22, 2015 at 19:34
Pyth - (削除) 23 (削除ここまで) (削除) 22 (削除ここまで) (削除) 19 (削除ここまで) (削除) 18 (削除ここまで) 16 bytes
Transposes to get all channels, then sums, divides, and hexifies each. Finishes by concatenating and prepending a #
.
+\#sm.H/sdldCs'z
Takes an image file name (any type) from stdin and outputs to stdout. VERY SLOW.
+ String concat
\# "#"
s String concat all channels
m Map
.H Hex string
/ ld Divided by length of channel
sd Sum of channel
C Transpose to separate into channels
s Concatenate all rows
'z Read image with filename input
Sample run
>>>pyth avg.pyth
V5VAR.jpg
#8b7d41
-
\$\begingroup\$ You might want to specify the image type, if any. \$\endgroup\$izzyg– izzyg2015年07月22日 06:39:42 +00:00Commented Jul 22, 2015 at 6:39
-
1\$\begingroup\$ @isaacg good point. It takes anything. \$\endgroup\$Maltysen– Maltysen2015年07月22日 06:44:34 +00:00Commented Jul 22, 2015 at 6:44
-
\$\begingroup\$ How does it take anything, does it decode the jpeg to a bitmap? \$\endgroup\$Alec Teal– Alec Teal2015年07月22日 12:06:32 +00:00Commented Jul 22, 2015 at 12:06
-
3\$\begingroup\$ @AlecTeal According to the documentation Pyth checks whether the file is an image and automatically converts it into a bitmap. Searching the GitHub repository it looks like it uses the
PIL
library to handle images. Look here for the exact source. \$\endgroup\$Bakuriu– Bakuriu2015年07月22日 18:58:15 +00:00Commented Jul 22, 2015 at 18:58 -
\$\begingroup\$ @Bakuriu - I didn't upvote this answer because I wasn't aware how Pyth handled images so it looked a bit fishy to me... but now that you provided insight there, this gets my vote. Thanks for the clarification! \$\endgroup\$rayryeng– rayryeng2015年07月22日 19:27:06 +00:00Commented Jul 22, 2015 at 19:27
MATLAB - 68 bytes
The image is read in with imread
combined with uigetfile
to open up a GUI to choose the image you want to load in. The assumption with this code is that all images are RGB, and to calculate the average colour, we sum over each channel individually then divide by as many elements as there are in one channel, which is the total number of pixels in the RGB image (numel
(I)
) divided by 3. Because the average can possibly generate floating point values, a call to fix
is required to round the number down towards zero. sprintf
combined with the hexadecimal formatting string (%x
) is used to print out each integer value in the average into its hex equivalent. However, the 02
is there to ensure that an extra 0 is padded to the left should the average value for any channel be less than 16*.
I=imread(uigetfile);
['#' sprintf('%02x',fix(sum(sum(I))*3/numel(I)))]
Sample Runs
The nice thing about imread
is that you can read in images directly from URLs. As a reproducible example, let's assume you have downloaded the images on your computer and have run the above code... but for demonstration, I'll read the images directly from Code Golf:
First Image
>> I=imread('https://i.sstatic.net/dkShg.jpg');
>> ['#' sprintf('%02x',fix(sum(sum(I))*3/numel(I)))]
ans =
#53715f
Second Image
>> I=imread('https://i.sstatic.net/V5VAR.jpg');
>> ['#' sprintf('%02x',fix(sum(sum(I))*3/numel(I)))]
ans =
#8b7d41
*Note: This was a collaborative effort made by StackOverflow users on the MATLAB and Octave chat room.
CJam, 27 bytes
'#[q~]5>3/_:.+,円f/"%02X"fe%
This read a PPM image from STDIN.
CJam has no built-in image processing, so this code expects an ASCII Portable PixMap (magic number P3) with full 24-bit palette (maximum value 255) and no comments.
Test run
$ cjam avg.cjam < dkShg.ppm
#53715F
How it works
'# e# Push that character.
[q~] e# Evaluate the input and collect the results in an array.
5> e# Discard the first first results (Pi, 3, width, height, range).
3/ e# Split into chunks of length 3 (RGB).
_:.+ e# Push a copy and add across the columns (RGB).
,円f/ e# Divide each sum by the length of the array (number of pixels).
"%02X" e# Push that format string (hexadecimal integer, zero-pad to two digits).
fe% e# Format each integer, using the format string.
HTML5 + JavaScript (ES6), 335 bytes
This is not going to win but I had fun doing it anyways.
Uses the HTML5 Canvas API. Input is a URL of a CORS-enabled image.
f=(u,i=new Image)=>{i.crossOrigin='';i.src=u;i.onload=e=>{x=(c=document.createElement('canvas')).getContext('2d');a=w=c.width=i.width;a*=h=c.height=i.height;x.drawImage(i,0,0);for(d=x.getImageData(m=0,0,w,h).data,r=[0,0,0];m<d.length;m+=4){r[0]+=d[m];r[1]+=d[m+1];r[2]+=d[m+2]}console.log('#'+r.map(v=>(~~(v/a)).toString(16)).join``)}}
Demo
As it is ES6, it currently only works in Firefox and Edge.
f = (u,i = new Image) => {
i.crossOrigin = '';
i.src = u;
i.onload = e => {
x = (c = document.createElement('canvas')).getContext('2d');
a = w = c.width = i.width;
a *= h = c.height = i.height;
x.drawImage(i, 0, 0);
for (d = x.getImageData(m = 0, 0, w, h).data, r = [0, 0, 0]; m < d.length; m += 4) {
r[0] += d[m]
r[1] += d[m + 1];
r[2] += d[m + 2];
}
console.log('#' + r.map(v => (~~(v/a)).toString(16)).join``)
}
}
// Snippet stuff
console.log = x => document.body.innerHTML += x + '<br>';
f('http://crossorigin.me/https://i.sstatic.net/dkShg.jpg');
f('http://crossorigin.me/https://i.sstatic.net/V5VAR.jpg');
-
3\$\begingroup\$ Hey, I like that it's runnable directly in your answer because it's HTML + JS :) +1. \$\endgroup\$rayryeng– rayryeng2015年07月22日 18:49:29 +00:00Commented Jul 22, 2015 at 18:49
-
\$\begingroup\$ Can't you replace
new Image
withImage()
? \$\endgroup\$Ismael Miguel– Ismael Miguel2015年07月22日 21:48:57 +00:00Commented Jul 22, 2015 at 21:48 -
\$\begingroup\$ @IsmaelMiguel
TypeError: Constructor Image requires 'new'
\$\endgroup\$rink.attendant.6– rink.attendant.62015年07月22日 21:50:05 +00:00Commented Jul 22, 2015 at 21:50 -
\$\begingroup\$ Crap. But you can create
W='width'
andH='height'
and usei[H]
ori[W]
\$\endgroup\$Ismael Miguel– Ismael Miguel2015年07月22日 21:52:04 +00:00Commented Jul 22, 2015 at 21:52 -
1\$\begingroup\$ @IsmaelMiguel That uses more characters \$\endgroup\$rink.attendant.6– rink.attendant.62015年07月22日 21:55:41 +00:00Commented Jul 22, 2015 at 21:55
Python [3] + SciPy, (削除) 144 (削除ここまで) (削除) 133 (削除ここまで) 121
Loads pixel data, sums for each channel, divides by size*, formats. Values are rounded towards zero.
*size = width * height * channels, thus multiplied by 3
from scipy import misc,sum
i=misc.imread(input())
print('#'+(3*'{:2x}').format(*sum(i.astype(int),axis=(0,1))*3//i.size))
-
1\$\begingroup\$ Why not use
input()
for taking the path? It'll save you 20 bytes. \$\endgroup\$Kade– Kade2015年07月22日 13:15:49 +00:00Commented Jul 22, 2015 at 13:15 -
\$\begingroup\$ Thanks! I've managed to save 11 bytes though. \$\endgroup\$Trang Oul– Trang Oul2015年07月22日 13:36:52 +00:00Commented Jul 22, 2015 at 13:36
-
\$\begingroup\$ You only need one import,
import scipy
. Changem.imread
tomisc.imread
. \$\endgroup\$Kade– Kade2015年07月22日 13:41:20 +00:00Commented Jul 22, 2015 at 13:41 -
\$\begingroup\$ Doesn't work without importing misc,
NameError: name 'misc' is not defined
. Triedfrom scipy import*
, doesn't work either. \$\endgroup\$Trang Oul– Trang Oul2015年07月22日 13:51:57 +00:00Commented Jul 22, 2015 at 13:51 -
2\$\begingroup\$ @TrangOul What about
from scipy import sum, misc as m
? You save when you use sum as well, then. \$\endgroup\$matsjoyce– matsjoyce2015年07月22日 15:04:49 +00:00Commented Jul 22, 2015 at 15:04
R, 90 bytes
rgb(matrix(apply(png::readPNG(scan(,"")),3,function(x)sum(x*255)%/%length(x)),nc=3),m=255)
Path to PNG file is read from STDIN. Package png
needs to be installed.
Step-by-step:
#Reads path from stdin and feeds it to function readPNG from package png
p = png::readPNG(scan(,""))
#The result is a 3d matrix (1 layer for each color channel) filled with [0,1] values
#So next step, we compute the mean on each layer using apply(X,3,FUN)
#after moving the value to range [0,255] and keeping it as an integer.
a = apply(p,3,function(x)sum(x*255)%/%length(x))
#The result is then moved to a matrix of 3 columns:
m = matrix(a, nc=3)
#Which we feed to function rgb, while specifying that we're working on range [0,255]
rgb(m, m=255)
# Example:
rgb(matrix(apply(png::readPNG(scan(,"")),3,function(x)sum(x*255)%/%length(x)),nc=3),m=255)
# 1: ~/Desktop/dkShg.png
# 2:
# Read 1 item
# [1] "#53715F"
C, 259 Bytes
Takes a PPM file with no comments.
double*f,r,g,b;x,y,z,i;char*s="%d %d %d";main(a,_){(a-2)?(feof(f)?0:(fscanf(f,s,&x,&y,&z),r+=(x-r)/i,g+=(y-g)/i,b+=(z-b)/i++,main(0,0))):(f=fopen(((char**)_)[1],"r"),fscanf(f,"%*s%*d%*d%*d"),r=g=b=0.,i=1,main(0,0),printf(s,(int)r,(int)g,(int)b),fclose(f));}
Process
Initial code:
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
FILE *f = fopen(argv[1],"r");
int w,h,d,x,y,z,i;
double r,g,b;
fscanf(f,"%*s %d %d %d",&w,&h,&d);//get width, height, depth, ignore P6
r = g = b = 0.0; //zero r, g, and b totals
for (i=1; i<=w*h; ++i) {
fscanf(f,"%d %d %d",&x,&y,&z);//get next pixel
r+=(x-r)/i;//update averages
g+=(y-g)/i;
b+=(z-b)/i;
}
printf("%d %d %d",(int)r,(int)g,(int)b);//print result
fclose(f);
return 0;
}
Trim variables and remove loop:
double r,g,b;
FILE *f;
int i;
int main(int argc, char *argv[])
{
if (argc==2) { // {./me} {file.ppm}
f = fopen(argv[1],"r");
fscanf(f,"%*s%*d%*d%*d");//drop info
r = g = b = 0.0;
i = 1;
main(0,0);//begin load loop
printf("%d %d %d",(int)r,(int)g,(int)b);
fclose(f)
} else {
if (feof(f)) return 0;
fscanf(f,"%d%d%d",&x,&y,&z);
r+=(x-r)/i;
g+=(y-g)/i;
b+=(z-b)/i;
i++;
main(0,0);
}
return 0;
}
From there I combined the various statements into a single return statement. Removed it and any other extraneous type info, renamed variables, and cut the whitespace.
Cobra - 371
@ref 'System.Numerics'
use System.Drawing
use System.Numerics
class P
def main
i,d=Bitmap(Console.readLine?''),BigInteger
r,g,b,c=d(),d(),d(),d()
for x in i.width,for y in i.height,r,g,b,c=for n in 4 get BigInteger.add([r,g,b,c][n],d([(p=i.getPixel(x,y)).r,p.g,p.b,1][n]))
print'#'+(for k in[r,g,b]get Convert.toString(BigInteger.divide(k,c)to int,16)).join('')
Java, (削除) 449 (削除ここまで) (削除) 447 (削除ここまで) (削除) 446 (削除ここまで) (削除) 430 (削除ここまで) 426 bytes
import java.awt.*;interface A{static void main(String[]a)throws Exception{java.awt.image.BufferedImage i=javax.imageio.ImageIO.read(new java.io.File(new java.util.Scanner(System.in).nextLine()));int r=0,g=0,b=0,k=0,x,y;for(x=0;x<i.getWidth();x++)for(y=0;y<i.getHeight();k++){Color c=new Color(i.getRGB(x,y++));r+=c.getRed();g+=c.getGreen();b+=c.getBlue();}System.out.printf("#%06X",0xFFFFFF&new Color(r/k,g/k,b/k).getRGB());}}
Thanks to this answer over on stack overflow for the String.format
trick.
95.6...
, which you have rounded to95
in the specified output. \$\endgroup\$