Classificar os pixels

35

Sua tarefa é criar um programa que, com uma imagem de entrada, crie uma imagem de saída do mesmo tamanho, em que todos os pixels sejam ordenados pelo valor hexadecimal.

Seu programa pode:

  • Classifique os pixels da esquerda para a direita e depois para baixo ou primeiro para baixo em colunas e depois para a direita. De qualquer forma, o pixel superior esquerdo é o menor e o inferior direito é o maior.
  • Use transparência, mas isso não é necessário.
  • Classifique por RGB, mas você pode usar CMY ou qualquer outro formato com pelo menos 3 valores. Você pode escolher em quais valores classificar. (HSV pode dar algumas imagens legais)
  • Use qualquer formato de imagem conhecido que a maioria dos computadores possa abrir.

Regras:

  • A saída deve ser gravada no disco ou canalizável para um arquivo.
  • A entrada é fornecida como um argumento da linha de comando, na forma de um caminho relativo para a imagem ou canalizada a partir da linha de comando.
  • Este é o código golf, pelo que o código mais curto em bytes vence!
vrwim
fonte
2
Relacionado.
Martin Ender

Respostas:

20

Pitão - 10 bytes

Lê a imagem, recolhe o bitmap, classifica e depois divide o bitmap novamente e depois grava.

.wclK'zSsK

Não funciona online por razões óbvias. Leva a entrada como caminho relativo para o arquivo de imagem e gera para o.png.

Saída do gótico americano:

Maltysen
fonte
3
Espero que eu não sou o único a ter a impressão de que a imagem está se movendo ...
Quentin
Parece madeira.
Joe Z.
19

JavaScript (ES6), 383 377 354 bytes

f=s=>{d=document,i=new Image,i.src=s,i.onload=$=>{c=d.createElement`canvas`,x=c.getContext`2d`,c.width=w=i.width,c.height=h=i.height,x.drawImage(i,0,0),D=x.getImageData(0,0,w,h),t=D.data,t.set([].concat(...[...t].map((v,i,T)=>i%4?[,,,0]:T.slice(i,i+4)).sort((a,b)=>a.some((v,i)=>k=v-b[i])&&k)).slice(12*w*h)),x.putImageData(D,0,0),d.body.appendChild(c)}}

saída de amostra

Demonstração executável:

Como esse código funciona é usar getImageDatapara obter uma matriz do formulário

[R,G,B,A,
 R,G,B,A,
 R,G,B,A,
 ...]

E mappara uma matriz do formulário

[[R,G,B,A],[0,0,0,0],[0,0,0,0],[0,0,0,0],
 [R,G,B,A],[0,0,0,0],[0,0,0,0],[0,0,0,0],
 [R,G,B,A],[0,0,0,0],[0,0,0,0],[0,0,0,0],
 ...]

Para que os valores R sejam mapeados para matrizes do conjunto RGBA, e os valores B, G e A se transformem em matrizes zero de valor mínimo. Quando classificamos essa matriz, todas as [0,0,0,0]matrizes são classificadas na parte inferior e as matrizes de valor real são classificadas normalmente na parte superior:

[[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0],
 [0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0],
 [0,0,0,0],..., [R,G,B,A],[R,G,B,A],[R,G,B,A],...]
                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
               extract & flatten these sorted pixels

Deslizamos o quarto superior da matriz (para perder os valores vazios que criamos), achatamos-a [].concat.applye terminamos com uma matriz da primeira forma novamente, mas desta vez está classificada.

Pouco golfe com espaço em branco e comentários:

