Ordenar los píxeles

35

Su tarea es crear un programa que, dada una imagen de entrada, cree una imagen de salida del mismo tamaño, donde todos los píxeles se ordenan por valor hexadecimal.

Su programa puede:

  • Ordene los píxeles de izquierda a derecha y luego hacia abajo o primero hacia abajo en columnas y luego hacia la derecha. En cualquier caso, el píxel superior izquierdo es el más pequeño y el inferior derecho es el más grande.
  • Use transparencia, pero esto no es obligatorio.
  • Ordene por RGB, pero puede usar CMY o cualquier otro formato con al menos 3 valores. Puede elegir qué valores ordenar. (HSV puede dar algunas buenas imágenes)
  • Use cualquier formato de imagen conocido que la mayoría de las computadoras puedan abrir.

Reglas:

  • La salida debe escribirse en el disco o ser canalizable a un archivo.
  • La entrada se proporciona como un argumento de línea de comandos, en forma de una ruta relativa a la imagen, o se canaliza desde la línea de comandos.
  • Este es el código de golf, por lo que gana el código más corto en bytes.
vrwim
fuente
2
Relacionado.
Martin Ender

Respuestas:

20

Pyth - 10 bytes

Lee la imagen, contrae el mapa de bits, ordena y luego divide el mapa de bits nuevamente, luego escribe.

.wclK'zSsK

No funciona en línea por razones obvias. Toma la entrada como ruta relativa al archivo de imagen y las salidas en o.png.

Salida del gótico americano:

Maltysen
fuente
3
Espero no ser el único en tener la impresión de que la imagen se está moviendo ...
Quentin
Se parece a la madera.
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)}}

salida de muestra

Demo ejecutable:

Cómo funciona este código es usarlo getImageDatapara obtener una matriz del formulario

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

Y mapa una variedad de la forma

[[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 los valores R se asignen a las matrices del conjunto RGBA, y los valores B, G y A se convierten en matrices cero de valor mínimo. Cuando clasificamos esta matriz, todas las [0,0,0,0]matrices se clasifican en la parte inferior y las matrices de valor real se clasifican normalmente en la 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

Desnatamos el cuarto superior de la matriz (para perder los valores vacíos que creamos), lo aplanamos [].concat.applyy terminamos con una matriz de la primera forma nuevamente, pero esta vez, está ordenada.

Ligeramente fuera de golf con espacios en blanco y comentarios:

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

Tenga en cuenta que la mayoría de los navegadores pueden fallar al ejecutar este código para imágenes grandes, porque pasa una gran cantidad de argumentos [].concat. Cuando el entorno del navegador no permite suficiente memoria para todos los argumentos, un enfoque alternativo es volver a mapear los valores RGBA de las primeras cuatro matrices nuevamente en la matriz, para una puntuación 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)}}

Simplemente reemplazamos el [].concat(...{stuff}).slice(12*w*h)con {stuff}.map((v,i,A)=>A[3*w*h+(i>>2)][i%4]).)

apsillers
fuente
¿Podría incluir una salida de ejemplo?
Paŭlo Ebermann el
@ PaŭloEbermann Hecho.
apsillers
@insertusernamehereOh, Dios, solo probé en imágenes pequeñas Mi concat.applyllamada está proporcionando demasiados argumentos concaty el motor JS la está rechazando. D:¡Gracias! Lo arreglaré y notaré los dos puntajes. (¡Y me alegro de poder ayudar!)
apsillers
@insertusernamehere Gracias por el aviso sobre la limitación de tamaño; Ahora también he publicado una versión un poco más larga que funciona en imágenes más grandes.
apsillers
@apsillers Buen trabajo. No puedo votar de nuevo por desgracia. :)
insertusernamehere
14

Mathematica 86 83 72 bytes

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

Con 14 bytes guardados gracias a @Martin Buttner.


Ejemplo

La imagen en sí es de entrada. Alternativamente, se podría usar una variable que contenga la imagen.

gótico

