Reorganize os pixels na imagem para que não possam ser reconhecidos e recupere-os


86

Crie um programa que possa reorganizar os pixels da imagem para que não seja reconhecido. No entanto, seu programa poderá convertê-lo novamente para a imagem original.

Você pode escrever duas funções - para codificação e decodificação, no entanto, uma função aplicada repetidamente fornece a imagem original (exemplo em matemática - f(x) = 1 - x ) é um bônus.

Também produzir algum padrão na saída também oferece bônus.

A imagem pode ser representada como matriz 1D / 2D ou objeto de imagem, se o seu idioma suportar. Observe que você só pode alterar a ordem dos pixels!

Será lógico selecionar o código vencedor, que produz uma imagem menos reconhecível, no entanto, não sei como mensurá-lo exatamente, todas as maneiras que posso imaginar podem ser enganadas. Por isso, escolhi essa pergunta para ser um concurso de popularidade - deixe os usuários escolherem a melhor resposta!

Imagem de teste 1 (800 x 422 px): Imagem de teste 2 (800 x 480 px): forneça uma imagem de saída de código.


A questão é muito longa: "Escreva um algoritmo de criptografia para imagens, cuja saída é uma imagem".
David Richerby

3
@DavidRicherby… que usa os mesmos pixels / cores. Cinco pixels pretos na "imagem simples" -> cinco pixels pretos na "imagem cifrada".
Daniel Beck

2
@ user2992539 Tudo bem, nesse caso, você pode querer declarar explicitamente que isso é usado como desempate. Caso contrário, apenas dizer que é um bônus não é muito significativo.
Martin Ender

3
Essa pergunta me fez pensar no mapa de gatos de Arnold . Não acho que seja adequado para esse fim, mas é interessante da mesma maneira - repetir o mapa vezes o suficiente leva você de volta à imagem original.
Trichoplax

4
Agora em outro lugar na rede Stack Exchange: Security.SE de todos os lugares
Maçaneta da porta

Respostas:


58

Python 2.7 (com PIL) - sem pseudo-aleatoriedade

Eu divido a imagem em 2 por 2 blocos (ignorando o restante) e giro cada bloco em 180 graus, depois faço o mesmo com 3 por 3 blocos, depois 4, etc. até algum parâmetro BLKSZ. Então eu faço o mesmo para BLKSZ-1, depois BLKSZ-2, todo o caminho de volta para 3 e depois para 2. Esse método se inverte exatamente; a função desembaralhar é a função embaralhar.

O código :

from PIL import Image
import math

im = Image.open("ST1.png", "r")
arr = im.load() #pixel data stored in this 2D array

def rot(A, n, x1, y1): #this is the function which rotates a given block
    temple = []
    for i in range(n):
        temple.append([])
        for j in range(n):
            temple[i].append(arr[x1+i, y1+j])
    for i in range(n):
        for j in range(n):
            arr[x1+i,y1+j] = temple[n-1-i][n-1-j]


xres = 800
yres = 480
BLKSZ = 50 #blocksize
for i in range(2, BLKSZ+1):
    for j in range(int(math.floor(float(xres)/float(i)))):
        for k in range(int(math.floor(float(yres)/float(i)))):
            rot(arr, i, j*i, k*i)
for i in range(3, BLKSZ+1):
    for j in range(int(math.floor(float(xres)/float(BLKSZ+2-i)))):
        for k in range(int(math.floor(float(yres)/float(BLKSZ+2-i)))):
            rot(arr, BLKSZ+2-i, j*(BLKSZ+2-i), k*(BLKSZ+2-i))

im.save("ST1OUT "+str(BLKSZ)+".png")

print("Done!")

Dependendo do tamanho do bloco, é possível fazer com que o cálculo elimine todas as semelhanças com a imagem original: (BLKSZ = 50) insira a descrição da imagem aqui insira a descrição da imagem aqui

Ou torne a computação eficiente: (BLKSZ = 10) insira a descrição da imagem aqui insira a descrição da imagem aqui


6
Os melhores resultados serão se BLKSZ tiver cerca da metade do tamanho da imagem. Enfim, eu gosto de algoritmo e para pequenos BLKSZ parece uma arte moderna! Legal!
Somnium

11
Eu sempre votei em python.
Qd #

Em vez de disputar todos os valores de 2 a 50, talvez você deva usar apenas números primos?
Neil

@ Neil Provavelmente então parecerá mais aleatório e menos artístico.
Somnium

A BLKSZ = 10paisagem é muito legal!
wchargin

52

C #, WinForm

Editar Alterando a maneira como você preenche a matriz de coordenadas, você pode ter padrões diferentes - veja abaixo

Você gosta desse tipo de padrão?

Panorama

Abstrato

Bônus:

Grito Scream Scrambled

Troca aleatória exatamente uma vez todos os pixels na metade superior com todos os pixels na metade inferior. Repita o mesmo procedimento para desembaralhar (bônus).

Código

Scramble.cs

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Windows.Forms;
using System.Drawing.Imaging;
using System.IO;

namespace Palette
{
    public partial class Scramble : Form
    {
        public Scramble()
        {
            InitializeComponent();
        }

        public struct Coord
        {
            public int x, y;
        }

        private void Work(Bitmap srcb, Bitmap outb)
        {
            int w = srcb.Width, h = srcb.Height;
            Coord[] coord = new Coord[w * h];

            FastBitmap fsb = new FastBitmap(srcb);
            FastBitmap fob = new FastBitmap(outb);
            fsb.LockImage();
            fob.LockImage();
            ulong seed = 0;
            int numpix = 0;
            for (int y = 0; y < h; y++)
                for (int x = 0; x < w; numpix++, x++)
                {
                    coord[numpix].x = x;
                    coord[numpix].y = y;
                    uint color = fsb.GetPixel(x, y);
                    seed += color;
                    fob.SetPixel(x, y, color);
                }
            fsb.UnlockImage();
            fob.UnlockImage();
            pbOutput.Refresh();
            Application.DoEvents();

            int half = numpix / 2;
            int limit = half;
            XorShift rng = new XorShift(seed);
            progressBar.Visible = true;
            progressBar.Maximum = limit;

            fob.LockImage();
            while (limit > 0)
            {
                int p = (int)(rng.next() % (uint)limit);
                int q = (int)(rng.next() % (uint)limit);
                uint color = fob.GetPixel(coord[p].x, coord[p].y); 
                fob.SetPixel(coord[p].x, coord[p].y, fob.GetPixel(coord[half+q].x, coord[half+q].y)); 
                fob.SetPixel(coord[half+q].x, coord[half+q].y, color); 
                limit--;
                if (p < limit)
                {
                    coord[p]=coord[limit];
                }
                if (q < limit)
                {
                    coord[half+q]=coord[half+limit];
                }
                if ((limit & 0xfff) == 0)
                {
                    progressBar.Value = limit;
                    fob.UnlockImage();
                    pbOutput.Refresh();
                    fob.LockImage();
                }
            }
            fob.UnlockImage();
            pbOutput.Refresh();
            progressBar.Visible = false; 
        }

        void DupImage(PictureBox s, PictureBox d)
        {
            if (d.Image != null)
                d.Image.Dispose();
            d.Image = new Bitmap(s.Image.Width, s.Image.Height);  
        }

        void GetImagePB(PictureBox pb, string file)
        {
            Bitmap bms = new Bitmap(file, false);
            Bitmap bmp = bms.Clone(new Rectangle(0, 0, bms.Width, bms.Height), PixelFormat.Format32bppArgb);
            bms.Dispose(); 
            if (pb.Image != null)
                pb.Image.Dispose();
            pb.Image = bmp;
        }

        private void btnOpen_Click(object sender, EventArgs e)
        {
            OpenFileDialog openFileDialog = new OpenFileDialog();

            openFileDialog.InitialDirectory = "c:\\temp\\";
            openFileDialog.Filter = "Image Files(*.BMP;*.JPG;*.PNG)|*.BMP;*.JPG;*.PNG|All files (*.*)|*.*";
            openFileDialog.FilterIndex = 1;
            openFileDialog.RestoreDirectory = true;

            if (openFileDialog.ShowDialog() == DialogResult.OK)
            {
                try
                {
                    string file = openFileDialog.FileName;
                    GetImagePB(pbInput, file);
                    pbInput.Tag = file;
                    DupImage(pbInput, pbOutput);
                    Work(pbInput.Image as Bitmap, pbOutput.Image as Bitmap);
                    file = Path.GetDirectoryName(file) + Path.DirectorySeparatorChar + Path.GetFileNameWithoutExtension(file) + ".scr.png";
                    pbOutput.Image.Save(file);
                }
                catch (Exception ex)
                {
                    MessageBox.Show("Error: Could not read file from disk. Original error: " + ex.Message);
                }

            }
        }
    }

    //Adapted from Visual C# Kicks - http://www.vcskicks.com/
    unsafe public class FastBitmap
    {
        private Bitmap workingBitmap = null;
        private int width = 0;
        private BitmapData bitmapData = null;
        private Byte* pBase = null;

        public FastBitmap(Bitmap inputBitmap)
        {
            workingBitmap = inputBitmap;
        }

        public BitmapData LockImage()
        {
            Rectangle bounds = new Rectangle(Point.Empty, workingBitmap.Size);

            width = (int)(bounds.Width * 4 + 3) & ~3;

            //Lock Image
            bitmapData = workingBitmap.LockBits(bounds, ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
            pBase = (Byte*)bitmapData.Scan0.ToPointer();
            return bitmapData;
        }

        private uint* pixelData = null;

        public uint GetPixel(int x, int y)
        {
            pixelData = (uint*)(pBase + y * width + x * 4);
            return *pixelData;
        }

        public uint GetNextPixel()
        {
            return *++pixelData;
        }

        public void GetPixelArray(int x, int y, uint[] Values, int offset, int count)
        {
            pixelData = (uint*)(pBase + y * width + x * 4);
            while (count-- > 0)
            {
                Values[offset++] = *pixelData++;
            }
        }

        public void SetPixel(int x, int y, uint color)
        {
            pixelData = (uint*)(pBase + y * width + x * 4);
            *pixelData = color;
        }

        public void SetNextPixel(uint color)
        {
            *++pixelData = color;
        }

        public void UnlockImage()
        {
            workingBitmap.UnlockBits(bitmapData);
            bitmapData = null;
            pBase = null;
        }
    }

    public class XorShift
    {
        private ulong x; /* The state must be seeded with a nonzero value. */

        public XorShift(ulong seed)
        {
            x = seed;
        }

        public ulong next()
        {
            x ^= x >> 12; // a
            x ^= x << 25; // b
            x ^= x >> 27; // c
            return x * 2685821657736338717L;
        }
    }
} 

Scramble.designer.cs

namespace Palette
{
    partial class Scramble
    {
        private System.ComponentModel.IContainer components = null;

        protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }

        private void InitializeComponent()
        {
            this.panel = new System.Windows.Forms.FlowLayoutPanel();
            this.pbInput = new System.Windows.Forms.PictureBox();
            this.pbOutput = new System.Windows.Forms.PictureBox();
            this.progressBar = new System.Windows.Forms.ProgressBar();
            this.btnOpen = new System.Windows.Forms.Button();
            this.panel.SuspendLayout();
            ((System.ComponentModel.ISupportInitialize)(this.pbInput)).BeginInit();
            ((System.ComponentModel.ISupportInitialize)(this.pbOutput)).BeginInit();
            this.SuspendLayout();
            // 
            // panel
            // 
            this.panel.AutoScroll = true;
            this.panel.AutoSize = true;
            this.panel.Controls.Add(this.pbInput);
            this.panel.Controls.Add(this.pbOutput);
            this.panel.Dock = System.Windows.Forms.DockStyle.Top;
            this.panel.Location = new System.Drawing.Point(0, 0);
            this.panel.Name = "panel";
            this.panel.Size = new System.Drawing.Size(748, 306);
            this.panel.TabIndex = 3;
            // 
            // pbInput
            // 
            this.pbInput.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
            this.pbInput.Location = new System.Drawing.Point(3, 3);
            this.pbInput.MinimumSize = new System.Drawing.Size(100, 100);
            this.pbInput.Name = "pbInput";
            this.pbInput.Size = new System.Drawing.Size(100, 300);
            this.pbInput.SizeMode = System.Windows.Forms.PictureBoxSizeMode.AutoSize;
            this.pbInput.TabIndex = 3;
            this.pbInput.TabStop = false;
            // 
            // pbOutput
            // 
            this.pbOutput.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
            this.pbOutput.Location = new System.Drawing.Point(109, 3);
            this.pbOutput.MinimumSize = new System.Drawing.Size(100, 100);
            this.pbOutput.Name = "pbOutput";
            this.pbOutput.Size = new System.Drawing.Size(100, 300);
            this.pbOutput.SizeMode = System.Windows.Forms.PictureBoxSizeMode.AutoSize;
            this.pbOutput.TabIndex = 4;
            this.pbOutput.TabStop = false;
            // 
            // progressBar
            // 
            this.progressBar.Dock = System.Windows.Forms.DockStyle.Bottom;
            this.progressBar.Location = new System.Drawing.Point(0, 465);
            this.progressBar.Name = "progressBar";
            this.progressBar.Size = new System.Drawing.Size(748, 16);
            this.progressBar.TabIndex = 5;
            // 
            // btnOpen
            // 
            this.btnOpen.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
            this.btnOpen.Location = new System.Drawing.Point(12, 429);
            this.btnOpen.Name = "btnOpen";
            this.btnOpen.Size = new System.Drawing.Size(53, 30);
            this.btnOpen.TabIndex = 6;
            this.btnOpen.Text = "Start";
            this.btnOpen.UseVisualStyleBackColor = true;
            this.btnOpen.Click += new System.EventHandler(this.btnOpen_Click);
            // 
            // Scramble
            // 
            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.BackColor = System.Drawing.SystemColors.ControlDark;
            this.ClientSize = new System.Drawing.Size(748, 481);
            this.Controls.Add(this.btnOpen);
            this.Controls.Add(this.progressBar);
            this.Controls.Add(this.panel);
            this.Name = "Scramble";
            this.Text = "Form1";
            this.panel.ResumeLayout(false);
            this.panel.PerformLayout();
            ((System.ComponentModel.ISupportInitialize)(this.pbInput)).EndInit();
            ((System.ComponentModel.ISupportInitialize)(this.pbOutput)).EndInit();
            this.ResumeLayout(false);
            this.PerformLayout();

        }


        private System.Windows.Forms.FlowLayoutPanel panel;
        private System.Windows.Forms.PictureBox pbOutput;
        private System.Windows.Forms.ProgressBar progressBar;
        private System.Windows.Forms.PictureBox pbInput;
        private System.Windows.Forms.Button btnOpen;
    }
}

Program.cs

using System;
using System.Collections.Generic;
using System.Windows.Forms;

namespace Palette
{
  static class Program
  {
    [STAThread]
    static void Main()
    {
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);
        Application.Run(new Scramble());
    }
  }
}

Marque 'Código não seguro' na propriedade do projeto para compilar.

Padrão complexo

Passeio

Altere a primeira parte da função de trabalho, até Application.DoEvents:

        int w = srcb.Width, h = srcb.Height;
        string Msg = "Scramble";

        Graphics gr = Graphics.FromImage(outb);

        Font f = new Font("Arial", 100, FontStyle.Bold);
        var size = gr.MeasureString(Msg, f);
        f = new Font("Arial", w / size.Width * 110, FontStyle.Bold);
        size = gr.MeasureString(Msg, f);
        gr.DrawString(Msg, f, new SolidBrush(Color.White), (w - size.Width) / 2, (h - size.Height) / 2);

        gr.Dispose();

        Coord[] coord = new Coord[w * h];
        FastBitmap fsb = new FastBitmap(srcb);
        FastBitmap fob = new FastBitmap(outb);
        fsb.LockImage();
        fob.LockImage();
        ulong seed = 1;
        int numpix = h * w;
        int c1 = 0, c2 = numpix;
        int y2 = h / 2;

        int p2 = numpix/2;

        for (int p = 0; p < p2; p++)
        {
            for (int s = 1; s > -2; s -= 2)
            {
                int y = (p2+s*p) / w;
                int x = (p2+s*p) % w;

                uint d = fob.GetPixel(x, y);
                if (d != 0)
                {
                    c2--;
                    coord[c2].x = x;
                    coord[c2].y = y;
                }
                else
                {
                    coord[c1].x = x;
                    coord[c1].y = y;
                    c1++;
                }
                fob.SetPixel(x, y, fsb.GetPixel(x, y));
            }
        }
        fsb.UnlockImage();
        fob.UnlockImage();
        pbOutput.Refresh();
        Application.DoEvents();

1
Interessante) Gostaria de saber se, com uma abordagem semelhante, é possível criar padrões mais complicados na saída.
Somnium 24/07

1
Boa ideia - o que acontece com a linha do meio quando há um número ímpar de linhas?
flawr

1
@ flawr a divisão é por pixel. Se houver um número ímpar de pixels, o último ficará intocado. Se houver um número ímpar de linhas, a metade esquerda da linha do meio é 'lado superior' e a metade direita é 'lado inferior'.
Edc65

1
@ user2992539 Acho que você pode subdividir mais - até quadriculado. Com mais subdivisões, a imagem é mais reconhecível.
edc65

7
Como sua versão do "Scramble"!)
Somnium

43

C, desfoque arbitrário, facilmente reversível

Tarde para a festa. Aqui está a minha entrada!

Este método faz um borrão embaralhado. Eu chamo de scramblur . É extremamente simples. Em um loop, ele escolhe um pixel aleatório e o troca com um pixel próximo escolhido aleatoriamente em um modelo de tela toroidal. Você especifica a distância máxima que define o que "pixel próximo" significa (1 significa sempre escolher um pixel adjacente), o número de iterações e, opcionalmente, uma semente de número aleatório. Quanto maior a distância máxima e maior o número de iterações, mais desfocado o resultado.

É reversível especificando um número negativo de iterações (isso é simplesmente uma conveniência da interface da linha de comandos; na verdade, não existe iterações negativas). Internamente, ele usa um LCPRNG personalizado de 64 bits (gerador de números pseudo-aleatórios lineares congruentes) e pré-gera um bloco de valores. A tabela permite fazer um loop pelo bloco para frente ou para trás para embaralhar ou desembaralhar, respectivamente.

Demo

Nas duas primeiras imagens, à medida que você rola para baixo, cada imagem é desfocada usando um deslocamento máximo mais alto: Mais acima é a imagem original (por exemplo, deslocamento de 0 pixel), seguida por 1, 2, 4, 8, 16, 32, 64 , 128 e finalmente 256. A contagem da iteração é 10⁶ = 1.000.000 para todas as imagens abaixo.

Nas duas segundas imagens, cada imagem é desfocada usando um deslocamento progressivamente mais baixo - por exemplo, mais embaçado para menos embaçado - de um deslocamento máximo de 256 para 0. Desfrute!

Panorama Abstrato

E para as próximas duas imagens, você pode ver as progressões em tamanho real aqui e aqui :

Liberando o mal Simpsons

Código

Eu hackeei isso juntos em cerca de uma hora enquanto acordava hoje de manhã e ele quase não contém documentação. Posso voltar em alguns dias e adicionar mais documentação posteriormente, se as pessoas solicitarem.

//=============================================================================
// SCRAMBLUR
//
// This program is a image-processing competition entry which scrambles or
// descrambles an image based on a pseudorandom process.  For more details,
// information, see:
//
//    http://codegolf.stackexchange.com/questions/35005
//
// It is assumed that you have the NETPBM package of image-processing tools
// installed on your system.  This can be obtained from:
//
//    http://netpbm.sourceforge.net/
//
// or by using your system's package manager, e.g., yum, apt-get, port, etc.
//
// Input to the program is a 24-bit PNM image (type "P6").  Output is same.
// Example command-line invocation:
//
// pngtopnm original.png  | scramblur 100  1000000 | pnmtopng >scrambled.png
// pngtopnm scrambled.png | scramblur 100 -1000000 | pnmtopng >recovered.png
//
//
// Todd S. Lehman, July 2014

#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include <string.h>

typedef uint8_t uint8;
typedef uint64_t uint64;

//-----------------------------------------------------------------------------
// PIXEL STRUCTURE

#pragma pack(push, 1)
typedef struct
{
  uint8 r, g, b;     // Red, green, and blue color components
}
Pixel;
#pragma pack(pop)

//-----------------------------------------------------------------------------
// IMAGE STRUCTURE

typedef struct
{
  int width;          // Width of image in pixels
  int height;         // Height of image in pixels
  int pixel_count;    // Total number of pixels in image (e.g., width * height)
  int maxval;         // Maximum pixel component value (e.g., 255)
  Pixel *data;        // One-dimensional array of pixels
}
Image;

//-----------------------------------------------------------------------------
// 64-BIT LCG TABLE

static const long lcg64_table_length = 1000000;  // 10⁶ entries => 8 Megabytes

static uint64 lcg64_table[lcg64_table_length];

//-----------------------------------------------------------------------------
// GET 64-BIT LCG VALUE FROM TABLE

uint64 lcg64_get(long const iteration)
{
  return lcg64_table[iteration % lcg64_table_length];
}

//-----------------------------------------------------------------------------
// INITIALIZE 64-BIT LCG TABLE

void lcg64_init(uint64 const seed)
{
  uint64 x = seed;
  for (long iteration = 0; iteration < lcg64_table_length; iteration++)
  {
    uint64 const a = UINT64_C(6364136223846793005);
    uint64 const c = UINT64_C(1442695040888963407);
    x = (x * a) + c;
    lcg64_table[iteration] = x;
  }
}

//-----------------------------------------------------------------------------
// READ BINARY PNM IMAGE

Image image_read(FILE *const file)
{
  Image image = { .data = NULL };

  char *line = NULL;
  size_t linecap = 0;

  // Read image type.  (Currently only P6 is supported here.)
  if (getline(&line, &linecap, file) < 0) goto failure;
  if (strcmp(line, "P6\n") != 0) goto failure;

  // Read width and height of image in pixels.
  {
    if (getline(&line, &linecap, file) < 0) goto failure;
    char *pwidth = &line[0];
    char *pheight = strchr(line, ' ');
    if (pheight != NULL) pheight++; else goto failure;
    image.width = atoi(pwidth);
    image.height = atoi(pheight);
    image.pixel_count = image.width * image.height;
  }

  // Read maximum color value.  (Currently only 255 is supported here.)
  {
    if (getline(&line, &linecap, file) < 0) goto failure;
    image.maxval = atoi(line);
    if (image.maxval != 255)
      goto failure;
  }

  // Allocate image buffer and read image data.
  if (!(image.data = calloc(image.pixel_count, sizeof(Pixel))))
    goto failure;

  if (fread(image.data, sizeof(Pixel), image.pixel_count, file) !=
      image.pixel_count)
    goto failure;

success:
  free(line);
  return image;

failure:
  free(line);
  free(image.data); image.data = NULL;
  return image;
}

//-----------------------------------------------------------------------------
// WRITE BINARY PNM IMAGE

void image_write(const Image image, FILE *const file)
{
  printf("P6\n");
  printf("%d %d\n", image.width, image.height);
  printf("%d\n", image.maxval);
  (void)fwrite(image.data, sizeof(Pixel), image.pixel_count, file);
}

//-----------------------------------------------------------------------------
// DISCARD IMAGE

void image_discard(Image image)
{
  free(image.data);
}

//-----------------------------------------------------------------------------
// SCRAMBLE OR UNSCRAMBLE IMAGE

void image_scramble(Image image,
                    int const max_delta,
                    long const iterations,
                    uint64 const lcg64_seed)
{
  if (max_delta == 0) return;

  int neighborhood1 = (2 * max_delta) + 1;
  int neighborhood2 = neighborhood1 * neighborhood1;

  lcg64_init(lcg64_seed);

  long iteration_start = (iterations >= 0)? 0 : -iterations;
  long iteration_end   = (iterations >= 0)? iterations : 0;
  long iteration_inc   = (iterations >= 0)? 1 : -1;

  for (long iteration = iteration_start;
       iteration != iteration_end;
       iteration += iteration_inc)
  {
    uint64 lcg64 = lcg64_get(iteration);

    // Choose random pixel.
    int pixel_index = (int)((lcg64 >> 0) % image.pixel_count);

    // Choose random pixel in the neighborhood.
    int d2 = (int)((lcg64 >> 8) % neighborhood2);
    int dx = (d2 % neighborhood1) - (neighborhood1 / 2);
    int dy = (d2 / neighborhood1) - (neighborhood1 / 2);
    int other_pixel_index = pixel_index + dx + (dy * image.width);
    while (other_pixel_index < 0)
      other_pixel_index += image.pixel_count;
    other_pixel_index %= image.pixel_count;

    // Swap pixels.
    Pixel t = image.data[pixel_index];
    image.data[pixel_index] = image.data[other_pixel_index];
    image.data[other_pixel_index] = t;
  }
}