f=s=>{ 
  // first, load image, then do everything else onload
  i=new Image,
  i.src = s,
  i.onload=$=>{
    // set up the canvas
    d=document,
    c=d.createElement`canvas`,
    w=c.width=i.width,
    h=c.height=i.height,
    x=c.getContext`2d`,

    // draw image to canvas and capture pixel data
    x.drawImage(i,0,0),
    D=x.getImageData(0,0,w,h),
    t=D.data,

    // set pixel data to...
    t.set(
      // the flattened array...
      [].concat(...
        // that is a mapping of the pixel data...
        [...t].map(
          // that clumps RGBA families into subarrays
          // by replacing every fourth value with [R,G,B,A]
          // and all other values to [0,0,0,0]...
          (v,i,T)=>i%4?[,,,0]:T.slice(i,i+4)
        )
        // and is then sorted...
        .sort(
          // by reducing each array to a positive, negative, or zero
          // by comparing R,G,B,& A until the first nonzero difference
          (a,b)=>a.some((v,i)=>k=v-b[i])&&k
        )
      )
      // then eliminate the low-sorted empty [0,0,0,0] values we created,
      // leaving only the top fourth, with real values
      // (note that 3*4*w*h is the same as 3*t.length)
      .slice(3*4*w*h)
    ),

    // now that `t` is set, store `D` in canvas
    x.putImageData(D,0,0),

    // show canvas
    d.body.appendChild(c)
  }
}

Observe que a maioria dos navegadores pode falhar ao executar esse código para imagens grandes, porque ele passa um grande número de argumentos para [].concat. Quando o ambiente do navegador não permite memória suficiente para todos os argumentos, uma abordagem alternativa é mapear novamente os valores RGBA dos quatro primeiros arrays de volta para o array, para uma pontuação total de 361 bytes :

f=s=>{d=document,i=new Image,i.src=s,i.onload=$=>{c=d.createElement`canvas`,x=c.getContext`2d`,c.width=w=i.width,c.height=h=i.height,x.drawImage(i,0,0),D=x.getImageData(0,0,w,h),t=D.data,t.set([...t].map((v,i,T)=>i%4?[,,,0]:T.slice(i,i+4)).sort((a,b)=>a.some((v,i)=>k=v-b[i])&&k).map((v,i,A)=>A[3*w*h+(i>>2)][i%4])),x.putImageData(D,0,0),d.body.appendChild(c)}}

Simplesmente substituímos [].concat(...{stuff}).slice(12*w*h)por {stuff}.map((v,i,A)=>A[3*w*h+(i>>2)][i%4]).)

apsillers
fonte
Você poderia incluir um exemplo de saída?
Paŭlo Ebermann 3/11
@ PaŭloEbermann Feito.
Apsillers
@insertusernamehere Oh, caramba, só testei em imagens pequenas. Minha concat.applyligação está fornecendo muitos argumentos concate o mecanismo JS está rejeitando-o. D:Obrigado! Vou consertar isso e anotar as duas pontuações. (E eu estou feliz que eu poderia ajudar!)
apsillers
@insertusernamehere Obrigado pelo aviso sobre a limitação de tamanho; Agora também publiquei uma versão um pouco mais longa que funciona em imagens maiores.
Apsillers
@apsillers Bom trabalho. Infelizmente, não posso votar novamente. :)
insertusernamehere
14

