对像素排序


35

您的任务是创建一个给定输入图像的程序,该程序将创建相同大小的输出图像,其中所有像素均按十六进制值排序。

您的程序可能:

  • 按从左到右的顺序对像素进行排序,然后再进行降序排列,或者先按列进行排序,然后再按右顺序进行排序。无论如何,左上方的像素是最小的,而右下方的像素是最大的。
  • 使用透明度,但这不是必需的。
  • 按RGB排序,但是您可以使用CMY或任何其他具有至少3个值的格式。您可以选择要排序的值。(HSV可能会提供一些不错的图像)
  • 使用大多数计算机可以打开的任何已知图像格式。

规则:

  • 输出必须写入磁盘或可通过管道传输到文件。
  • 输入是作为命令行参数提供的,形式是图像的相对路径,或者从命令行通过管道输入。
  • 这是代码高尔夫,所以最短的代码以字节为单位!

Answers:


20

Pyth-10个字节

读取图像,折叠位图,排序,然后再次拆分位图,然后写入。

.wclK'zSsK

由于明显的原因无法在线工作。将输入作为图像文件的相对路径,然后输出到中o.png

American Gothic的输出:


3
我希望我不是唯一一个印象深刻的人……
Quentin

它看起来像木头。
JoeZ。16年

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]))。


您能否提供示例输出?
圣保罗Ebermann

@PaŭloEbermann完成。
a药

@insertusernamehere哦,天哪,我只在小图像上进行过测试。我的concat.apply电话提供了太多参数,concat而JS引擎拒绝了。D:谢谢!我将修复该问题并记下两个分数。(很高兴能为您提供帮助!)
apsillers

@insertusernamehere感谢您注意大小限制;我现在还发布了一个稍长的版本,可用于较大的图像。
a药

@apsillers很好。很遗憾,无法再次投票。:)
insertusername此处,2015年

14

Mathematica 86 83 72字节

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

@Martin Buttner节省了14个字节。


输入图像本身。或者,可以使用包含图像的变量。

哥特


挑战是指定像素按十六进制值排序,但是如果我没看错,则对{R,G,B}或{R,G,B,alpha}列表使用Mathematica的默认排序。我不清楚这些是否等效。
Michael Stern 2015年

@MichaelStern我不了解Mathematica,但是如果像大多数语言一样按元素对元组进行排序,则它们是等效的:十六进制之类的数字由每个数字排序,其中两个由元组中的每个元素表示。
Maltysen,2015年

@ MichaelStern,Maltysen是正确的。RGB排序和十六进制排序是等效的:按照R,G,B的顺序工作,就像Hex中的位置值排序一样。
DavidC

好,我+1。
迈克尔·斯特恩

ImageDataArrayReshape可以使用中缀表示法。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)}

取消高尔夫:

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。像您的答案一样,它假定有一个要向其附加画布的主体,并且源图像来自同一域。
2015年

+1非常时尚的解决方案。它还可以处理最大1600 x 1900px的图像。
2015年

2
今天,我了解到这appendChild返回了其论点。很有用!您启发了我将我的输入从377减少到354,但我不能完全击败您:)。(当我使用您的appendChild-chaining和with技术时,我可以将其降低到347,但仍然是13!)出色的工作!
Apsillers 2015年

5

C(使用SDL1.2),333个 322 315字节

对于这种工作,C很可能不是“架子上最锋利的刀”,无论如何,我都想尝试一下。欢迎提供改进我的答案的技巧。程序获取输入图像文件的名称作为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

在此处输入图片说明

我通常不使用C高尔夫,但是我昨天刚刚回答了这个挑战,我只想继续玩这个新玩具:)

感谢@ pseudonym117帮助我节省了5个字节


while将结尾处的更改为for(;;SDL_Flip(s));,可以节省1个字节,而且我相信您可以省略int该方法C,再节省4个字节。
pseudonym117

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

请注意,源必须位于同一域上,否则脚本会因安全问题而暂停。


局限性

我已经在装有2.5 GHz i7和16 GB RAM的计算机上的OS X(10.10)的Firefox 42中对其进行了测试。在不要求Firefox继续执行脚本的情况下,我可以处理的最大图像大小为1600 x 1932 px


不打高尔夫球

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个字节。感谢ar34zfor (a in b)for(;;)
  • 通过存储在变量中节省了3个字节document
  • 通过删除其中一些节省了4个字节()
  • 使用标记的模板字符串节省了10个字节,省略了on对象的创建并删除了另一对冗余的。多亏了a弹手()()
  • 通过对代码进行大规模重构,使排序后的颜色数组变平,节省了14个字节。非常感谢ApsillersYpnypn互相削弱。
  • 通过重构获取每个像素颜色的-loop,节省了1个字节for

1
您可以最小化for循环,使用for(k in t)它可以节省更多的字节:)
ar34z 2015年

1
干得好!一些改进:失去()new Image(); 请在字符串参数(,)中使用标记的模板字符串,不要在单箭头功能参数中使用括号(只需这样做; parens仅用于多参数或零参数箭头函数)。另外,您可能有一个或两个带括号的单语句循环,这不是必需的。createElement`canvas`getContext`2d`f=u=>{...}for
apsillers 2015年

哦,实际上,对于零参数箭头函数,请使用单字符伪参数而不是两字符空括号。(i.onload=$=>...而不是i.onload=()=>...
apsillers

我认为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));它说“对于每一个阵列ut,供应u作为参数列表f.push通过apply(因为push可以接受的参数的无限数量,并推动他们都按顺序)。
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) -

假定输入/输出格式为ImageMagick像素枚举.txt格式。输入作为文件名传递,输出进入STDOUT。


如果以上都不是众所周知的图像格式,那么我们可以添加必要的转换:

Bash + GNU utils + 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)))代替的方式做一些事情(我刚刚醒来,所以我可能混淆了变量)。
卡德2015年

如您所写,它不起作用(索引超出范围),但是我没有尝试切换任何变量(此刻我没有太多时间)。@Shebang
Zach Gates

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个字节。


我不太确定为什么需要整数。似乎使用浮点数实际上可以使结果正确。使用int上的代码运行该代码,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个字节

第一次发,第一次打高尔夫球。显然不是最适合打高尔夫球的

没有真正尊重管道。将图像路径作为输入并将其输出,并在名称前加上字母“ o”。

与位图配合使用效果更好,与其他人配合使用时效果不佳

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

工作原理:简单明了,即读取图片,遍历所有坐标并获取像素,对像素进行排序,再次遍历坐标,但是这次将像素按其在排序列表中出现的顺序进行设置,然后将文件写入磁盘。

在此处输入图片说明

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.