DavidC
fuente
El desafío especifica que los píxeles se clasifiquen por valor hexadecimal, pero si leo esto correctamente, utilizará la clasificación predeterminada de Mathematica para las listas {R, G, B} o {R, G, B, alpha}. No me queda claro que estos sean equivalentes.
Michael Stern, el
@MichaelStern No conozco Mathematica, pero si clasifica las tuplas por elementos como la mayoría de los idiomas, son equivalentes: los números como el hexadecimal se ordenan por cada dígito, dos de los cuales están representados por cada elemento en la tupla.
Maltysen
@MichaelStern, Maltysen está en lo correcto. La clasificación RGB y la clasificación Hex son equivalentes: la clasificación por R, luego G, luego el valor B funciona igual que la clasificación por valor posicional en Hex.
DavidC
OK, +1 de mi parte.
Michael Stern
ImageDatay ArrayReshapepodría usar la notación infija. Flattenes lo suficientemente largo como para guardar un par de bytes asignándolo a f. ¿Y realmente lo necesitas "Byte"? ¿El valor predeterminado simplemente no escalaría los valores del canal para [0,1]que la clasificación y la reconstrucción de la imagen siguieran funcionando bien?
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)}

Sin golf:

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
fuente
@insertusernamehere Funciona en el último Firefox. Al igual que su respuesta, se supone que hay un cuerpo para agregar el lienzo y que la imagen de origen es del mismo dominio.
Dendrobium
+1 Solución muy elegante. También procesa imágenes de hasta 1600 x 1900px.
insertusernamehere
2
Hoy aprendí que appendChilddevuelve su argumento. ¡Muy útil! Me inspiraste a reducir mi entrada de 377 a 354, pero no puedo vencer la tuya :). (Cuando uso tu técnica appendChildy tu encadenamiento puedo reducirlo witha 347, ¡pero aún así a 13!) ¡Excelente trabajo!
apsillers
5

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

C probablemente no sea el "cuchillo más afilado del estante" para este tipo de trabajo, de todos modos quería probarlo. Consejos para mejorar mi respuesta son bienvenidos. El programa obtiene el nombre del archivo de imagen de entrada como un 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));}

compilar y ejecutar: gcc -I/usr/include/SDL snippet.c -lSDL -lSDL_image && ./a.out

ingrese la descripción de la imagen aquí

No suelo jugar golf en C, pero ayer acabo de responder a este desafío y solo quería seguir jugando con ese nuevo juguete :)

gracias a @ pseudonym117 por ayudarme a ahorrar 5 bytes

dieter
fuente
Puede guardar 1 byte cambiando su whileal final a for(;;SDL_Flip(s));, y creo que puede omitir intel método Cy guardar 4 más.
seudónimo117
4

JavaScript (ES6), 452 480 484 487 511 bytes

Wow, esto se hizo más largo de lo 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)}}

La función toma una URL como entrada f('test.jpg');y dibuja el resultado en un canvaselemento que se adjunta a body.

Tenga en cuenta que la fuente debe estar en el mismo dominio o el script se detendrá con un problema de seguridad.


Limitaciones

Lo probé en Firefox 42 en OS X (10.10) en una máquina con i7 de 2.5 GHz y 16 GB de RAM. El tamaño máximo de imagen que podía procesar sin que Firefox pidiera continuar con la ejecución del script fue 1600 x 1932 px .


Sin golf

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

Salida

Para una mejor comparación, también tomé el " American Gothic " como fuente de ejemplo:

ingrese la descripción de la imagen aquí


Ediciones

  • Guardado 24 bytes usando en for (a in b)lugar de for(;;). Gracias a ar34z
  • Guardado 3 bytes al almacenar documenten una variable.
  • Ahorró 4 bytes al descartar algunos de estos ().
  • Ahorró 10 bytes mediante el uso de cadenas de plantillas etiquetadas , omitiendo la ()creación del objeto activado y eliminando otro par de redundantes (). Gracias a los apsillers .
  • Ahorró 14 bytes mediante una refactorización masiva del código que aplana la matriz de colores después de la clasificación. Muchas gracias a apsillers e Ypnypn por debilitarse mutuamente.
  • Guardado 1 byte por refactorización la for-loop que obtiene los colores de cada píxel.
insertusernamehere
fuente
1
Puede minimizar el uso de bucles for for(k in t)que ahorrará algunos bytes más :)
ar34z
1
¡Buen trabajo! Algunas mejoras: perder el ()en new Image(); use cadenas de plantillas etiquetadas para sus argumentos de cadena ( createElement`canvas`, getContext`2d`), no use paréntesis para los parámetros de funciones de flecha única (solo haga f=u=>{...}; los parens son solo para funciones de flecha de parámetros múltiples o parámetros cero). También es posible que tenga uno o dos forbucles de una sola declaración que tengan corchetes, que no son necesarios.
apsillers
Oh, en realidad, para las funciones de flecha de argumento cero, use un argumento ficticio de un solo carácter en lugar de dos pares vacíos de dos caracteres. (en i.onload=$=>...lugar de i.onload=()=>...)
apsillers
Creo que for(l in u)f.push(u[l]);puede convertirsefor(z of u)f.push(z);
Ypnypn
@Ypnypn Puede ser incluso más corto que eso :). - for(u of t)for(z of u)f.push(z)es bastante corto, pero se puede acortar aún más t.map(u=>u.map(z=>f.push(z))). En muchos casos, usar .mapo .somecon una función de flecha será más corto que usar un forbucle. Si quieres ir realmente loco, usted puede ahorrar aún más aquí con t.map(u=>f.push.apply(f,u));lo que dice "Por cada serie uen t, el suministro ucomo una lista de argumentos a f.pushtravés de apply(ya que pushpuede aceptar un número ilimitado de argumentos y empuja todos ellos en orden).
apsillers
4

