Cor média de uma imagem

21

Cor média de uma imagem

Os cientistas conseguiram determinar a cor média do universo, mas em quantos bytes podemos encontrar a cor média em uma imagem?

Sua tarefa

Sua entrada será uma imagem única, na qual você precisará encontrar a média das cores na imagem e gerar uma sequência de cores hexadecimal ( #??????). A imagem pode ter qualquer um dos seguintes formatos

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

A entrada também pode ser tomada como um URL / URI para a imagem.

Funções embutidas que calculam médias ou mostram a imagem de uma só vez, como ImageMeasurementsnão são permitidas.

Exemplos

Os resultados variam um pouco, dependendo de como você calcula a média e de quais modelos de cores você usa. Adicionei valores RGB e LCH (HSV) para as imagens abaixo.

Amostra 1saída: #53715FRGB, também pode ser #3B7D3DLCH (HSV)


Amostra 2saída: #8B7D41RGB, #96753CLCH (HSV)

Downgoat
fonte
Quais formatos de imagem temos que lidar? Especificamente, podemos optar por lidar apenas com PPM?
Dennis
Posso ter um caso de teste menor, por favor? Meu script é muito lento e, embora eu o execute no caso grande, não perco esse tempo se estiver errado. Ou mesmo apenas o script com o qual você calculou.
Maltysen
@ Maltysen Adicionei um exemplo de 240 x 140. Hopfully que é pequeno o suficiente
Downgoat
Devemos sempre arredondar para baixo? No primeiro exemplo, o 95.6..., para o qual você arredondou 95na saída especificada.
Dennis
4
PS Não faz sentido postar uma pergunta na caixa de areia, a menos que você a deixe lá por pelo menos 24 horas, para que pessoas em outros fusos horários possam vê-la e, realisticamente, você precisa dar 72 horas, porque nem todo mundo verifica o caixa de areia obsessivamente.
Peter Taylor

Respostas:

19

Pitão - 23 22 19 18 16 bytes

Transpõe para obter todos os canais, soma, divide e hexifica cada um. Conclui concatenando e anexando a #.

+\#sm.H/sdldCs'z

Leva um nome de arquivo de imagem (qualquer tipo) de stdin e gera para stdout. MUITO 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

Amostra de execução

>>>pyth avg.pyth 
V5VAR.jpg
#8b7d41
Maltysen
fonte
Você pode especificar o tipo de imagem, se houver.
Isaacg
11
@isaacg good point. É preciso qualquer coisa.
Maltysen
Como é necessário algo, ele decodifica o jpeg para um bitmap?
Alec Teal
3
@AlecTeal De acordo com a documentação, o Pyth verifica se o arquivo é uma imagem e o converte automaticamente em um bitmap. Pesquisando no repositório GitHub, parece que ele usa a PILbiblioteca para manipular imagens. Procure aqui a fonte exata.
Bakuriu 22/07/2015
@Bakuriu - Eu não votei positivamente nesta resposta porque não sabia como Pyth lidava com as imagens, por isso parecia um pouco suspeito para mim ... mas agora que você forneceu uma visão lá, isso é meu voto. Obrigado pelo esclarecimento!
rayryeng - Restabelece Monica
22

Bash, 46 bytes

O ImageMagick dimensiona a imagem para um pixel que contém a média das cores na imagem e a gera como texto.

convert $1 -scale 1x1\! txt:-|egrep -o '#\w+'
SteelRaven
fonte
4
Isso é esperto! +1
Maltysen 22/07/2015
10

MATLAB - 68 bytes

A imagem é lida com imreadcombinada com uigetfilepara abrir uma GUI para escolher a imagem que você deseja carregar. A suposição com este código é que todas as imagens são RGB e, para calcular a cor média, somamos cada canal individualmente e depois dividimos por quantos elementos houver em um canal, que é o número total de pixels na imagem RGB ( ) dividido por 3. Como a média pode gerar valores de ponto flutuante, é necessária uma chamada para arredondar o número para zero . combinado com a sequência de formatação hexadecimal ( ) é usado para imprimir cada valor inteiro na média em seu equivalente hexadecimal. No entanto, existe para garantir que um 0 extra seja preenchido à esquerda, caso o valor médio de qualquer canal seja menor que 16numel(I)fixsprintf%x02* .

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

Execuções de amostra

O legal imreadé que você pode ler imagens diretamente de URLs. Como um exemplo reproduzível, vamos supor que você baixou as imagens no seu computador e executou o código acima ... mas para demonstração, vou ler as imagens diretamente do Code Golf:

Primeira imagem

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

ans =

#53715f

Segunda imagem

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

ans =

#8b7d41

* Nota: este foi um esforço colaborativo feito pelos usuários do StackOverflow na sala de bate-papo MATLAB e Octave .

rayryeng - Restabelecer Monica
fonte
7

CJam, 27 bytes

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

Isso leu uma imagem PPM do STDIN.

Como o CJam não possui processamento de imagem embutido, esse código espera um PixMap portátil ASCII (número mágico P3) com uma paleta completa de 24 bits (valor máximo 255) e sem comentários.

Execução de teste

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

Como 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
fonte
7

HTML5 + JavaScript (ES6), 335 bytes

Isso não vai ganhar, mas eu me diverti fazendo isso de qualquer maneira.

Usa a API do Canvas em HTML5 . Entrada é um URL de uma imagem ativada 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``)}}

Demo

Como é o ES6, atualmente só funciona no Firefox e 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
fonte
3
Ei, eu gosto que seja executável diretamente na sua resposta porque é HTML + JS :) +1.
rayryeng - Restabelece Monica
Você não pode substituir new Imagecom Image()?
Ismael Miguel
@IsmaelMiguelTypeError: Constructor Image requires 'new'
rink.attendant.
Porcaria. Mas você pode criar W='width'e H='height'usar i[H]oui[W]
Ismael Miguel
11
@IsmaelMiguel que usa mais caracteres
rink.attendant.6
6

Python [3] + SciPy, 144 133 121

Carrega dados de pixel, somas para cada canal, divide por tamanho *, formatos. Os valores são arredondados para zero.

* tamanho = largura * altura * canais, 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
fonte
11
Por que não usar input()para seguir o caminho? Economizará 20 bytes.
Kade
Obrigado! Eu consegui salvar 11 bytes embora.
Trang Oul
Você só precisa de uma importação import scipy. Mude m.imreadpara misc.imread.
Kade
Não funciona sem importar misc NameError: name 'misc' is not defined,. Tentei from scipy import*, também não funciona.
Trang Oul
2
@TrangOul What about from scipy import sum, misc as m? Você salva quando usa soma também.
matsjoyce
3

R, 90 bytes

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

O caminho para o arquivo PNG é lido no STDIN. O pacote pngprecisa ser instalado.

Passo a passo:

#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
fonte
2

C, 259 bytes

Toma um arquivo PPM sem comentários .

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

Processo

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

Apare variáveis ​​e remova o 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;
}

A partir daí, combinei as várias declarações em uma única declaração de retorno. Removido e qualquer outra informação de tipo estranho, renomeado variáveis ​​e cortando o espaço em branco.

LambdaBeta
fonte
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('')
Furioso
fonte
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());}}

Graças a esta resposta, no estouro da pilha, o String.formattruque.

SuperJedi224
fonte