Color promedio de una imagen

21

Color promedio de una imagen

Los científicos han podido determinar el color promedio del universo, pero ¿en cuántos bytes podemos encontrar el color promedio de una imagen?

Tu tarea

Su entrada será una sola imagen que necesitará para encontrar el promedio de los colores en la imagen y generar una cadena de color hexadecimal ( #??????). La imagen puede tener cualquiera de los siguientes formatos

  • JPEG / JFIF
    • JPEG 2000
  • PELEA
  • GIF
  • BMP
  • PNG
  • PNM
    • PPM

La entrada también se puede tomar como una URL / URI a la imagen.

Las funciones integradas que calculan promedios o muestrean la imagen de una vez, como por ejemplo, ImageMeasurementsno están permitidas.

Ejemplos

Los resultados diferirán ligeramente dependiendo de cómo calcule el promedio y qué modelos de color use. He agregado valores RGB y LCH (HSV) para las imágenes a continuación.

Muestra 1salida: #53715FRGB, también puede ser #3B7D3DLCH (HSV)


Muestra 2salida: #8B7D41RGB, #96753CLCH (HSV)

Downgoat
fuente
¿Qué formatos de imagen tenemos que manejar? Específicamente, ¿podemos elegir manejar solo PPM?
Dennis
¿Puedo tener un caso de prueba más pequeño por favor? Mi script es muy lento y, aunque lo ejecutaré en el caso grande, no pierdo ese tiempo si está mal. O incluso solo el script con el que lo calculó.
Maltysen
@ Maltysen He agregado un ejemplo 240x140. Esperemos que sea lo suficientemente pequeño
Downgoat
¿Deberíamos redondear siempre hacia abajo? En el primer ejemplo, el 95.6...que ha redondeado 95en la salida especificada.
Dennis
44
PD: No tiene sentido publicar una pregunta en el sandbox a menos que la deje allí durante al menos 24 horas, para que las personas en otras zonas horarias puedan verla, y de manera realista, debe darle 72 horas porque no todos revisan caja de arena obsesivamente.
Peter Taylor

Respuestas:

19

Pyth - 23 22 19 18 16 bytes

Transpone para obtener todos los canales, luego suma, divide y hexifica cada uno. Termina concatenando y anteponiendo a #.

+\#sm.H/sdldCs'z

Toma un nombre de archivo de imagen (cualquier tipo) de stdin y lo envía a stdout. Muy lento .

+               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

Ejecución de la muestra

>>>pyth avg.pyth 
V5VAR.jpg
#8b7d41
Maltysen
fuente
Es posible que desee especificar el tipo de imagen, si corresponde.
isaacg
1
@isaacg buen punto. Se necesita cualquier cosa.
Maltysen
¿Cómo se necesita algo, decodifica el JPEG en un mapa de bits?
Alec Teal
3
@AlecTeal Según la documentación, Pyth comprueba si el archivo es una imagen y lo convierte automáticamente en un mapa de bits. Al buscar en el repositorio de GitHub, parece que usa la PILbiblioteca para manejar imágenes. Busque aquí la fuente exacta.
Bakuriu
@Bakuriu: no voté por esta respuesta porque no sabía cómo Pyth manejaba las imágenes, por lo que me pareció un poco sospechoso ... pero ahora que me proporcionó una idea, esto tiene mi voto. ¡Gracias por la aclaración!
rayryeng - Restablece a Monica el
22

Bash, 46 bytes

ImageMagick escala la imagen a un píxel que contiene el promedio de los colores de la imagen y luego la muestra como texto.

convert $1 -scale 1x1\! txt:-|egrep -o '#\w+'
SteelRaven
fuente
44
Eso es inteligente! +1
Maltysen
10

MATLAB - 68 bytes

La imagen se lee en imreadcombinación con uigetfilepara abrir una GUI para elegir la imagen que desea cargar. La suposición con este código es que todas las imágenes son RGB, y para calcular el color promedio, sumamos cada canal individualmente y luego dividimos por tantos elementos como haya en un canal, que es el número total de píxeles en la imagen RGB ( ) dividido por 3. Debido a que el promedio posiblemente puede generar valores de coma flotante, se requiere una llamada a para redondear el número hacia cero . combinado con la cadena de formato hexadecimal ( ) se usa para imprimir cada valor entero en el promedio en su equivalente hexadecimal. Sin embargo, existe para garantizar que un 0 adicional se rellene a la izquierda si el valor promedio de cualquier canal es menor que 16numel(I)fixsprintf%x02* .

I=imread(uigetfile);
['#' sprintf('%02x',fix(sum(sum(I))*3/numel(I)))]

Ejecuciones de muestra

Lo bueno de esto imreades que puedes leer imágenes directamente desde las URL. Como ejemplo reproducible, supongamos que ha descargado las imágenes en su computadora y ha ejecutado el código anterior ... pero para una demostración, leeré las imágenes directamente de Code Golf:

Primera imagen

>> I=imread('http://i.stack.imgur.com/dkShg.jpg');
>> ['#' sprintf('%02x',fix(sum(sum(I))*3/numel(I)))]

ans =

#53715f

Segunda imagen

>> I=imread('http://i.stack.imgur.com/V5VAR.jpg');
>> ['#' sprintf('%02x',fix(sum(sum(I))*3/numel(I)))]

ans =

#8b7d41

* Nota: Este fue un esfuerzo de colaboración realizado por los usuarios de StackOverflow en la sala de chat de MATLAB y Octave .

rayryeng - Restablece a Monica
fuente
7

CJam, 27 bytes

'#[q~]5>3/_:.+\,f/"%02X"fe%

Esto leyó una imagen PPM de STDIN.

CJam no tiene procesamiento de imagen incorporado, por lo que este código espera un PixMap portátil ASCII (número mágico P3) con una paleta completa de 24 bits (valor máximo 255) y sin comentarios.

Prueba de funcionamiento

$ cjam avg.cjam < dkShg.ppm 
#53715F

Cómo funciona

'#     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.
Dennis
fuente
7

HTML5 + JavaScript (ES6), 335 bytes

Esto no va a ganar, pero me divertí haciéndolo de todos modos.

Utiliza la API HTML5 Canvas . La entrada es una URL de una imagen habilitada para CORS .

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

Manifestación

Como es ES6, actualmente solo funciona en Firefox y 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.stack.imgur.com/dkShg.jpg');

f('http://crossorigin.me/https://i.stack.imgur.com/V5VAR.jpg');

rink.attendant.6
fuente
3
Oye, me gusta que se pueda ejecutar directamente en tu respuesta porque es HTML + JS :) +1.
rayryeng - Restablecer Monica
¿No puedes reemplazar new Imagecon Image()?
Ismael Miguel
@IsmaelMiguelTypeError: Constructor Image requires 'new'
rink.attendant.6
Mierda. Pero puedes crear W='width'y H='height'usar i[H]oi[W]
Ismael Miguel
1
@IsmaelMiguel que usa más personajes
rink.attendant.6
6