//-----------------------------------------------------------------------------
int main(const int argc, char const *const argv[])
{
  int max_delta     = (argc > 1)? atoi(argv[1]) : 1;
  long iterations   = (argc > 2)? atol(argv[2]) : 1000000;
  uint64 lcg64_seed = (argc > 3)? (uint64)strtoull(argv[3], NULL, 10) : 0;

  Image image = image_read(stdin);
  if (!image.data) { fprintf(stderr, "Invalid input\n"), exit(1); }

  image_scramble(image, max_delta, iterations, lcg64_seed);

  image_write(image, stdout);

  image_discard(image);

  return 0;
}

4
Apenas rolada passado esta resposta, parece incrível
Thomas

1
Esta resposta é realmente alta. Você acha que pode mover as imagens extras (ou seja, tudo, exceto as duas imagens de teste, totalmente desfocadas) para uma galeria externa?
Tim S.

@TimS. - feito! encolheu-os em pequenas miniaturas.
Todd Lehman

42

Python 3.4

  • Bônus 1: Inverso: repetir restaura a imagem original.
  • Imagem principal opcional: a imagem original só pode ser restaurada usando a mesma imagem principal novamente.
  • Bônus 2: Padrão de produção na saída: a imagem principal é aproximada nos pixels embaralhados.

Quando o bônus 2 é alcançado, usando uma imagem-chave adicional, o bônus 1 não é perdido. O programa ainda é auto-inverso, desde que seja executado com a mesma imagem-chave novamente.

Uso padrão

Imagem de teste 1:

Imagem de teste codificada 1

Imagem de teste 2:

Imagem de teste mexida 2

A execução do programa com um único arquivo de imagem como argumento salva um arquivo de imagem com os pixels misturados uniformemente por toda a imagem. A execução novamente com a saída codificada salva um arquivo de imagem com a codificação aplicada novamente, o que restaura o original, pois o processo de codificação é inverso.

O processo de codificação é inverso, porque a lista de todos os pixels é dividida em 2 ciclos, para que cada pixel seja trocado por um e apenas um outro pixel. A execução uma segunda vez troca todos os pixels com os pixels com os quais foi trocada pela primeira vez, colocando tudo de volta ao início. Se houver um número ímpar de pixels, haverá um que não se moverá.

Graças à resposta de mfvonh como a primeira a sugerir 2 ciclos.

Uso com uma imagem principal

Imagem de teste de embaralhamento 1 com imagem de teste 2 como imagem principal

Teste de embaralhamento 1 com teste 2

Imagem de teste de codificação 2 com imagem de teste 1 como imagem principal

Teste de precipitação 2 com teste 1

A execução do programa com um segundo argumento de arquivo de imagem (a imagem principal) divide a imagem original em regiões com base na imagem principal. Cada uma dessas regiões é dividida em 2 ciclos separadamente, para que toda a confusão ocorra dentro das regiões e os pixels não sejam movidos de uma região para outra. Isso espalha os pixels sobre cada região e, portanto, as regiões se tornam uma cor manchada uniforme, mas com uma cor média ligeiramente diferente para cada região. Isso fornece uma aproximação aproximada da imagem principal, nas cores erradas.

A execução novamente troca os mesmos pares de pixels em cada região, para que cada região seja restaurada ao seu estado original e a imagem como um todo reapareça.

Graças à resposta do edc65 como a primeira a sugerir a divisão da imagem em regiões. Eu queria expandir isso para usar regiões arbitrárias, mas a abordagem de trocar tudo na região 1 por tudo na região 2 significava que as regiões tinham que ter o mesmo tamanho. Minha solução é manter as regiões isoladas umas das outras e simplesmente embaralhar cada região em si mesma. Como as regiões não precisam mais ter tamanho semelhante, torna-se mais simples aplicar regiões de formato arbitrário.

Código

import os.path
from PIL import Image   # Uses Pillow, a fork of PIL for Python 3
from random import randrange, seed


def scramble(input_image_filename, key_image_filename=None,
             number_of_regions=16777216):
    input_image_path = os.path.abspath(input_image_filename)
    input_image = Image.open(input_image_path)
    if input_image.size == (1, 1):
        raise ValueError("input image must contain more than 1 pixel")
    number_of_regions = min(int(number_of_regions),
                            number_of_colours(input_image))
    if key_image_filename:
        key_image_path = os.path.abspath(key_image_filename)
        key_image = Image.open(key_image_path)
    else:
        key_image = None
        number_of_regions = 1
    region_lists = create_region_lists(input_image, key_image,
                                       number_of_regions)
    seed(0)
    shuffle(region_lists)
    output_image = swap_pixels(input_image, region_lists)
    save_output_image(output_image, input_image_path)


def number_of_colours(image):
    return len(set(list(image.getdata())))


def create_region_lists(input_image, key_image, number_of_regions):
    template = create_template(input_image, key_image, number_of_regions)
    number_of_regions_created = len(set(template))
    region_lists = [[] for i in range(number_of_regions_created)]
    for i in range(len(template)):
        region = template[i]
        region_lists[region].append(i)
    odd_region_lists = [region_list for region_list in region_lists
                        if len(region_list) % 2]
    for i in range(len(odd_region_lists) - 1):
        odd_region_lists[i].append(odd_region_lists[i + 1].pop())
    return region_lists


def create_template(input_image, key_image, number_of_regions):
    if number_of_regions == 1:
        width, height = input_image.size
        return [0] * (width * height)
    else:
        resized_key_image = key_image.resize(input_image.size, Image.NEAREST)
        pixels = list(resized_key_image.getdata())
        pixel_measures = [measure(pixel) for pixel in pixels]
        distinct_values = list(set(pixel_measures))
        number_of_distinct_values = len(distinct_values)
        number_of_regions_created = min(number_of_regions,
                                        number_of_distinct_values)
        sorted_distinct_values = sorted(distinct_values)
        while True:
            values_per_region = (number_of_distinct_values /
                                 number_of_regions_created)
            value_to_region = {sorted_distinct_values[i]:
                               int(i // values_per_region)
                               for i in range(len(sorted_distinct_values))}
            pixel_regions = [value_to_region[pixel_measure]
                             for pixel_measure in pixel_measures]
            if no_small_pixel_regions(pixel_regions,
                                      number_of_regions_created):
                break
            else:
                number_of_regions_created //= 2
        return pixel_regions


def no_small_pixel_regions(pixel_regions, number_of_regions_created):
    counts = [0 for i in range(number_of_regions_created)]
    for value in pixel_regions:
        counts[value] += 1
    if all(counts[i] >= 256 for i in range(number_of_regions_created)):
        return True


def shuffle(region_lists):
    for region_list in region_lists:
        length = len(region_list)
        for i in range(length):
            j = randrange(length)
            region_list[i], region_list[j] = region_list[j], region_list[i]


def measure(pixel):
    '''Return a single value roughly measuring the brightness.

    Not intended as an accurate measure, simply uses primes to prevent two
    different colours from having the same measure, so that an image with
    different colours of similar brightness will still be divided into
    regions.
    '''
    if type(pixel) is int:
        return pixel
    else:
        r, g, b = pixel[:3]
        return r * 2999 + g * 5869 + b * 1151


def swap_pixels(input_image, region_lists):
    pixels = list(input_image.getdata())
    for region in region_lists:
        for i in range(0, len(region) - 1, 2):
            pixels[region[i]], pixels[region[i+1]] = (pixels[region[i+1]],
                                                      pixels[region[i]])
    scrambled_image = Image.new(input_image.mode, input_image.size)
    scrambled_image.putdata(pixels)
    return scrambled_image


def save_output_image(output_image, full_path):
    head, tail = os.path.split(full_path)
    if tail[:10] == 'scrambled_':
        augmented_tail = 'rescued_' + tail[10:]
    else:
        augmented_tail = 'scrambled_' + tail
    save_filename = os.path.join(head, augmented_tail)
    output_image.save(save_filename)


if __name__ == '__main__':
    import sys
    arguments = sys.argv[1:]
    if arguments:
        scramble(*arguments[:3])
    else:
        print('\n'
              'Arguments:\n'
              '    input image          (required)\n'
              '    key image            (optional, default None)\n'
              '    number of regions    '
              '(optional maximum - will be as high as practical otherwise)\n')

Gravação de imagem JPEG

Os arquivos .jpg são processados ​​muito rapidamente, mas com o custo de ficar muito quente. Isso deixa uma imagem queimada depois que o original é restaurado:

jpg queimar

Mas, falando sério, um formato com perdas resultará na alteração leve de algumas cores dos pixels, o que por si só torna a saída inválida. Quando uma imagem principal é usada e o embaralhamento de pixels é restrito às regiões, toda a distorção é mantida na região em que ocorreu e depois se espalha uniformemente nessa região quando a imagem é restaurada. A diferença na distorção média entre as regiões deixa uma diferença visível entre elas; portanto, as regiões usadas no processo de embaralhamento ainda são visíveis na imagem restaurada.

A conversão para .png (ou qualquer formato sem perdas) antes da codificação garante que a imagem não codificada seja idêntica à original sem gravação ou distorção:

png sem queimar

Pequenos detalhes

  • Um tamanho mínimo de 256 pixels é imposto às regiões. Se a imagem fosse dividida em regiões muito pequenas, a imagem original ainda estaria parcialmente visível após a codificação.
  • Se houver mais de uma região com um número ímpar de pixels, um pixel da segunda região será reatribuído para o primeiro e assim por diante. Isso significa que só pode haver uma região com um número ímpar de pixels e, portanto, apenas um pixel permanecerá sem codificação.
  • Há um terceiro argumento opcional que restringe o número de regiões. Ajustar para 2, por exemplo, fornecerá imagens embaralhadas em dois tons. Isso pode parecer melhor ou pior, dependendo das imagens envolvidas. Se um número for especificado aqui, a imagem poderá ser restaurada apenas usando o mesmo número novamente.
  • O número de cores distintas na imagem original também limita o número de regiões. Se a imagem original for de dois tons, independentemente da imagem principal ou do terceiro argumento, poderá haver no máximo 2 regiões.

2
+1 Aplausos! Eu pensei vagamente sobre isso, mas achei muito difícil de implementar.
edc65

1
Isto é brilhante. Eu tenho uma entrada enviada, mas eu gosto mais da sua por causa do recurso de imagem principal.
28814 Todd Lehman

Eu ficaria curioso para saber como essas duas imagens se parecem entre si: lardlad.com/assets/wallpaper/simpsons1920.jpg e blogs.nd.edu/oblation/files/2013/09/BreakingBad.jpg (redimensionado para 720x450 ou o que fizer sentido e, é claro, pré-convertido em PNG para evitar a queima do JPEG).
28814 Todd Lehman

2
@ToddLehman meu algoritmo é limitado pela necessidade de ser seu próprio inverso. Se você quiser ver algumas abordagens realmente interessantes para embaralhar uma imagem para se parecer com outra, você deve olhar para o American Gothic na paleta da Mona Lisa . Alguns desses programas fazem coisas incríveis com as imagens que você menciona.
Trichoplax

2
O recurso de imagem principal coloca essa cabeça e ombros acima do resto.
Jack Aidley

33

Aqui está uma transformação não aleatória para uma mudança

  1. Coloque todas as colunas pares à esquerda e todas as colunas ímpares à direita.
  2. repetir nxvezes
  3. faça o mesmo nos nytempos das linhas

A transformação é quase inversa, repetindo a transformação um total de size_xvezes (na direção x) retorna a imagem original. Eu não descobri a matemática exata, mas o uso de múltiplos inteiros int(log_2(size_x))produz o melhor embaralhamento com as menores imagens fantasmas

montanhas embaralhadas insira a descrição da imagem aqui

from numpy import *
from pylab import imread, imsave

def imshuffle(im, nx=0, ny=0):
    for i in range(nx):
        im = concatenate((im[:,0::2], im[:,1::2]), axis=1)
    for i in range(ny):
        im = concatenate((im[0::2,:], im[1::2,:]), axis=0)
    return im

im1 = imread('circles.png')
im2 = imread('mountain.jpg')

imsave('s_circles.png', imshuffle(im1, 7,7))
imsave('s_mountain.jpg', imshuffle(im2, 8,9))

É assim que parecem as primeiras 20 iterações das etapas (nx = ny, observe o efeito de diferentes resoluções) insira a descrição da imagem aqui


7
Esse é um algoritmo muito legal. E você deve receber um bônus por usar a foto de Lena Söderberg. :)
Todd Lehman

