Сортировка пикселей


35

Ваша задача - создать программу, которая при заданном входном изображении создаст выходное изображение одинакового размера, в котором все пиксели упорядочены по шестнадцатеричному значению.

Ваша программа может:

  • Сортировка пикселей слева направо, а затем вниз или сначала сортировка по столбцам, а затем вправо. В любом случае верхний левый пиксель самый маленький, а нижний правый самый большой.
  • Используйте прозрачность, но это не обязательно.
  • Сортировка по RGB, но вы можете использовать CMY или любой другой формат с как минимум 3 значениями. Вы можете выбрать, какие значения сортировать. (HSV может дать несколько хороших изображений)
  • Используйте любой известный формат изображения, который может открыть большинство компьютеров.

Правила:

  • Вывод должен быть записан на диск или передан в файл.
  • Входные данные задаются в качестве аргумента командной строки, в форме относительного пути к изображению или передаются из командной строки.
  • Это код гольф, поэтому выигрывает самый короткий код в байтах!

Ответы:


20

Pyth - 10 байт

Читает изображение, сворачивает растровое изображение, сортирует, а затем снова разбивает растровое изображение, а затем записывает.

.wclK'zSsK

Не работает онлайн по понятным причинам. Принимает ввод как относительный путь к файлу изображения и выводит в o.png.

Выход из американской готики:


3
Я надеюсь, что я не единственный, у кого сложилось впечатление, что картина движется ...
Квентин,

Это похоже на дерево.
Джо З.

19

JavaScript (ES6), 383 377 354 байта

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

образец вывода

Демоверсия запуска:

Как работает этот код, это использовать getImageDataдля получения массива вида

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

И mapэто массиву вида

[[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],
 ...]

Таким образом, значения R отображаются в массивы набора RGBA, а значения B, G и A превращаются в нулевые массивы минимального значения. Когда мы сортируем этот массив, все [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],
 [0,0,0,0],..., [R,G,B,A],[R,G,B,A],[R,G,B,A],...]
                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
               extract & flatten these sorted pixels

Мы просматриваем верхнюю четверть массива (чтобы потерять пустые значения, которые мы создали), сглаживаем его [].concat.applyи в итоге получаем массив первой формы, но на этот раз он отсортирован.

Немного де-гольф с пробелами и комментариями:

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

Обратите внимание, что большинство браузеров могут не запускать этот код для больших изображений, потому что он передает огромное количество аргументов [].concat. Когда среда браузера не позволяет достаточно памяти для всех аргументов, альтернативный подход состоит в том, чтобы повторно отобразить значения RGBA из верхних четвертых массивов обратно в массив, для общего результата 361 байт :

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

Мы просто заменить [].concat(...{stuff}).slice(12*w*h)с {stuff}.map((v,i,A)=>A[3*w*h+(i>>2)][i%4]).)


Не могли бы вы включить пример вывода?
Paŭlo Ebermann

@ PaŭloEbermann Готово.
Апсиллеры

@insertusernamehere О, черт, я тестировал только на маленьких изображениях. Мой concat.applyзвонок содержит слишком много аргументов, concatи движок JS отклоняет его. D:Благодарность! Я исправлю это и запомню две оценки. (И я рад, что смог помочь!)
Апсиллеры

@insertusernamehere Спасибо за сообщение об ограничении размера; Я также опубликовал немного более длинную версию, которая работает на больших изображениях.
Апсиллеры

@apsillers Отличная работа. К сожалению, не могу снова проголосовать. :)
insertusername здесь

14

Mathematica 86 83 72 байта

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

Благодаря 14 байтам, сохраненным благодаря @Martin Buttner.


пример

Само изображение является входным. В качестве альтернативы можно использовать переменную, содержащую изображение.

готика


Задача определяет, что пиксели сортируются по шестнадцатеричному значению, но если я правильно прочитал, вы используете сортировку Mathematica по умолчанию для списков {R, G, B} или {R, G, B, alpha}. Мне не ясно, что это эквивалентно.
Майкл Стерн

@MichaelStern Я не знаю Mathematica, но если он сортирует кортежи поэлементно, как и большинство языков, они эквивалентны: числа, такие как шестнадцатеричные, сортируются по каждой цифре, два из которых представлены каждым элементом в кортеже.
Maltysen