Python [3] + SciPy, 144 133 121

Carga datos de píxeles, sumas para cada canal, divide por tamaño *, formatos. Los valores se redondean hacia cero.

* tamaño = ancho * alto * canales, multiplicado por 3

from scipy import misc,sum
i=misc.imread(input())
print('#'+(3*'{:2x}').format(*sum(i.astype(int),axis=(0,1))*3//i.size))
Trang Oul
fuente
1
¿Por qué no usar input()para tomar el camino? Te ahorrará 20 bytes.
Kade
¡Gracias! Sin embargo, he logrado guardar 11 bytes.
Trang Oul
Sólo se necesita una importación, import scipy. Cambiar m.imreada misc.imread.
Kade
No funciona sin importar misceláneos, NameError: name 'misc' is not defined. Probado from scipy import*, tampoco funciona.
Trang Oul
2
@TrangOul ¿Qué pasa from scipy import sum, misc as m? Ahorras cuando usas la suma también, entonces.
matsjoyce
3

R, 90 bytes

rgb(matrix(apply(png::readPNG(scan(,"")),3,function(x)sum(x*255)%/%length(x)),nc=3),m=255)

La ruta al archivo PNG se lee desde STDIN. El paquete pngnecesita ser instalado.

Paso a paso:

#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"
plannapus
fuente
2

C, 259 bytes

Toma un archivo PPM sin comentarios .

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

Proceso

Código inicial:

#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;
}

Recorte las variables y elimine el bucle:

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;
}

A partir de ahí, combiné varias declaraciones en una sola declaración de devolución. Se eliminó junto con cualquier otra información de tipo extraño, se cambió el nombre de las variables y se cortó el espacio en blanco.

LambdaBeta
fuente
2

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('')
Οurous
fuente
2

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());}}

Gracias a esta respuesta sobre el desbordamiento de pila para el String.formattruco.

SuperJedi224
fuente