Mathematica 86 83 72 bytes

 f=Flatten;Image[f[Sort[f[s=ImageData@#1,1]]]~ArrayReshape~Dimensions@s]&

Com 14 bytes salvos, graças a @Martin Buttner.


Exemplo

A imagem em si é inserida. Alternativamente, uma variável contendo a imagem pode ser usada.

gótico

DavidC
fonte
O desafio especifica que os pixels sejam classificados por valor hexadecimal, mas, se eu leio corretamente, você usa a classificação padrão do Mathematica para as listas {R, G, B} ou {R, G, B, alpha}. Não está claro para mim que estes são equivalentes.
Michael Stern
@MichaelStern Não conheço o Mathematica, mas se ele classifica tuplas elementares como a maioria dos idiomas, elas são equivalentes: números como hex são ordenados por cada dígito, dois dos quais são representados por cada elemento da tupla.
Maltysen 3/11
@ MichaelStern, Maltysen está correto. A classificação RGB e a classificação Hex são equivalentes: classificar pelo R, depois pelo G e pelo valor B funciona exatamente como a classificação do valor do local no Hex.
DavidC
OK, +1 de mim.
Michael Stern
ImageDatae ArrayReshapepoderia usar notação infix. Flattené longo o suficiente para salvar alguns bytes atribuindo-o a f. E você realmente precisa "Byte"? O padrão não escalaria apenas os valores do canal para [0,1]que a classificação e a reconstrução da imagem ainda funcionassem perfeitamente?
Martin Ender
5

Javascript ES6, 334 bytes

f=s=>{with((d=document).body.appendChild(c=d.createElement`canvas`).getContext`2d`)(i=new Image).src=s,drawImage(i,0,0,w=c.width=i.width,h=c.height=i.height),t=(g=getImageData(0,0,w,h)).data,t.set([...t].map(i=>(0+i.toString(16)).slice(-2)).join``.match(/.{8}/g).sort().join``.match(/../g).map(i=>parseInt(i,16))),putImageData(g,0,0)}

Ungolfed:

f=s=>{                                   // create function that accepts image name
 with((d=document).body.appendChild(     // use "with" to exclude having to prepend "<context2d>." to drawImage, getImageData and putImageData
   c=d.createElement`canvas`).getContext`2d`) // create canvas to get pixels from and draw output to
  (i=new Image).src=s,                   // create image, define source filename
  drawImage(i,0,0,w=c.width=i.width,     // draw image to canvas
                  h=c.height=i.height),
  t=(g=getImageData(0,0,w,h)).data,      // get image data from canvas in form of Uint8Array
  t.set([...t]                           // convert image data from Uint8Array to standard array
   .map(i=>(0+i.toString(16)).slice(-2)) // convert R,G,B,A bytes to base16 strings with leading zeros
   .join``.match(/.{8}/g)                // convert array of [R,G,B,A,R,G,B,A,...] to [RGBA,RGBA,...]
   .sort()                               // sort pixel values
   .join``.match(/../g)                  // convert array of [RGBA,RGBA,...] to [R,G,B,A,R,G,B,A,...]
   .map(i=>parseInt(i,16))),             // convert hex strings back to integers, reassign to image data
  putImageData(g,0,0)                    // dump image data onto canvas
}
Dendrobium
fonte
@insertusernamehere Funciona no Firefox mais recente. Como sua resposta, ele pressupõe que exista um corpo para anexar a tela e que a imagem de origem seja do mesmo domínio.
Dendrobium
+1 Solução muito elegante. Ele também processa imagens de até 1600 x 1900px.
usar o seguinte comando
2
Hoje eu aprendi que appendChildretorna seu argumento. Muito útil! Você me inspirou a diminuir minha entrada de 377 para 354, mas não consigo vencer a sua :). (Quando uso a técnica appendChilde o encadeamento with, posso reduzi -la para 347, mas ainda a 13 de distância!) Excelente trabalho!
Apsillers
5

C (usando SDL1.2), 333 322 315 bytes

C provavelmente não é a 'faca mais afiada da prateleira' para esse tipo de trabalho, eu queria tentar de qualquer maneira. Dicas para melhorar minha resposta são bem-vindas. O programa obtém o nome do arquivo de imagem de entrada como um argumento cli.

#include <SDL.h>
#include <SDL_image.h>
#define X SDL_Surface*
#define Y const void*
C(Y a,Y b){return*(Uint32*)a-*(Uint32*)b;}main(int o,char**a){X i=IMG_Load(a[1]);X s=SDL_SetVideoMode(i->w,i->h,32,0);i=SDL_ConvertSurface(i,s->format,0);qsort(i->pixels,i->w*i->h,4,C);SDL_BlitSurface(i,0,s,0);for(;;SDL_Flip(s));}

compile e execute: gcc -I/usr/include/SDL snippet.c -lSDL -lSDL_image && ./a.out

insira a descrição da imagem aqui

Normalmente não jogo golfe em C, mas acabei de responder a esse desafio ontem e só queria continuar brincando com esse novo brinquedo :)

obrigado a @ pseudonym117 por me ajudar a economizar 5 bytes

dieter
fonte
Pode economizar 1 byte, alterando o seu whileno final para for(;;SDL_Flip(s));, e acredito que você pode omitir into método Ce economizar mais 4.
pseudonym117
4

JavaScript (ES6), 452 480 484 487 511 bytes

Uau, isso ficou mais do que o esperado:

f=u=>{i=new Image;i.src=u;i.onload=_=>{c=(d=document).createElement`canvas`;c.width=w=i.width;c.height=h=i.height;x=c.getContext`2d`;x.drawImage(i,0,0,w,h);p=x.getImageData(0,0,w,h).data;t=[];f=[];for(j=0;j<p.length;++j)t.push([p[j],p[++j],p[++j],p[++j]]);t.sort((a,b)=>a[0]>b[0]||a[0]==b[0]&&a[1]>b[1]||a[0]==b[0]&&a[1]==b[1]&&a[2]>b[2]).map(u=>f.push.apply(f,u));x.putImageData(new ImageData(new Uint8ClampedArray(f),w,h),0,0);d.body.appendChild(c)}}

A função pega uma URL como entrada f('test.jpg');e desenha o resultado em umcanvas elemento que é anexado ao body.

Observe que a fonte deve estar no mesmo domínio ou o script será interrompido com um problema de segurança.


Limitações

Eu testei no Firefox 42 no OS X (10.10) em uma máquina com 2,5 GHz i7 e 16 GB de RAM. O tamanho máximo da imagem que eu poderia processar sem o Firefox pedir para continuar a execução do script era 1600 x 1932 px .


Ungolfed

f = u => {
    i = new Image;
    i.src = u;
    i.onload = _ => {
        c = (d = document).createElement`canvas`;
        c.width = w = i.width;
        c.height = h = i.height;

        x = c.getContext`2d`;
        x.drawImage(i, 0, 0, w, h);

        p = x.getImageData(0, 0, w, h).data;

        t = [];
        f = [];

        for (j = 0; j < p.length;++j)
            t.push([p[j], p[++j], p[++j], p[++j]]);

        t.sort( (a,b) => a[0] > b[0] || a[0] == b[0] && a[1] > b[1] || a[0] == b[0] && a[1] == b[1] && a[2] > b[2] )
         .map(u => f.push.apply(f,u));

        x.putImageData( new ImageData( new Uint8ClampedArray(f), w, h), 0, 0);
        d.body.appendChild(c)
    }
}

Saída

Para uma comparação melhor, peguei também o " American Gothic " como fonte de exemplo:

insira a descrição da imagem aqui


Edições

  • Salva 24 bytes usando em for (a in b)vez defor(;;) . Graças a ar34z
  • Salva 3 bytes armazenandodocument em uma variável.
  • Economizou 4 bytes eliminando alguns destes() .
  • Economizou 10 bytes usando seqüências de caracteres de modelo marcadas , omitindo a ()criação do objeto e removendo outro par de redundantes (). Graças a apsillers .
  • Economizou 14 bytes por refatoração maciça do código que nivela a matriz de cores após a classificação. Muito obrigado aos apsillers e Ypnypn por um ao outro.
  • Economizou 1 byte , refatorando o forloop que obtém as cores de cada pixel.
insertusernamehere
fonte
11
Você pode minimizar os para-loops usando for(k in t)o que permitirá poupar mais alguns bytes :)
ar34z
11
Bom trabalho! Algumas melhorias: perder o ()in new Image(); use seqüências de caracteres de modelo marcadas para seus argumentos de sequência de caracteres ( createElement`canvas`, getContext`2d`), não use parênteses para parâmetros de funções de seta única (basta fazer f=u=>{...}; parens são apenas para funções de seta de parâmetro múltiplo ou de parâmetro zero). Além disso, você pode ter um ou dois forloops de instrução única com colchetes, que não são necessários.
Apsillers
Ah, na verdade, para funções de seta com argumento zero, use um argumento fictício de caractere único em vez de parens vazios de dois caracteres. (em i.onload=$=>...vez de i.onload=()=>...)
apsillers
Eu acho que for(l in u)f.push(u[l]);pode se tornarfor(z of u)f.push(z);
Ypnypn
@Ypnypn Pode ser ainda mais curto que isso :). - for(u of t)for(z of u)f.push(z)é bastante curto, mas pode ser reduzido ainda mais para t.map(u=>u.map(z=>f.push(z))). Em muitos casos, usar .mapou .somecom uma função de seta será mais curto do que usar um forloop. Se você quiser ir realmente louco, você pode economizar ainda mais aqui com t.map(u=>f.push.apply(f,u));o que diz "Para cada variedade uem t, a oferta ucomo uma lista de argumentos para f.pushvia apply(uma vez que pushpode aceitar um número ilimitado de argumentos e empurra todos eles em ordem).
Apsillers
4