@MichaelStern, Maltysen правильный. Сортировка RGB и шестнадцатеричная сортировка эквивалентны: сортировка по R, затем по G, затем по значению B работает так же, как сортировка по местным значениям в Hex.
DavidC

ОК +1 от меня
Майкл Стерн

ImageDataи ArrayReshapeможет использовать инфиксную нотацию. Flattenдостаточно долго, чтобы сохранить пару байтов, назначив его f. А тебе на самом деле нужно "Byte"? Разве по умолчанию не будет просто масштабировать значения каналов [0,1]так, чтобы сортировка и восстановление изображений все равно работали?
Мартин Эндер

5

Javascript ES6, 334 байта

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
}

@insertusernamehere Работает на последней версии Firefox. Как и ваш ответ, предполагается, что есть тело, к которому нужно добавить холст, и что исходное изображение находится в том же домене.
Дендробиум

+1 Очень гладкое решение. Он также обрабатывает изображения до 1600 x 1900 пикселей.
имя пользователя здесь

2
Сегодня я узнал, что appendChildвозвращает свой аргумент. Очень полезный! Вы вдохновили меня сократить мою запись с 377 до 354, но я не могу победить вашу :). (Когда я использую вашу appendChildцепочку и withтехнику, я могу уменьшить ее до 347, но все равно до 13!) Отличная работа!
Апсиллеры

5

C (с использованием SDL1.2), 333 322 315 байт

Скорее всего, С не самый «острый нож на полке» для такого рода работ, я все равно хотел попробовать. Советы по улучшению моего ответа приветствуются. Программа получает имя файла входного изображения в качестве аргумента 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));}

скомпилируйте и запустите: gcc -I/usr/include/SDL snippet.c -lSDL -lSDL_image && ./a.out

введите описание изображения здесь

Я обычно не играю в гольф в Си, но я только что ответил на этот вызов вчера, и я просто хотел продолжить играть с этой новой игрушкой :)

спасибо @ pseudonym117 за помощь в экономии 5 байт


Можно сохранить 1 байт, изменив значение whileв конце на for(;;SDL_Flip(s));, и я считаю, что вы можете опустить intметод Cи сохранить еще 4.
псевдоним117

4

JavaScript (ES6), 452 480 484 487 511 байт

Вау, это дольше, чем ожидалось:

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

Функция принимает URL-адрес в качестве входного f('test.jpg');и выводит результат в canvas-элемент, который добавляется к body.

Обратите внимание, что источник должен находиться в том же домене, иначе сценарий остановится из-за проблемы безопасности.


Ограничения

Я протестировал его в Firefox 42 на OS X (10.10) на машине с 2,5 ГГц i7 и 16 ГБ оперативной памяти. Максимальный размер изображения, который я мог обработать без запроса Firefox о продолжении выполнения сценария, составлял 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)
    }
}

Выход

Для лучшего сравнения я также взял « Американскую готику » в качестве примера источника:

введите описание изображения здесь


Правки

  • Сохранено 24 байта при использовании for (a in b)вместо for(;;). Благодаря ar34z
  • Сохранено 3 байта путем сохранения documentв переменной.
  • Сохраненные 4 байта , отбрасывая некоторые из них ().
  • Сохранено 10 байт с помощью теговых шаблонных строк , исключение ()создания объекта on и удаление еще одной пары избыточных (). Спасибо апсиллерам .
  • Сохранено 14 байтов путем массивного рефакторинга кода, который выравнивает цветовой массив после сортировки. Большое спасибо апсиллерам и Ypnypn за то, что они подрезали друг друга.
  • Сохраненный 1 байт путем рефакторинга for-loop, который получает цвета каждого пикселя.

1
Вы можете свести к минимуму использование циклов for, for(k in t)что позволит сэкономить еще несколько байтов :)
ar34z

1
Хорошо сделано! Некоторые улучшения: потеря ()в new Image(); используйте помеченные шаблонные строки в качестве строковых аргументов ( createElement`canvas`, getContext`2d`), не используйте скобки для параметров функций с одной стрелкой (просто делайте f=u=>{...}; скобки используются только для многопараметрических функций или функций стрелок с нулевым параметром). Также у вас может быть один или два цикла с одним оператором, forкоторые имеют скобки, которые не нужны.
Апсиллеры

О, на самом деле, для функций стрелок с нулевым аргументом используйте односимвольный фиктивный аргумент вместо двухсимвольных пустых паренов. ( i.onload=$=>...вместо i.onload=()=>...)
апсиллеры