Sempre votou Lena

24

Mathematica

Isso é bem direto. Eu escolho 5 * nPixelspares de coordenadas aleatórios e troco esses dois pixels (o que obscurece completamente a imagem). Para decifrar, faço o mesmo ao contrário. Obviamente, preciso propagar o PRNG para obter os mesmos pares de coordenadas nos dois passos.

scramble[image_] := Module[
   {data, h, w, temp},
   data = ImageData@image;
   {h, w} = Most@Dimensions@data;
   SeedRandom[42];
   (
      temp = data[[#[[1]], #[[2]]]];
      data[[#[[1]], #[[2]]]] = data[[#2[[1]], #2[[2]]]];
      data[[#2[[1]], #2[[2]]]] = temp;
      ) & @@@
    Partition[
     Transpose@{RandomInteger[h - 1, 10*h*w] + 1, 
       RandomInteger[w - 1, 10*h*w] + 1}, 2];
   Image@data
   ];
unscramble[image_] := Module[
   {data, h, w, temp},
   data = ImageData@image;
   {h, w} = Most@Dimensions@data;
   SeedRandom[42];
   (
      temp = data[[#[[1]], #[[2]]]];
      data[[#[[1]], #[[2]]]] = data[[#2[[1]], #2[[2]]]];
      data[[#2[[1]], #2[[2]]]] = temp;
      ) & @@@
    Reverse@
     Partition[
      Transpose@{RandomInteger[h - 1, 10*h*w] + 1, 
        RandomInteger[w - 1, 10*h*w] + 1}, 2];
   Image@data
   ];

A única diferença entre as duas funções está Reverse@em unscramble. Ambas as funções usam um objeto de imagem real. Você pode usá-los da seguinte maneira:

in = Import["D:\\Development\\CodeGolf\\image-scrambler\\circles.png"]
scr = scramble[im]
out = unscramble[scr]

oute insão idênticos. Aqui está o que scrparece:

insira a descrição da imagem aqui insira a descrição da imagem aqui


4
Ótimo! O único problema é que é mais seguro criar o PRNG você mesmo, porque se depois de algum tempo o Mathematica pensar em mudar o algoritmo do PRNG, isso não decodificará as imagens codificadas antigas!
Somnium

1
Agradável. Você deve conseguir o mesmo resultado com Permute e FindPermutation.
24914

Eu não tenho certeza se entendi. Você pode inserir a permutação precisa que deseja como uma lista de ciclos.
24914

@DavidCarraher Hm, interessante. Não precisaria lembrar a permutação original para usar FindPermutation?
Martin Ender

Ou talvez algo que {c, a, b}[[{2, 3, 1}]]possa ser usado?
Somnium

22

C # (+ bônus para algoritmo simétrico)

Isso funciona ao encontrar um xtal que x^2 == 1 mod (number of pixels in image), e depois multiplicar o índice de cada pixel por x, a fim de encontrar seu novo local. Isso permite que você use exatamente o mesmo algoritmo para embaralhar e decifrar uma imagem.

using System.Drawing;
using System.IO;
using System.Numerics;

namespace RearrangePixels
{
    class Program
    {
        static void Main(string[] args)
        {
            foreach (var arg in args)
                ScrambleUnscramble(arg);
        }

        static void ScrambleUnscramble(string fileName)
        {
            using (var origImage = new Bitmap(fileName))
            using (var newImage = new Bitmap(origImage))
            {
                BigInteger totalPixels = origImage.Width * origImage.Height;
                BigInteger modSquare = GetSquareRootOf1(totalPixels);
                for (var x = 0; x < origImage.Width; x++)
                {
                    for (var y = 0; y < origImage.Height; y++)
                    {
                        var newNum = modSquare * GetPixelNumber(new Point(x, y), origImage.Size) % totalPixels;
                        var newPoint = GetPoint(newNum, origImage.Size);
                        newImage.SetPixel(newPoint.X, newPoint.Y, origImage.GetPixel(x, y));
                    }
                }
                newImage.Save("scrambled-" + Path.GetFileName(fileName));
            }
        }

        static BigInteger GetPixelNumber(Point point, Size totalSize)
        {
            return totalSize.Width * point.Y + point.X;
        }

        static Point GetPoint(BigInteger pixelNumber, Size totalSize)
        {
            return new Point((int)(pixelNumber % totalSize.Width), (int)(pixelNumber / totalSize.Width));
        }

        static BigInteger GetSquareRootOf1(BigInteger modulo)
        {
            for (var i = (BigInteger)2; i < modulo - 1; i++)
            {
                if ((i * i) % modulo == 1)
                    return i;
            }
            return modulo - 1;
        }
    }
}

primeira imagem de teste, codificada

segunda imagem de teste, embaralhada


1
Inteligente) Sempre haverá solução para essa equação de congruência?
Somnium

1
@ user2992539 Sempre haverá as soluções triviais 1(imagem original) e modulo-1(imagem invertida / invertida). A maioria dos números tem soluções não triviais, mas existem algumas exceções, ao que parece . (relacionado com o primeiro-factorização de modulo)
Tim S.

Pelo que entendi, soluções triviais levam a uma imagem semelhante à entrada um.
Somnium

Correto: 1gera a imagem original e -1gera, por exemplo, imgur.com/EiE6VW2
Tim S.

19

C #, auto-inverso, sem aleatoriedade

Se a imagem original tiver dimensões com potências de dois, cada linha e coluna será trocada pela linha e coluna que possui o padrão de bits invertido, por exemplo, para uma imagem de largura 256, a linha 0xB4 será trocada pela linha 0x2D. Imagens de outros tamanhos são divididas em retângulos com lados de potências de 2.

namespace CodeGolf
{
    class Program
    {
        static void Main(string[] args)
        {
            foreach (var arg in args)
                Scramble(arg);
        }

        static void Scramble(string fileName)
        {
            using (var origImage = new System.Drawing.Bitmap(fileName))
            using (var tmpImage = new System.Drawing.Bitmap(origImage))
            {
                {
                    int x = origImage.Width;
                    while (x > 0) {
                       int xbit = x & -x;
                        do {
                            x--;
                            var xalt = BitReverse(x, xbit);
                            for (int y = 0; y < origImage.Height; y++)
                                tmpImage.SetPixel(xalt, y, origImage.GetPixel(x, y));
                        } while ((x & (xbit - 1)) != 0);
                    }
                }
                {
                    int y = origImage.Height;
                    while (y > 0) {
                        int ybit = y & -y;
                        do {
                            y--;
                            var yalt = BitReverse(y, ybit);
                            for (int x = 0; x < origImage.Width; x++)
                                origImage.SetPixel(x, yalt, tmpImage.GetPixel(x, y));
                        } while ((y & (ybit - 1)) != 0);
                    } 
                }
                origImage.Save(System.IO.Path.GetFileNameWithoutExtension(fileName) + "-scrambled.png");
            }
        }

        static int BitReverse(int n, int bit)
        {
            if (bit < 4)
                return n;
            int r = n & ~(bit - 1);
            int tmp = 1;
            while (bit > 1) {
                bit >>= 1;
                if ((n & bit) != 0)
                    r |= tmp;
                tmp <<= 1;
            }
            return r;
        }
    }
}

Primeira imagem:

Primeira imagem mexida

Segunda imagem:

Segunda imagem mexida


2
Eu gosto da saída "xadrez" deste.
27414 Brian Brian

14

C #

Mesmo método para embaralhar e desembaralhar. Eu gostaria de receber sugestões para melhorar isso.

using System;
using System.Drawing;
using System.Linq;

public class Program
{
    public static Bitmap Scramble(Bitmap bmp)
    {
        var res = new Bitmap(bmp);
        var r = new Random(1);

        // Making lists of even and odd numbers and shuffling them
        // They contain numbers between 0 and picture.Width (or picture.Height)
        var rX = Enumerable.Range(0, bmp.Width / 2).Select(x => x * 2).OrderBy(x => r.Next()).ToList();
        var rrX = rX.Select(x => x + 1).OrderBy(x => r.Next()).ToList();
        var rY = Enumerable.Range(0, bmp.Height / 2).Select(x => x * 2).OrderBy(x => r.Next()).ToList();
        var rrY = rY.Select(x => x + 1).OrderBy(x => r.Next()).ToList();

        for (int y = 0; y < bmp.Height; y++)
        {
            for (int x = 0; x < rX.Count; x++)
            {
                // Swapping pixels in a row using lists rX and rrX
                res.SetPixel(rrX[x], y, bmp.GetPixel(rX[x], y));
                res.SetPixel(rX[x], y, bmp.GetPixel(rrX[x], y));
            }
        }
        for (int x = 0; x < bmp.Width; x++)
        {
            for (int y = 0; y < rY.Count; y++)
            {
                // Swapping pixels in a column using sets rY and rrY
                var px = res.GetPixel(x, rrY[y]);
                res.SetPixel(x, rrY[y], res.GetPixel(x, rY[y]));
                res.SetPixel(x, rY[y], px);
            }
        }

        return res;
    }
}

Resultados resulta em xadrez psicodélico Primeiro O segundo


Bom que isso tem algum padrão listrado)
Somnium

1
Você pode trocar as 2 imagens? Na questão, a imagem das montanhas é a primeira.
AL

1
Você poderia incluir uma breve explicação do algoritmo?
Trichoplax

14

Python 2 (auto-inverso, sem aleatoriedade, sensível ao contexto)

Isso não ganhará nenhum prêmio por "menos reconhecível", mas talvez possa ser considerado "interessante". :-)

Eu queria criar algo sensível ao contexto, onde a confusão dos pixels realmente depende da própria imagem.

A idéia é bem simples: classifique todos os pixels de acordo com algum valor arbitrário derivado da cor do pixel e troque as posições do primeiro pixel nessa lista com o último, o segundo com o segundo e o último, e assim por diante.

Infelizmente, nessa abordagem simples, há um problema com pixels da mesma cor; portanto, para torná-lo inverso, meu programa ficou um pouco mais complicado ...

from PIL import Image

img = Image.open('1.png', 'r')
pixels = img.load()
size_x, size_y = img.size

def f(colour):
    r,g,b = colour[:3]
    return (abs(r-128)+abs(g-128)+abs(b-128))//128

pixel_list = [(x,y,f(pixels[x,y])) for x in xrange(size_x) for y in xrange(size_y)]
pixel_list.sort(key=lambda x: x[2])
print "sorted"

colours = {}
for p in pixel_list:
    if p[2] in colours:
        colours[p[2]] += 1
    else:
        colours[p[2]] = 1
print "counted"

for n in set(colours.itervalues()):
    pixel_group = [p for p in pixel_list if colours[p[2]]==n]
    N = len(temp_list)
    for p1, p2 in zip(pixel_group[:N//2], pixel_group[-1:-N//2:-1]):
        pixels[p1[0],p1[1]], pixels[p2[0],p2[1]] = pixels[p2[0],p2[1]], pixels[p1[0],p1[1]]
print "swapped"

img.save('1scrambled.png')
print "saved"

Este é o resultado: (abs (r-128) + abs (g-128) + abs (b-128)) // 128 (abs (r-128) + abs (g-128) + abs (b-128)) // 128

Você pode obter resultados bastante diferentes alterando a função hash f:

  • r-g-b:

    rgb

  • r+g/2.**8+b/2.**16:

    r + g / 2. ** 8 + b / 2. ** 16

  • math.sin(r+g*2**8+b*2**16):

    math.sin (r + g * 2 ** 8 + b * 2 ** 16)

  • (r+g+b)//600:

    (r + g + b) // 600

  • 0:

    0 0


3
UAU! Este é ótimo !!! Bom trabalho!
Todd Lehman

1
Esse é o mais interessante até agora. Bom trabalho!
Bebe

12

Mathematica (+ bônus)

Isso recolhe os canais de cores e embaralha a imagem como uma longa lista de dados. O resultado é uma versão codificada ainda menos reconhecível porque não possui a mesma distribuição de cores do original (já que esses dados também foram codificados). Isso é mais óbvio na segunda imagem embaralhada, mas se você olhar atentamente, também verá o mesmo efeito na primeira. A função é inversa.

Houve um comentário de que isso pode não ser válido porque embaralha por canal. Eu acho que deveria ser, mas não é grande coisa. A única alteração necessária para embaralhar pixels inteiros (em vez de por canal) seria mudar Flatten @ xpara Flatten[x, 1]:)

ClearAll @ f;

f @ x_ := 
  With[
    {r = SeedRandom[Times @@ Dimensions @ x], f = Flatten @ x},
    ArrayReshape[
      Permute[f, Cycles @ Partition[RandomSample @ Range @ Length @ f, 2]],
      Dimensions @ x]]

Explicação

Define uma função fque utiliza uma matriz bidimensional x. A função usa o produto das dimensões da imagem como uma semente aleatória e depois nivela a matriz para uma lista unidimensional f(sombreada localmente). Em seguida, ele cria uma lista da forma em {1, 2, ... n}que no comprimento fpermuta aleatoriamente essa lista, a particiona em segmentos de 2 (portanto, por exemplo, {{1, 2}, {3, 4}, ...}(deixar o último número se as dimensões forem ímpares) e depois permuta ftrocando os valores em as posições indicadas em cada sub-lista recém-criada e, finalmente, remodela a lista permutada de volta às dimensões originais de x.FlattenO comando também recolhe os dados do canal em cada pixel. A função é inversa, porque os ciclos incluem apenas dois pixels cada.

Uso

img1=Import@"http://i.stack.imgur.com/2C2TY.jpg"//ImageData;
img2=Import@"http://i.stack.imgur.com/B5TbK.png"//ImageData;

f @ img1 // Image

insira a descrição da imagem aqui

f @ f @ img1 // Image

insira a descrição da imagem aqui

f @ img2 // Image

insira a descrição da imagem aqui

f @ f @ img2 // Image

insira a descrição da imagem aqui

Aqui está usando Flatten[x, 1].

g@x_ := With[{r = SeedRandom[Times @@ Dimensions @ x], f = Flatten[x, 1]}, 
  ArrayReshape[
   Permute[f, Cycles@Partition[RandomSample@Range@Length@f, 2]], 
   Dimensions@x]]

g@img2 // Image

insira a descrição da imagem aqui


1
Meu palpite é que isso não atende aos critérios, pois é trocado em uma escala menor que a de pixels.
Trichoplax

1
Não acho que seja uma resposta válida, mas também gosto muito. É uma reviravolta fascinante, tão +1 de qualquer maneira ...
Trichoplax

1
@githubphagocyte See update :)
mfvonh

Grande - Estendi a mão para o +1 novamente, mas é claro que não pode fazê-lo duas vezes ...
Trichoplax

1
@ githubphagocyte Ah, claro, eu esqueço que a sintaxe curta pode ser bizarra. Sim. f @ f @ img1 // Imageé (na sintaxe completa)Image[f[f[img1]]]
mfvonh

10

Matlab (+ bônus)

Basicamente, alterno a posição de dois pixels aleatoriamente e identifico cada pixel que foi alternado, para que não seja alternado novamente. O mesmo script pode ser usado novamente para a 'descriptografia' porque eu redefino o gerador de números aleatórios a cada vez. Isso é feito até que quase todos os pixels sejam alternados (é por isso que o tamanho da etapa é maior que 2)

Edição: Acabei de ver que Martin Büttner usou uma abordagem semelhante - eu não pretendia copiar a idéia - comecei a escrever meu código quando não havia respostas, então, desculpe-me por isso. Eu ainda acho que minha versão usa algumas idéias diferentes =) (E meu algoritmo é muito mais ineficiente se você olhar para o ponto em que as duas coordenadas aleatórias são escolhidas ^^)

Imagens

1 2

Código

img = imread('shuffle_image2.bmp');
s = size(img)
rand('seed',0)
map = zeros(s(1),s(2));
for i = 1:2.1:s(1)*s(2) %can use much time if stepsize is 2 since then every pixel has to be exchanged
    while true %find two unswitched pixels
        a = floor(rand(1,2) .* [s(1),s(2)] + [1,1]);
        b = floor(rand(1,2) .* [s(1),s(2)] + [1,1]);
        if map(a(1),a(2)) == 0 && map(b(1),b(2)) == 0
            break
        end
    end
    %switch
    map(a(1),a(2)) = 1;
    map(b(1),b(2)) = 1;
    t = img(a(1),a(2),:);
    img(a(1),a(2),:) = img(b(1),b(2),:);
    img(b(1),b(2),:) = t;
end
image(img)
imwrite(img,'output2.png')

Não entendo completamente, seu código aplicado pela segunda vez na imagem criptografada o descriptografará?
Somnium

2
Exatamente: a cada vez, exatamente dois pixels são trocados e não serão trocados novamente durante todo o processo. Como os números 'aleatórios' são às vezes exatamente iguais (devido à redefinição do gerador de números aleatórios), os pares de pixels serão trocados novamente. (O RNG depende sempre do número gerado anterior para gerar o próximo, eu espero que isso claro.)
flawr

1
Ah, essa foi realmente a minha ideia inicial, mas não me incomodei em garantir que cada pixel fosse trocado exatamente uma vez, porque eu precisava trabalhar. : D +1!
Martin Ender

3
@ user2992539 Confira o Octave, que é uma boa alternativa de código- fonte aberto para o matlab, e você pode executar 99% do código do matlab diretamente na oitava.
flawr

2
Eu acho que se você apertar os olhos com muita força nas fotos, ainda poderá ver alguma estrutura da entrada (o que ocorre devido a não mover todos os pixels). Eu acho que se você alterasse seu algoritmo de seleção para rodar em O (1) em vez de O (∞), você poderia corrigir isso. ;)
Martin Enders

10

Mathematica-Use uma permutação para embaralhar e sua inversa para desembaralhar.

Uma imagem jpg é uma matriz tridimensional de {r,g,b}cores de pixel. (As três dimensões estruturam o conjunto de pixels por linha, coluna e cor). Ele pode ser achatado em uma lista de {r,g,b}triplos, permutado de acordo com uma lista de ciclos "conhecida" e finalmente remontado em uma matriz das dimensões originais. O resultado é uma imagem embaralhada.

Desembaralhar pega a imagem embaralhada e a processa com o reverso da lista de ciclos. Emite, sim, a imagem original.

Portanto, uma única função (no presente caso scramble) serve para embaralhar e também desembaralhar pixels em uma imagem.

Uma imagem é inserida junto com um número inicial (para garantir que o gerador de números aleatórios esteja no mesmo estado para embaralhar e desembaralhar). Quando o parâmetro, reverse, é False, a função embaralha. Quando é True, a função será decodificada.


passeio

Os pixels são achatados e uma lista aleatória de ciclos é gerada. O permute usa ciclos para alternar as posições dos pixels na lista nivelada.

decifrar

A mesma função scrambleé usada para desembaralhar. No entanto, a ordem da lista de ciclos é revertida.

scramble[img_,s_,reverse_:False,imgSize_:300]:=
  Module[{i=ImageData[img],input,r},input=Flatten[i,1];SeedRandom[s];
  r=RandomSample@Range[Length[input]];Image[ArrayReshape[Permute[input,
  Cycles[{Evaluate@If[reverse,Reverse@r,r]}]],Dimensions[i]],ImageSize->imgSize]]

Exemplos

A mesma semente (37) é usada para embaralhar e desembaralhar.

Isso produz a imagem embaralhada da montanha. A figura abaixo mostra que a variável scrambledMount pode ser substituída pela imagem real da cena da montanha.

scrambledMount=scramble[mountain, 37, True]

mount1


Agora nós executamos o inverso; scrambledMount é inserido e a imagem original é recuperada.

 scramble[scrambledMount, 37, True]

mount2


A mesma coisa para os círculos:

circles1


 scramble[scrambledCircles, 37, True]

circles2


Não vejo como uma imagem pode ser uma matriz tridimensional.
Edc65

1
@ edc65, Linhas x Colunas x Cores. A imagem da montanha é de 422 linhas por 800 colunas por 3 cores. Se a matriz for achatada, ela produzirá 1012800 partes de dados como uma matriz unidimensional, ou seja, como uma lista.
25414

@ edc65 Devo acrescentar que o Permute foi usado para reorganizar as cores como triplos rgb. Não apliquei mais isso porque não estava interessada em fazer alterações na lista de cores de qualquer pixel. Se você considerar as informações rgb, {r, g, b} como um elemento, então estamos falando de uma matriz 2D. Visto dessa maneira, faz todo o sentido levantar a questão (como uma imagem pode ser uma matriz tridimensional?) Que você levantou. De fato, pode ser mais normal considerar uma imagem como uma matriz 2D, desconsiderando o fato de que os elementos rgb adicionam outra dimensão.
26414

10

Pitão

Eu gosto desse quebra-cabeça, ele parecia interessante e eu vim com uma função envolvente e de movimento para aplicar na imagem.

Wraped

Eu li a imagem como um texto (da esquerda para a direita, para cima e para baixo) e a escrevi como uma concha de caracol.

Esta função é cíclica: existe um em N, f ^ (n) (x) = x por exemplo, para uma figura de 4 * 2, f (f (f (x))) = x

Movimento

Pego um número aleatório e movo cada coluna e alinhar a partir dela

Código

# Opening and creating pictures
img = Image.open("/home/faquarl/Bureau/unnamed.png")
PM1 = img.load()
(w,h) = img.size
img2 = Image.new( 'RGBA', (w,h), "black") 
PM2 = img2.load()
img3 = Image.new( 'RGBA', (w,h), "black") 
PM3 = img3.load()

# Rotation
k = 0
_i=w-1
_j=h-1
_currentColMin = 0
_currentColMax = w-1
_currentLigMin = 0
_currentLigMax = h-1
_etat = 0
for i in range(w):
    for j in range(h):
        PM2[_i,_j]=PM1[i,j]
        if _etat==0:
            if _currentColMax == _currentColMin:
                _j -= 1
                _etat = 2
            else:
                _etat = 1
                _i -= 1
        elif _etat==1:
            _i -= 1
            if _j == _currentLigMax and _i == _currentColMin:
                _etat = 2
        elif _etat==2:
            _j -= 1
            _currentLigMax -= 1
            if _j == _currentLigMin and _i == _currentColMin:
                _etat = 5
            else:
                _etat = 3
        elif _etat==3:
            _j -= 1
            if _j == _currentLigMin and _i == _currentColMin:
                _etat = 4
        elif _etat==4:
            _i += 1
            _currentColMin += 1
            if _j == _currentLigMin and _i == _currentColMax:
                _etat = 7
            else:
                _etat = 5
        elif _etat==5:
            _i += 1
            if _j == _currentLigMin and _i == _currentColMax:
                _etat = 6
        elif _etat==6:
            _j += 1
            _currentLigMin += 1
            if _j == _currentLigMax and _i == _currentColMax:
                _etat = 1
            else:
                _etat = 7
        elif _etat==7:
            _j += 1
            if _j == _currentLigMax and _i == _currentColMax:
                _etat = 8
        elif _etat==8:
            _i -= 1
            _currentColMax -= 1
            if _j == _currentLigMax and _i == _currentColMin:
                _etat = 3
            else:
                _etat = 1
        k += 1
        if k == w * h:
            i = w
            j = h
# Movement
if w>h:z=w
else:z=h
rand.seed(z)
a=rand.randint(0,h)
for i in range(w):
  for j in range(h):
  if i%2==0:
    PM3[(i+a)%w,(j+a)%h]=PM2[i,j]
  else:
    PM3[(i-a)%w,(j-a)%h]=PM2[i,j]
# Rotate Again

As fotos

Primeira rotação: insira a descrição da imagem aqui

então permutação: insira a descrição da imagem aqui

E com a última rotaion: insira a descrição da imagem aqui

Quanto ao outro exemplo: insira a descrição da imagem aqui


2
Como você restaura a imagem original?
Trichoplax

Se for apenas rotação, posso fazer isso por um certo período de tempo (depende do tamanho). No entanto, se eu tivesse as permutações, não tenho certeza se é cíclico, então só tenho uma segunda função que só muda é o que PM2 [_i, _j] = PM1 [i, j] se tornou PM2 [i, j] = PM1 [ _i, _j] e PM3 [(i + a)% w, (j + a)% h] = PM2 [i, j] tornou-se PM3 [(ia)% w, (ja)% h] = PM2 [i, j] Eu estou procurando uma maneira de fazê-lo sem chang estas duas linhas
Faquarl

8

VB.NET (+ bônus)

Isso usa a idéia de flawr, graças a ele, no entanto, usa diferentes algoritmos de troca e verificação. O programa codifica e decodifica da mesma maneira.

Imports System

Module Module1

    Sub swap(ByVal b As Drawing.Bitmap, ByVal i As Integer, ByVal j As Integer)
        Dim c1 As Drawing.Color = b.GetPixel(i Mod b.Width, i \ b.Width)
        Dim c2 As Drawing.Color = b.GetPixel(j Mod b.Width, j \ b.Width)
        b.SetPixel(i Mod b.Width, i \ b.Width, c2)
        b.SetPixel(j Mod b.Width, j \ b.Width, c1)
    End Sub

    Sub Main(ByVal args() As String)
        For Each a In args
            Dim f As New IO.FileStream(a, IO.FileMode.Open)
            Dim b As New Drawing.Bitmap(f)
            f.Close()
            Dim sz As Integer = b.Width * b.Height - 1
            Dim w(sz) As Boolean
            Dim r As New Random(666)
            Dim u As Integer, j As Integer = 0
            Do While j < sz
                Do
                    u = r.Next(0, sz)
                Loop While w(u)
                ' swap
                swap(b, j, u)
                w(j) = True
                w(u) = True
                Do
                    j += 1
                Loop While j < sz AndAlso w(j)
            Loop
            b.Save(IO.Path.ChangeExtension(a, "png"), Drawing.Imaging.ImageFormat.Png)
            Console.WriteLine("Done!")
        Next
    End Sub

End Module

Imagens de saída:


8

Depois de ser lembrado que isso está prestes a trocar pixels e não alterá-los, aqui está minha solução para isso:

Mexidos: insira a descrição da imagem aqui

Restaurado: insira a descrição da imagem aqui

Isso é feito aleatoriamente a ordem dos pixels, mas para poder restaurá-lo, a randomização é corrigida. Isso é feito usando um pseudo-aleatório com uma semente fixa e gera uma lista de índices que descrevem quais pixels trocar. Como a troca será a mesma, a mesma lista restaurará a imagem original.

public class ImageScramble {

  public static void main(String[] args) throws IOException {
    if (args.length < 2) {
      System.err.println("Usage: ImageScramble <fileInput> <fileOutput>");
    } else {
      // load image
      final String extension = args[0].substring(args[0].lastIndexOf('.') + 1);
      final BufferedImage image = ImageIO.read(new File(args[0]));
      final int[] pixels = image.getRGB(0, 0, image.getWidth(), image.getHeight(), null, 0, image.getWidth());

      // create randomized swap list
      final ArrayList<Integer> indexes = IntStream.iterate(0, i -> i + 1).limit(pixels.length).collect(ArrayList::new, ArrayList::add, ArrayList::addAll);
      Collections.shuffle(indexes, new Random(1337));

      // swap all pixels at index n with pixel at index n+1
      int tmp;
      for (int i = 0; i < indexes.size(); i += 2) {
        tmp = pixels[indexes.get(i)];
        pixels[indexes.get(i)] = pixels[indexes.get(i + 1)];
        pixels[indexes.get(i + 1)] = tmp;
      }

      // write image to disk
      final BufferedImage imageScrambled = new BufferedImage(image.getWidth(), image.getHeight(), image.getType());
      imageScrambled.setRGB(0, 0, imageScrambled.getWidth(), imageScrambled.getHeight(), pixels, 0, imageScrambled.getWidth());
      ImageIO.write(imageScrambled, extension, new File(args[1]));
    }
  }
}

Observe que o uso desse algoritmo em um formato de compactação com perdas não produzirá o mesmo resultado, pois o formato da imagem alterará os dados. Isso deve funcionar bem com qualquer codec sem perda, como PNG.


8

Mathematica

Definimos uma função auxiliar he a função de embaralhamento scramblecomo:

h[l_List, n_Integer, k_Integer: 1] := 
  With[{ m = Partition[l, n, n, 1, 0] }, 
    Flatten[
      Riffle[
        RotateLeft[ m[[ ;; , {1} ]] , k ],
        m[[ ;; , 2;; ]]
      ], 1
    ] [[ ;; Length[l] ]]
  ];

scramble[img_Image, k_Integer] :=
  Module[{ list , cNum = 5 },
    Which[
      k > 0,    list = Prime@Range[cNum],
      k < 0,    list = Reverse@Prime@Range[cNum],
      True,     list = {}
    ];
    Image[
      Transpose[
        Fold[ h[ #1, #2, k ] &, #, list ] & /@
        Transpose[
          Fold[ h[#1, #2, k] &, #, list ] & /@ ImageData[img]
        ]
      ]
    ]
  ];

Depois de carregar uma imagem, você pode chamar scramble[img, k]onde ké qualquer inteiro, para embaralhar a imagem. Ligar novamente com -kdecodificará. (Se kestiver 0, então nenhuma alteração será feita.) Normalmente, kdeve ser escolhido algo como 100: o que fornece uma imagem bastante embaralhada:

Exemplo de saída 1

Exemplo de saída 2


7

Matlab: embaralhamento de linhas e colunas com base em invariâncias de soma de linhas / colunas

Parecia um quebra-cabeça divertido, então eu pensei sobre isso e criei a seguinte função. Ele se baseia na invariância das somas de valor de pixel de linha e coluna durante o deslocamento circular: muda cada linha e depois cada coluna pela soma total dos valores de pixel da linha / coluna (assumindo uint8 para o número inteiro na variável shift ) Isso pode ser revertido deslocando cada coluna e depois a linha pelo valor da soma na direção oposta.

Não é tão bonito quanto os outros, mas gosto que não seja aleatório e totalmente especificado pela imagem - sem escolha de parâmetros.

Eu o projetei originalmente para mudar cada canal de cor separadamente, mas notei a especificação para mover apenas pixels completos.

function pic_scramble(input_filename)
i1=imread(input_filename);
figure;
subplot(1,3,1);imagesc(i1);title('Original','fontsize',20);

i2=i1;
for v=1:size(i1,1)
    i2(v,:,:)=circshift(i2(v,:,:),sum(sum(i2(v,:,:))),2);
end
for w=1:size(i2,2)
    i2(:,w,:)=circshift(i2(:,w,:),sum(sum(i2(:,w,:))),1);
end
subplot(1,3,2);imagesc(i2);title('Scrambled','fontsize',20);

i3=i2;
for w=1:size(i3,2)
    i3(:,w,:)=circshift(i3(:,w,:),-1*sum(sum(i3(:,w,:))),1);
end
for v=1:size(i3,1)
    i3(v,:,:)=circshift(i3(v,:,:),-1*sum(sum(i3(v,:,:))),2);
end
subplot(1,3,3);imagesc(i3);title('Recovered','fontsize',20);

Primeira imagem de teste Imagem de teste Secont


6

Java

Este programa alterna aleatoriamente pixels (cria o mapeamento pixel a pixel), mas, em vez da função aleatória, usa Math.sin () (número inteiro x). É totalmente reversível. Com imagens de teste, ele cria alguns padrões.

Parâmetros: número inteiro (número de passes, número negativo para reverter, 0 não faz nada), imagem de entrada e imagem de saída (pode ser a mesma). O arquivo de saída deve estar no formato que usa compactação sem perdas.

1 passagem: insira a descrição da imagem aqui insira a descrição da imagem aqui

100 passes (leva alguns minutos para fazer): insira a descrição da imagem aqui insira a descrição da imagem aqui

Código:

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;

public class Test{

public static void main(String... args) {
    String in = "image.png";
    String out = in;
    int passes = 0;
    if (args.length < 1) {
        System.out.println("no paramem encryptimg, 1 pass, reading and saving image.png");
        System.out.println("Usage: pass a number. Negative - n passes of decryption, positive - n passes of encryption, 0 - do nothing");
    } else {
        passes = Integer.parseInt(args[0]);
        if (args.length > 1) {
            in = args[1];
        }
        if(args.length > 2){
            out = args[2];
        }
    }
    boolean encrypt = passes > 0;
    passes = Math.abs(passes);
    for (int a = 0; a < passes; a++) {
        BufferedImage img = null;
        try {
            img = ImageIO.read(new File(a == 0 ? in : out));
        } catch (IOException e) {
            e.printStackTrace();
            return;
        }
        int pixels[][] = new int[img.getWidth()][img.getHeight()];
        int[][] newPixels = new int[img.getWidth()][img.getHeight()];
        for (int x = 0; x < pixels.length; x++) {
            for (int y = 0; y < pixels[x].length; y++) {
                pixels[x][y] = img.getRGB(x, y);
            }
        }
        int amount = img.getWidth() * img.getHeight();
        int[] list = new int[amount];
        for (int i = 0; i < amount; i++) {
            list[i] = i;
        }
        int[] mapping = new int[amount];
        for (int i = amount - 1; i >= 0; i--) {
            int num = (Math.abs((int) (Math.sin(i) * amount))) % (i + 1);
            mapping[i] = list[num];
            list[num] = list[i];
        }
        for (int xz = 0; xz < amount; xz++) {
            int x = xz % img.getWidth();
            int z = xz / img.getWidth();
            int xzMap = mapping[xz];
            int newX = xzMap % img.getWidth();
            int newZ = xzMap / img.getWidth();
            if (encrypt) {
                newPixels[x][z] = pixels[newX][newZ];
            } else {
                newPixels[newX][newZ] = pixels[x][z];
            }
        }
        BufferedImage newImg = new BufferedImage(img.getWidth(), img.getHeight(), BufferedImage.TYPE_INT_RGB);
        for (int x = 0; x < pixels.length; x++) {
            for (int y = 0; y < pixels[x].length; y++) {
                newImg.setRGB(x, y, newPixels[x][y]);
            }
        }

        try {
            String[] s = out.split("\\.");
            ImageIO.write(newImg, s[s.length - 1],
                    new File(out));
        } catch (IOException e) {
            e.printStackTrace();
            return;
        }
    }
}
}

6

Python 2.7 com PIL

Um pouco tarde para a festa, mas achei que seria divertido converter as imagens em mantas (e de volta, é claro). Primeiro, deslocamos as colunas para cima ou para baixo em 4 vezes o número das colunas (colunas pares para baixo, colunas ímpares para cima). Em seguida, deslocamos as linhas para a esquerda ou direita por 4 vezes o número da linha (colunas pares à esquerda, colunas ímpares à direita).

O resultado é bastante tartan.

Para reverter, apenas fazemos isso na ordem oposta e mudamos na quantidade oposta.

Código

from PIL import Image

def slideColumn (pix, tpix, x, offset, height):
  for y in range(height):
    tpix[x,(offset+y)%height] = pix[x,y]

def slideRow (pix, tpix, y, offset, width):
  for x in range(width):
    tpix[(offset+x)%width,y] = pix[x,y]

def copyPixels (source, destination, width, height):
  for x in range(width):
    for y in range(height):
      destination[x,y]=source[x,y]

def shuffleHorizontal (img, tmpimg, factor, encoding):
  xsize,ysize = img.size
  pix = img.load()
  tpix = tmpimg.load()
  for y in range(ysize):
    offset = y*factor
    if y%2==0:
      offset = xsize-offset
    offset = (xsize + offset) % xsize
    if encoding:
      slideRow(pix,tpix,y,offset,xsize)
    else:
      slideRow(pix,tpix,y,-offset,xsize)
  copyPixels(tpix,pix,xsize,ysize)

def shuffleVertical (img, tmpimg, factor, encoding):
  xsize,ysize = img.size
  pix = img.load()
  tpix = tmpimg.load()
  for x in range(xsize):
    offset = x*factor
    if x%2==0:
      offset = ysize-offset
    offset = (ysize + offset) % ysize
    if encoding:
      slideColumn(pix,tpix,x,offset,ysize)
    else:
      slideColumn(pix,tpix,x,-offset,ysize)
  copyPixels(tpix,pix,xsize,ysize)


def plaidify (img):
  tmpimg = Image.new("RGB",img.size)
  shuffleVertical(img,tmpimg,4,True)
  shuffleHorizontal(img,tmpimg,4,True)

def deplaidify (img):
  tmpimg = Image.new("RGB",img.size)
  shuffleHorizontal(img,tmpimg,4,False)
  shuffleVertical(img,tmpimg,4,False)

Resultados

A manta da imagem 1:

o 1.jpg plaidified

A imagem da forma xadrez 2:

o plaidified 2.png


2
Muito agradável! É possível obter as diagonais em um ângulo de 45 °?
Todd Lehman

2
É possível alterar as linhas de deslocamento para: offset = x*xsize/ysize e offset = y*ysize/xsize Mas, infelizmente, também não oculta a imagem.
Jrrl

5

Python (+ bônus) - permutação dos pixels

Nesse método, cada pixel será colocado em outra posição, com a restrição de que o outro pixel seja colocado na primeira posição. Matematicamente, é uma permutação com duração de ciclo 2. Como tal, o método é o próprio inverso.

Em retrospecto, é muito semelhante ao mfvonh, mas essa submissão é em Python e eu tive que construir essa permutação por conta própria.

def scramble(I):
    result = np.zeros_like(I)
    size = I.shape[0:2]
    nb_pixels = size[0]*size[1]
    #Build permutation
    np.random.seed(0)
    random_indices = np.random.permutation( range(nb_pixels) )
    random_indices1 = random_indices[0:int(nb_pixels/2)]
    random_indices2 = random_indices[-1:-1-int(nb_pixels/2):-1]
    for c in range(3):
        Ic = I[:,:,c].flatten()
        Ic[ random_indices2 ] = Ic[random_indices1]
        Ic[ random_indices1 ] = I[:,:,c].flatten()[random_indices2]
        result[:,:,c] = Ic.reshape(size)
    return result

Primeira imagem de teste: Primeira imagem de teste Segunda imagem de teste: Segunda imagem de teste


5

Python 2.7 + PIL, inspiração nos quebra-cabeças deslizantes

Só tive outra ideia. Basicamente, esse método divide uma imagem em blocos de tamanhos iguais e, em seguida, embaralha sua ordem. Como o novo pedido é baseado em uma semente fixa, é possível reverter completamente o processo usando a mesma semente. Além disso, com o parâmetro adicional chamado granularidade, é possível obter resultados diferentes e irreconhecíveis.

Resultados:

Original

original

Granularidade 16

16

Granularidade 13

13

Granularidade 10

10

Granularidade 3

3

Granularidade 2

2

Granularidade 1

insira a descrição da imagem aqui

Original

original

Granularidade 16

16

Granularidade 13

13

Granularidade 10

10

Granularidade 3

3

Granularidade 2

2

Granularidade 1

1

Código:

#!/usr/bin/env python
# -*- coding: utf-8 -*-

from PIL import Image
import random

def scramble_blocks(im,granularity,password,nshuffle):
    set_seed(password)
    width=im.size[0]
    height=im.size[1]

    block_width=find_block_dim(granularity,width)       #find the possible block dimensions
    block_height=find_block_dim(granularity,height)

    grid_width_dim=width/block_width                #dimension of the grid
    grid_height_dim=height/block_height

    nblocks=grid_width_dim*grid_height_dim          #number of blocks

    print "nblocks: ",nblocks," block width: ",block_width," block height: ",block_height
    print "image width: ",width," image height: ",height
    print "getting all the blocks ..."
    blocks=[]
    for n in xrange(nblocks): #get all the image blocks
        blocks+=[get_block(im,n,block_width,block_height)]

    print "shuffling ..."
    #shuffle the order of the blocks
    new_order=range(nblocks)
    for n in xrange(nshuffle):
        random.shuffle(new_order)

    print "building final image ..."
    new_image=im.copy()
    for n in xrange(nblocks):
        #define the target box where to paste the new block
        i=(n%grid_width_dim)*block_width                #i,j -> upper left point of the target image
        j=(n/grid_width_dim)*block_height
        box = (i,j,i+block_width,j+block_height)    

        #paste it   
        new_image.paste(blocks[new_order[n]],box)

    return new_image



#find the dimension(height or width) according to the desired granularity (a lower granularity small blocks)
def find_block_dim(granularity,dim):
    assert(granularity>0)
    candidate=0
    block_dim=1
    counter=0
    while counter!=granularity:         #while we dont achive the desired granularity
        candidate+=1
        while((dim%candidate)!=0):      
            candidate+=1
            if candidate>dim:
                counter=granularity-1
                break

        if candidate<=dim:
            block_dim=candidate         #save the current feasible lenght

        counter+=1

    assert(dim%block_dim==0 and block_dim<=dim)
    return block_dim

def unscramble_blocks(im,granularity,password,nshuffle):
    set_seed(password)
    width=im.size[0]
    height=im.size[1]

    block_width=find_block_dim(granularity,width)       #find the possible block dimensions
    block_height=find_block_dim(granularity,height)

    grid_width_dim=width/block_width                #dimension of the grid
    grid_height_dim=height/block_height

    nblocks=grid_width_dim*grid_height_dim          #number of blocks

    print "nblocks: ",nblocks," block width: ",block_width," block height: ",block_height
    print "getting all the blocks ..."
    blocks=[]
    for n in xrange(nblocks): #get all the image blocks
        blocks+=[get_block(im,n,block_width,block_height)]

    print "shuffling ..."
    #shuffle the order of the blocks
    new_order=range(nblocks)
    for n in xrange(nshuffle):
        random.shuffle(new_order)

    print "building final image ..."
    new_image=im.copy()
    for n in xrange(nblocks):
        #define the target box where to paste the new block
        i=(new_order[n]%grid_width_dim)*block_width             #i,j -> upper left point of the target image
        j=(new_order[n]/grid_width_dim)*block_height
        box = (i,j,i+block_width,j+block_height)    

        #paste it   
        new_image.paste(blocks[n],box)

    return new_image

#get a block of the image
def get_block(im,n,block_width,block_height):

    width=im.size[0]

    grid_width_dim=width/block_width                        #dimension of the grid

    i=(n%grid_width_dim)*block_width                        #i,j -> upper left point of the target block
    j=(n/grid_width_dim)*block_height

    box = (i,j,i+block_width,j+block_height)
    block_im = im.crop(box)
    return block_im

#set random seed based on the given password
def set_seed(password):
    passValue=0
    for ch in password:                 
        passValue=passValue+ord(ch)
    random.seed(passValue)


if __name__ == '__main__':

    filename="0RT8s.jpg"
    # filename="B5TbK.png"
    password="yOs0ZaKpiS"
    nshuffle=1
    granularity=1

    im=Image.open(filename)

    new_image=scramble_blocks(im,granularity,password,nshuffle)
    new_image.show()
    new_image.save(filename.split(".")[0]+"_puzzled.png")

    new_image=unscramble_blocks(new_image,granularity,password,nshuffle)
    new_image.save(filename.split(".")[0]+"_unpuzzled.png")
    new_image.show()

5

47

94 linhas. 47 para codificação, 47 para decodificação.

require 'chunky_png'
require_relative 'codegolf-35005_ref.rb'


REF = {:png => ref, :w => 1280, :h => 720}
REF[:pix] = REF[:png].to_rgb_stream.unpack('C*').each_slice(3).to_a
SEVENTH_PRIME = 4*7 - 4-7 - (4&7)
FORTY_SEVEN   = 4*7 + 4+7 + (4&7) + (4^7) + 7/4
THRESHOLD     = FORTY_SEVEN * SEVENTH_PRIME


class RNG
    @@m = 2**32
    @@r = 0.5*(Math.sqrt(5.0) - 1.0)
    def initialize(n=0)
        @cur = FORTY_SEVEN + n
    end
    def hash(seed)
        (@@m*((seed*@@r)%1)).floor
    end
    def _next(max)
        hash(@cur+=1) % max
    end
    def _prev(max)
        hash(@cur-=1) % max
    end        
    def advance(n)
        @cur += n
    end
    def state
        @cur
    end
    alias_method :rand, :_next
end


def load_png(file, resample_w = nil, resample_h = nil)
    png  = ChunkyPNG::Image.from_file(file)
    w    = resample_w || png.width
    h    = resample_h || png.height
    png.resample_nearest_neighbor!(w,h) if resample_w || resample_h
    pix  = png.to_rgb_stream.unpack('C*').each_slice(3).to_a
    return {:png => png, :w => w, :h => h, :pix => pix}
end


def make_png(img)
    rgb_stream = img[:pix].flatten.pack('C*')
    img[:png] = ChunkyPNG::Canvas.from_rgb_stream(img[:w],img[:h],rgb_stream)
    return img
end


def difference(pix_a,pix_b)
    (pix_a[0]+pix_a[1]+pix_a[2]-pix_b[0]-pix_b[1]-pix_b[2]).abs
end


def code(img, img_ref, mode)
    img_in  = load_png(img)
    pix_in  = img_in[:pix]
    pix_ref = img_ref[:pix]
    s = img_in[:w] * img_in[:h] 
    rng = RNG.new(mode==:enc ? 0 : FORTY_SEVEN*s+1)
    rand = mode == :enc ? rng.method(:_next) : rng.method(:_prev)
    s.times do
        FORTY_SEVEN.times do
            j = rand.call(s)
            i = rng.state % s
            diff_val = difference(pix_ref[i],pix_ref[j])
            if diff_val > THRESHOLD
               pix_in[i], pix_in[j] = pix_in[j], pix_in[i]
            end
        end
    end
    make_png(img_in)
end


case ARGV.shift
when 'enc'
    org, cod = ARGV
    encoded_image = code(org,REF,:enc)
    encoded_image[:png].save(cod)
when 'dec'
    org, cod = ARGV
    decoded_image = code(cod,REF,:dec)
    decoded_image[:png].save(org)
else
    puts '<original> <coded>'
    puts 'specify either <enc> or <dec>'
    puts "ruby #{$0} enc codegolf-35005_inp.png codegolf-35005_enc.png"
end

codegolf-35005_ref.rb

insira a descrição da imagem aqui insira a descrição da imagem aqui

(convertido em jpg)

insira a descrição da imagem aqui

(original reduzido)

insira a descrição da imagem aqui


3
Alguns padrões originais são visíveis através dessas linhas. No entanto, lembra-se quando você desenha com um dedo na janela embaçada).
Somnium

2
... + 184426 bytes para codegolf-35005_ref.rb?
Edc65

5

Matlab com uma pitada de teoria dos grupos (+ bônus)

Nesta abordagem, assumimos que temos um número par de pixels totais. (Caso contrário, ignoramos apenas um pixel). Precisamos escolher metade dos pixels para trocar com a outra metade. Para isso, indexamos todos os pixels de 0até 2N-1e consideramos esses índices como um grupo cíclico.

Entre os primos, procuramos um número pque não seja muito pequeno nem muito grande, e que seja coprime para 2Na ordem do nosso grupo. Isso significa g gera nosso grupo ou{k*g mod 2N | k=0,1,...,2N-1} = {0,1,...,2N-1} .

Portanto, escolhemos os primeiros Nmúltiplos de gum conjunto e todos os indeces restantes como o outro conjunto e trocamos o conjunto de pixels correspondente.

Se pfor escolhido da maneira correta, o primeiro conjunto será distribuído uniformemente por toda a imagem.

Os dois casos de teste:

Um pouco fora do tópico, mas interessante:

Durante o teste, notei que se você o salvar em um jpg (com perda compactado) (em vez de um png sem perdas) e aplicar a transformação para frente e para trás, você verá rapidamente artefatos da compressão, isso mostra os resultados de dois rearranjos consecutivos :

Como você pode ver, a compactação jpg faz com que o resultado pareça quase preto e branco!

clc;clear;
inputname = 'codegolf_rearrange_pixels2.png';
inputname = 'codegolf_rearrange_pixels2_swapped.png';
outputname = 'codegolf_rearrange_pixels2_swapped.png';

%read image
src = imread(inputname);

%separate into channels
red = src(:,:,1);
green = src(:,:,2);
blue = src(:,:,3);

Ntotal = numel(red(:));  %number of pixels
Nswap = floor(Ntotal/2); %how many pairs we can swap

%find big enough generator
factors = unique(factor(Ntotal));
possible_gen = primes(max(size(red)));
eliminated = setdiff(possible_gen,factors);
if mod(numel(eliminated),2)==0 %make length odd for median
    eliminated = [1,eliminated];
end
generator = median(eliminated);

%set up the swapping vectors
swapindices1 = 1+mod((1:Nswap)*generator, Ntotal);
swapindices2 = setdiff(1:Ntotal,swapindices1);
swapindices2 = swapindices2(1:numel(swapindices1)); %make sure both have the same length

%swap the pixels
red([swapindices1,swapindices2]) = red([swapindices2,swapindices1]);
green([swapindices1,swapindices2]) = green([swapindices2,swapindices1]);
blue([swapindices1,swapindices2]) = blue([swapindices2,swapindices1]);

%write and display
output = cat(3,red,green,blue);
imwrite(output,outputname);
subplot(2,1,1);
imshow(src)
subplot(2,1,2);
imshow(output);

4

JavaScript (+ bônus) - repetidor de troca de pixels divididos

A função pega um elemento de imagem e

  1. Divide os pixels por 8.
  2. Faz uma troca reversível de grupos de pixels.
  3. Recorre a troca se o grupo de pixels> = 8.
function E(el){
    var V=document.createElement('canvas')
    var W=V.width=el.width,H=V.height=el.height,C=V.getContext('2d')
    C.drawImage(el,0,0)
    var id=C.getImageData(0,0,W,H),D=id.data,L=D.length,i=L/4,A=[]
    for(;--i;)A[i]=i
    function S(A){
        var L=A.length,x=L>>3,y,t,i=0,s=[]
        if(L<8)return A
        for(;i<L;i+=x)s[i/x]=S(A.slice(i,i+x))
        for(i=4;--i;)y=[6,4,7,5,1,3,0,2][i],t=s[i],s[i]=s[y],s[y]=t
        s=[].concat.apply([],s)
        return s
    }
    var N=C.createImageData(W,H),d=N.data,A=S(A)
    for(var i=0;i<L;i++)d[i]=D[(A[i>>2]*4)+(i%4)]
    C.putImageData(N,0,0)
    el.src=C.canvas.toDataURL()
}

Montanhas Círculos


4

Python 2.7 + PIL, Scrambler de coluna / linha

Este método simplesmente embaralha as linhas e colunas da imagem. É possível embaralhar apenas uma das dimensões ou ambas. Além disso, a ordem da nova linha / coluna codificada é baseada em uma senha. Além disso, outra possibilidade é embaralhar toda a matriz de imagens sem considerar as dimensões.

Resultados:

Embaralhando a imagem inteira:

insira a descrição da imagem aqui

insira a descrição da imagem aqui

Misturando as colunas:

insira a descrição da imagem aqui

insira a descrição da imagem aqui

Misturando as linhas:

insira a descrição da imagem aqui

insira a descrição da imagem aqui

Misturando colunas e linhas:

insira a descrição da imagem aqui

insira a descrição da imagem aqui

Também tentei aplicar várias execuções à imagem, mas os resultados finais não diferiram muito, apenas a dificuldade de descriptografá-la.

Código:

from PIL import Image
import random,copy

def scramble(im,columns,rows):
    pixels =list(im.getdata())

    newOrder=range(columns*rows)        
    random.shuffle(newOrder)            #shuffle

    newpixels=copy.deepcopy(pixels)
    for i in xrange(len(pixels)):
        newpixels[i]=pixels[newOrder[i]]

    im.putdata(newpixels)

def unscramble(im,columns,rows):
    pixels =list(im.getdata())

    newOrder=range(columns*rows)        
    random.shuffle(newOrder)            #unshuffle

    newpixels=copy.deepcopy(pixels)
    for i in xrange(len(pixels)):
        newpixels[newOrder[i]]=pixels[i]

    im.putdata(newpixels)

def scramble_columns(im,columns,rows):
    pixels =list(im.getdata())

    newOrder=range(columns)     
    random.shuffle(newOrder)            #shuffle

    newpixels=[]
    for i in xrange(rows):
        for j in xrange(columns):
            newpixels+=[pixels[i*columns+newOrder[j]]]

    im.putdata(newpixels)

def unscramble_columns(im,columns,rows):
    pixels =list(im.getdata())

    newOrder=range(columns)     
    random.shuffle(newOrder)            #shuffle

    newpixels=copy.deepcopy(pixels)
    for i in xrange(rows):
        for j in xrange(columns):
            newpixels[i*columns+newOrder[j]]=pixels[i*columns+j]

    im.putdata(newpixels)

def scramble_rows(im,columns,rows):
    pixels =list(im.getdata())

    newOrder=range(rows)        
    random.shuffle(newOrder)            #shuffle the order of pixels

    newpixels=copy.deepcopy(pixels)
    for j in xrange(columns):
        for i in xrange(rows):
            newpixels[i*columns+j]=pixels[columns*newOrder[i]+j]

    im.putdata(newpixels)

def unscramble_rows(im,columns,rows):
    pixels =list(im.getdata())

    newOrder=range(rows)        
    random.shuffle(newOrder)            #shuffle the order of pixels

    newpixels=copy.deepcopy(pixels)
    for j in xrange(columns):
        for i in xrange(rows):
            newpixels[columns*newOrder[i]+j]=pixels[i*columns+j]

    im.putdata(newpixels)


#set random seed based on the given password
def set_seed(password):
    passValue=0
    for ch in password:                 
        passValue=passValue+ord(ch)
    random.seed(passValue)

def encrypt(im,columns,rows,password):
    set_seed(password)
    # scramble(im,columns,rows)
    scramble_columns(im,columns,rows)
    scramble_rows(im,columns,rows)

def decrypt(im,columns,rows,password):
    set_seed(password)
    # unscramble(im,columns,rows)
    unscramble_columns(im,columns,rows)
    unscramble_rows(im,columns,rows)

if __name__ == '__main__':
    passwords=["yOs0ZaKpiS","NA7N3v57og","Nwu2T802mZ","6B2ec75nwu","FP78XHYGmn"]
    iterations=1
    filename="0RT8s.jpg"
    im=Image.open(filename)
    size=im.size
    columns=size[0]
    rows=size[1]

    for i in range(iterations):
        encrypt(im,columns,rows,passwords[i])
    im.save(filename.split(".")[0]+"_encrypted.jpg")

    for i in range(iterations):
        decrypt(im,columns,rows,passwords[iterations-i-1])
    im.save(filename.split(".")[0]+"_decrypted.jpg")

3

C # WinForms

Imagem1: insira a descrição da imagem aqui

Imagem 2: insira a descrição da imagem aqui

Código fonte:

class Program
{
    public static void codec(String src, String trg, bool enc)
    {
        Bitmap bmp = new Bitmap(src);
        Bitmap dst = new Bitmap(bmp.Width, bmp.Height);

        List<Point> points = new List<Point>();
        for (int y = 0; y < bmp.Height; y++)
            for (int x = 0; x < bmp.Width; x++)
                points.Add(new Point(x, y));

        for (int y = 0; y < bmp.Height; y++)
        {
            for (int x = 0; x < bmp.Width; x++)
            {
                int py = Convert.ToInt32(y + 45 * Math.Sin(2.0 * Math.PI * x / 128.0));
                int px = Convert.ToInt32(x + 45 * Math.Sin(2.0 * Math.PI * y / 128.0));

                px = px < 0 ? 0 : px;
                py = py < 0 ? 0 : py;
                px = px >= bmp.Width ? bmp.Width - 1 : px;
                py = py >= bmp.Height ? bmp.Height - 1 : py;

                int srcIndex = x + y * bmp.Width;
                int dstIndex = px + py * bmp.Width;

                Point temp = points[srcIndex];
                points[srcIndex] = points[dstIndex];
                points[dstIndex] = temp;
            }
        }

        for (int y = 0; y < bmp.Height; y++)
        {
            for (int x = 0; x < bmp.Width; x++)
            {
                Point p = points[x + y * bmp.Width];
                if (enc)
                    dst.SetPixel(x, y, bmp.GetPixel(p.X, p.Y));
                else
                    dst.SetPixel(p.X, p.Y, bmp.GetPixel(x, y));
            }
        }

        dst.Save(trg);
    }


    static void Main(string[] args)
    {
        // encode
        codec(@"c:\shared\test.png", @"c:\shared\test_enc.png", true);

        // decode
        codec(@"c:\shared\test_enc.png", @"c:\shared\test_dec.png", false);
    }
}

1

Python 3.6 + pypng

Riffle / Master Shuffle

#!/usr/bin/env python3.6

import argparse
import itertools

import png

def read_image(filename):
    img = png.Reader(filename)
    w, h, data, meta = img.asRGB8()
    return w, h, list(itertools.chain.from_iterable(
        [
            (row[i], row[i+1], row[i+2])
            for i in range(0, len(row), 3)
        ]
        for row in data
    ))

def riffle(img, n=2):
    l = len(img)
    base_size = l // n
    big_groups = l % n
    base_indices = [0]
    for i in range(1, n):
        base_indices.append(base_indices[-1] + base_size + int(i <= big_groups))
    result = []
    for i in range(0, base_size):
        for b in base_indices:
            result.append(img[b + i])
    for i in range(big_groups):
        result.append(img[base_indices[i] + base_size])
    return result

def master(img, n=2):
    parts = [[] for _ in range(n)]
    for i, pixel in enumerate(img):
        parts[i % n].append(pixel)
    return list(itertools.chain.from_iterable(parts))

def main():
    parser = argparse.ArgumentParser()

    parser.add_argument('infile')
    parser.add_argument('outfile')
    parser.add_argument('-r', '--reverse', action='store_true')
    parser.add_argument('-i', '--iterations', type=int, default=1)
    parser.add_argument('-n', '--groupsize', type=int, default=2)
    parser.add_argument('-c', '--complex', nargs='+', type=int)

    args = parser.parse_args()

    w, h, img = read_image(args.infile)

    if args.complex:
        if any(-1 <= n <= 1 for n in args.complex):
            parser.error("Complex keys must use group sizes of at least 2")
        if args.reverse:
            args.complex = [
                -n for n in reversed(args.complex)
            ]
        for n in args.complex:
            if n > 1:
                img = riffle(img, n)
            elif n < -1:
                img = master(img, -n)
    elif args.reverse:
        for _ in range(args.iterations):
            img = master(img, args.groupsize)
    else:
        for _ in range(args.iterations):
            img = riffle(img, args.groupsize)

    writer = png.Writer(w, h)
    with open(args.outfile, 'wb') as f:
        writer.write_array(f, list(itertools.chain.from_iterable(img)))


if __name__ == '__main__':
    main()

Meu algoritmo aplica o shuffle riffle em uma direção e o shuffle master na outra (já que os dois são inversos um do outro), várias iterações cada, mas cada um é generalizado para se dividir em qualquer número de subgrupos em vez de apenas dois. O efeito é que você pode criar uma chave de permutação de várias iterações, pois a imagem não será restaurada sem conhecer a sequência exata de riffle e master shuffles. Uma sequência pode ser especificada com uma série de números inteiros, com números positivos representando riffles e números negativos representando mestres.

Eu embaralhei a paisagem com a tecla [3, -5, 2, 13, -7]:

Paisagem 3 -5 2 13 -7

Curiosamente, algumas coisas interessantes acontecem em [3, -5], onde ficam alguns artefatos da imagem original:

Paisagem 3 -5

Aqui está o padrão abstrato embaralhado com a tecla [2, 3, 5, 7, -11, 13, -17]:

Círculos 2 3 5 7 -11 13 -17

Se apenas um parâmetro estiver errado na chave, a reprodução aleatória não restaurará a imagem:

Bad Unshuffle

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.