Utilitários Bash + GNU, 80

s()(sed 1d $1|cut -d\  -f$2)
sed 1q $1
s $1 2-|sort -t_ -k1.16|paste <(s $1 1) -

Isso pressupõe que o formato de entrada / saída esteja no formato .txt da enumeração de pixels do ImageMagick. A entrada é passada como um nome de arquivo e a saída vai para STDOUT.


Se o exposto acima não for considerado um formato de imagem conhecido, podemos adicionar as conversões necessárias:

Bash + utilitários GNU + ImageMagick, 108

s()(sed 1d t|cut -d\  -f$1)
convert $1 txt:t
(sed 1q t
s 2-|sort -t_ -k1.16|paste <(s 1) -)|convert txt:- $2

Entrada e saída são especificadas como nomes de arquivos. O ImageMagick determina quais formatos de arquivo usar pelas extensões de arquivo passadas, para que possamos usar os mais comuns:

$ ./sortpixels.sh 398px-Grant_Wood_-_American_Gothic_-_Google_Art_Project.jpg o.png
$ 

O o.png resultante se parece com:

insira a descrição da imagem aqui

Trauma Digital
fonte
3

Python 2, 128 bytes

from PIL import*
a=Image.open('a')
b=a.load()
c,d=a.size
a.putdata(sorted(b[e,f]for f in range(d)for e in range(c)))
a.save('b')