Я думаю, что for(l in u)f.push(u[l]);может статьfor(z of u)f.push(z);
Ypnypn

@Ypnypn Это может быть даже короче этого :). - for(u of t)for(z of u)f.push(z)чертовски коротко, но его можно сократить до t.map(u=>u.map(z=>f.push(z))). Во многих случаях использование .mapили .someс функцией стрелки будет короче, чем использование forцикла. Если вы хотите пойти на самом деле с ума, вы можете сэкономить еще больше здесь с t.map(u=>f.push.apply(f,u));которой говорит : «Для каждого массива uв t, поставки uв виде списка аргументов с f.pushпомощью apply(так как pushможет принимать неограниченное количество аргументов и толкает их все в порядке).
Апсиллеры

4

Утилиты 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) -

Предполагается, что формат ввода / вывода находится в формате перечисления пикселей ImageMagick .txt. Входные данные передаются в виде имени файла, а выходные данные передаются в STDOUT.


Если вышеупомянутое не считается широко известным форматом изображения, то мы можем добавить необходимые преобразования:

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

Вход и выход указываются в виде имен файлов. ImageMagick определяет, какие форматы файлов использовать переданные расширения файлов, поэтому мы можем использовать любые распространенные:

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

Получившийся файл o.png выглядит так:

введите описание изображения здесь


3

Python 2, 128 байт

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

При условии, что изображение является файлом с именем aбез расширения, на выходе будет файл с именем bбез расширения.

американская готика Американская готика (отсортировано)


Я не проверял, но вы должны быть в состоянии сделать что-то вроде a.putdata(sorted(b[f/c,f%d]for f in range(d*c)))этого (я только что проснулся, поэтому я, возможно, перепутал переменные).
Каде

Как вы уже написали, это не сработало (индекс вне диапазона), но я не пробовал переключаться между переменными (у меня сейчас мало времени). @Shebang
Зак Гейтс

3

Java, 316 байт

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

Размещает шестнадцатеричные значения цветов пикселей в массиве. Массив отсортирован, а цвета переназначены на пиксели изображения. Название полученного изображения a.png.

вход желтых тюльпанов выход желтых тюльпанов
Американский готический вход введите описание изображения здесь


3

SmileBASIC, 39 35 байт

Предполагая, что изображение загружено на графическую страницу 512 * 512:

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

Разъяснение:

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

Это так просто! К сожалению, мы должны использовать целые числа, которые добавляют 4 байта к размеру программы из-за суффиксов типов.


Я не совсем уверен, почему требуются целые. Кажется, что использование поплавков на самом деле дает правильные результаты. Выполнение этого кода с использованием целочисленных значений SYS/DEFSP.GRPставит a FF000000в верхнем левом углу и a 00101010в нижнем правом, что является очевидной противоположностью вопроса. Использование поплавков помещает 00000000вверху слева и FFF8F8F8внизу справа, что правильно. (Конечно, это рассматривает шестнадцатеричные цвета как неподписанный / более высокий канал больше, что, вероятно, правильно.)
snail_

Я думаю, что это действительно так, поскольку в вопросе не указан конкретный порядок сортировки (и поскольку значения подписаны, 0xFF000000он меньше, чем 0x00101010), но в любом случае, я не совсем уверен, почему я использовал здесь целые числа ... В то время я не понимал, как GLOAD использует значения без знака, когда вы использовали массив с плавающей точкой, и просто предположил, что он не работает.
12Me21

2

Java, 424 417 404 байта

Ну, это не тот язык, на котором вы хотите играть в гольф ...

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

2

C #, 497 байт

Первый пост, первый гольф. Очевидно, не самый лучший для игры в гольф

Не очень уважаю трубопровод. Принимает путь изображения в качестве входного и выводит его с буквой «о» перед именем.

Лучше работает с растровыми изображениями, результаты шансов с другими

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

1

Haskell, 195 байт

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

Это использует GD библиотеку. Использование f <filename>. Входной файл должен быть в pngформате. Выходной файл называется o.

Как это работает: просто, то есть читать картинку, пройтись по всем координатам и получить пиксели, отсортировать пиксели, снова пройтись по координатам, но на этот раз установите пиксели в порядке их появления в отсортированном списке, запишите файл в диск.

введите описание изображения здесь

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.