Bash + GNU utils, 80

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

Esto supone que el formato de entrada / salida está en el formato .txt de enumeración de píxeles de ImageMagick. La entrada se pasa como un nombre de archivo y la salida va a STDOUT.


Si lo anterior no se considera un formato de imagen conocido, entonces podemos agregar las conversiones necesarias:

Bash + utilidades 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

La entrada y la salida se especifican como nombres de archivo. ImageMagick determina qué formatos de archivo utilizar las extensiones de archivo aprobadas, por lo que podemos usar cualquiera de los formatos comunes:

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

El o.png resultante se ve así:

ingrese la descripción de la imagen aquí

Trauma digital
fuente
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')

Siempre que la imagen sea un archivo llamado asin extensión, la salida será un archivo llamado bsin extensión.

gótico americano Gótico americano (ordenado)

Puertas de Zach
fuente
No lo he comprobado, pero en su lugar deberías poder hacer algo a.putdata(sorted(b[f/c,f%d]for f in range(d*c)))similar (acabo de despertar, por lo que puedo haber mezclado las variables).
Kade
Como lo escribió, no funcionó (índice fuera de rango), pero no he intentado cambiar ninguna variable (no tengo mucho tiempo en este 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 los valores hexadecimales de los colores de píxeles en una matriz. La matriz se ordena y los colores se reasignan a los píxeles de la imagen. El nombre de la imagen resultante es a.png.

entrada de tulipanes amarillos salida de tulipanes amarillos
Entrada gótica americana ingrese la descripción de la imagen aquí

TNT
fuente
3

SmileBASIC, 39 35 bytes

Suponiendo que la imagen se carga en la 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

¡Es así de simple! Desafortunadamente, tenemos que usar números enteros, que agregan 4 bytes al tamaño del programa debido a los sufijos de tipo.

12Me21
fuente
No estoy realmente seguro de por qué se requieren ints. Parece que el uso de flotadores en realidad obtiene los resultados correctos. Al ejecutar este código usando ints on, se SYS/DEFSP.GRPcoloca un FF000000en la parte superior izquierda y una 00101010en la parte inferior derecha, que es el opuesto aparente de la pregunta. El uso de flotadores pone 00000000en la parte superior izquierda y FFF8F8F8en la parte inferior derecha, lo cual es correcto. (Por supuesto, esto trata los colores hexadecimales como canales sin signo / superiores, lo que probablemente sea correcto).
caracol_
Creo que es válido, ya que la pregunta no especifica un orden específico para ordenar (y dado que los valores están firmados, 0xFF000000es más pequeño que 0x00101010), pero de todos modos, no estoy seguro de por qué usé números enteros aquí ... Creo que en la vez que no entendí cómo GLOAD usaba valores sin signo cuando usabas una matriz flotante, y simplemente asumía que no funcionaba.
12Me21
2

Java, 424 417 404 bytes

Bueno, este no es un idioma en el que quieras jugar golf ...

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
fuente
2

C #, 497 bytes

Primera publicación, primer golf. Claramente no es el mejor para jugar golf

Realmente no respetando las tuberías. Toma una ruta de la imagen como entrada y la muestra con la letra "o" antes del nombre.

Funciona mejor con mapas de bits, resultados de probabilidades con otros

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
fuente
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

Esto usa la GDbiblioteca. Uso f <filename>. El archivo de entrada debe estar en pngformato. El archivo de salida se llama o.

Cómo funciona: sencillo, es decir, leer la imagen, recorrer todas las coordenadas y obtener los píxeles, ordenar los píxeles, recorrer las coordenadas nuevamente, pero esta vez configure los píxeles en el orden en que aparecen en la lista ordenada, escriba el archivo en disco.

ingrese la descripción de la imagen aquí

nimi
fuente