Desde que a imagem seja um arquivo nomeado asem extensão, a saída será um arquivo nomeado bsem extensão.

gótico americano Gótico americano (classificado)

Zach Gates
fonte
Eu não verifiquei, mas você deve ser capaz de fazer algo como esse a.putdata(sorted(b[f/c,f%d]for f in range(d*c)))(acabei de acordar, talvez tenha misturado as variáveis).
Kade
Como você escreveu, não funcionou (índice fora do intervalo), mas não tentei alternar nenhuma variável (não tenho muito tempo no momento). @Shebang
Zach Gates
3

Java, 316 bytes

import javax.imageio.*;class C{public static void main(String[]a)throws Exception{java.awt.image.BufferedImage i=ImageIO.read(new java.io.File(a[0]));int w=i.getWidth(),h=i.getHeight(),v[]=i.getRGB(0,0,w,h,null,0,w);java.util.Arrays.sort(v);i.setRGB(0,0,w,h,v,0,w);ImageIO.write(i,"png",new java.io.File("a.png"));}}

Coloca os valores hexadecimais das cores de pixel em uma matriz. A matriz é classificada e as cores são remapeadas para os pixels na imagem. O nome da imagem resultante é a.png.

entrada de tulipas amarelas saída de tulipas amarelas
Entrada gótica americana insira a descrição da imagem aqui

TNT
fonte
3

SmileBASIC, 39 35 bytes

Supondo que a imagem seja carregada na página de gráficos 512 * 512:

DIM A[0]GSAVE A,0SORT A
GLOAD A,0,1

Explicado:

DIM IMG[0] 'create array
GSAVE IMG,0 'save graphics to array
SORT IMG 'sort
GLOAD IMG,0,1 'load graphics from array

É simples assim! Infelizmente, temos que usar números inteiros, o que adiciona 4 bytes ao tamanho do programa devido aos sufixos de tipo.

12Me21
fonte
Não sei ao certo por que são necessárias ints. Parece que o uso de carros alegóricos realmente obtém os resultados corretos. A execução desse código usando ints SYS/DEFSP.GRPcoloca um FF000000no canto superior esquerdo e um 00101010no canto inferior direito, que é o oposto aparente da pergunta. O uso de flutuadores coloca 00000000no canto superior esquerdo e FFF8F8F8no canto inferior direito, o que está correto. (Obviamente, isso trata as cores hexadecimais como canais não assinados / mais altos, o que provavelmente está correto.)
snail_
Eu acho que é válido, já que a pergunta não especifica uma ordem específica para classificar (e como os valores são assinados, 0xFF000000é menor que 0x00101010), mas, de qualquer forma, não sei ao certo por que usei números inteiros aqui ... o tempo em que não entendi como o GLOAD usava valores não assinados quando você usava uma matriz flutuante e presumi que não funcionava.
12Me21 13/04
2

Java, 424 417 404 bytes

Bem, este não é o idioma em que você quer jogar golfe ...

import java.awt.image.*;import java.io.*;import javax.imageio.*;class F{public static void main(String[]x)throws Exception{BufferedImage i,o;i=ImageIO.read(new File(x[0]));o=new BufferedImage(i.getWidth(),i.getHeight(),BufferedImage.TYPE_INT_RGB);o.setData(i.getRaster());int[]p=((DataBufferInt)o.getRaster().getDataBuffer()).getData();java.util.Arrays.sort(p);ImageIO.write(o,"png",new File("o.png"));}}
Peter Lenkefi
fonte
2

C #, 497 bytes

Primeira vez, primeiro golfe. Claramente não é o melhor para jogar golfe

Não respeitando realmente a tubulação. Pega um caminho de imagem como entrada e o gera com a letra "o" anexada ao nome.

Funciona melhor com bitmaps, resultados de probabilidades com outras pessoas

using System.Linq;using System.Drawing;using System.Runtime.InteropServices;class Program{static void Main(string[]args){using(var im=(Bitmap)Image.FromFile(args[0])){int h=im.Height;int w=im.Width;var b=im.LockBits(new Rectangle(0,0,w,h),System.Drawing.Imaging.ImageLockMode.ReadWrite,System.Drawing.Imaging.PixelFormat.Format32bppRgb);var p=new int[h*w];Marshal.Copy(b.Scan0,p,0,h*w);var q=p.ToList();q.Sort();p=q.ToArray();Marshal.Copy(p,0,b.Scan0,h*w);im.UnlockBits(b);im.Save("o"+args[0]);}}}
Cylianna
fonte
1

Haskell, 195 bytes

import Data.List
import Graphics.GD
f p=do 
 a<-loadPngFile p;(x,y)<-imageSize a;let l=[(i,j)|j<-[0..y],i<-[0..x]]
 mapM(flip getPixel a)l>>=mapM(\(d,c)->setPixel d c a).zip l.sort;savePngFile"o"a

Isso usa a GDbiblioteca. Uso f <filename>. O arquivo de entrada deve estar no pngformato. O arquivo de saída é nomeado o.

Como funciona: simples, ou seja, leia a imagem, passe por todas as coordenadas e obtenha os pixels, classifique os pixels, caminhe pelas coordenadas novamente, mas desta vez defina os pixels na ordem em que aparecem na lista classificada, escreva o arquivo como disco.

insira a descrição da imagem aqui

nimi
fonte