Riorganizzare i pixel nell'immagine in modo che non possano essere riconosciuti e ripristinarli


86

Crea un programma in grado di riorganizzare i pixel nell'immagine in modo che non possa essere riconosciuto. Tuttavia, il programma dovrebbe essere in grado di riconvertirlo in immagine originale.

Puoi scrivere due funzioni - per la codifica e la decodifica, tuttavia una funzione che viene applicata ripetutamente dà l'immagine originale (esempio in matematica - f(x) = 1 - x) è un vantaggio.

Anche la produzione di alcuni pattern in output dà anche un bonus.

L'immagine può essere rappresentata come array 1D / 2D o oggetto immagine se la tua lingua lo supporta. Nota che puoi solo cambiare l'ordine dei pixel!

Sarà logico selezionare come codice vincitore che produce un'immagine meno riconoscibile, tuttavia non so come misurarlo esattamente, tutti i modi che posso immaginare possono essere imbrogliati. Pertanto ho scelto questa domanda come concorso di popolarità: lascia che gli utenti scelgano quale sia la risposta migliore!

Immagine di prova 1 (800 x 422 px): Immagine di prova 2 (800 x 480 px): Fornire un'immagine di output del codice.


La domanda è un modo lungimirante di dire "Scrivi un algoritmo di crittografia per le immagini, il cui output è un'immagine".
David Richerby,

3
@DavidRicherby ... che utilizza gli stessi pixel / colori. Cinque pixel neri nell '"immagine normale" -> cinque pixel neri nell' "immagine cifrata".
Daniel Beck,

2
@ user2992539 Va bene, in tal caso potresti voler dichiarare esplicitamente che questo viene usato come tie-breaker. Altrimenti, solo dire che è un bonus non è molto significativo.
Martin Ender,

3
Questa domanda mi ha fatto pensare alla mappa dei gatti di Arnold . Non penso che sia abbastanza adatto a questo scopo ma è interessante allo stesso modo: ripetere la mappa abbastanza volte ti riporta all'immagine originale.
trichoplax,

4
Ora altrove sulla rete Stack Exchange: Security.SE di tutti i posti
Maniglia della porta

Risposte:


58

Python 2.7 (con PIL) - Nessuna pseudo casualità

Divido l'immagine in 2 per 2 blocchi (ignorando il resto) e ruoto ogni blocco di 180 gradi, quindi faccio lo stesso con 3 per 3 blocchi, quindi 4, ecc. Fino ad alcuni parametri BLKSZ. Quindi faccio lo stesso per BLKSZ-1, poi BLKSZ-2, fino a 3, poi 2. Questo metodo si inverte esattamente; la funzione di riordinamento è la funzione di decodifica.

Il codice :

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!")

A seconda della dimensione del blocco, è possibile fare in modo che il calcolo sradichi tutta la somiglianza con l'immagine originale: (BLKSZ = 50) inserisci qui la descrizione dell'immagine inserisci qui la descrizione dell'immagine

O rendere efficiente il calcolo: (BLKSZ = 10) inserisci qui la descrizione dell'immagine inserisci qui la descrizione dell'immagine


6
Sembrano i migliori risultati se BLKSZ avrà circa la metà della dimensione dell'immagine. Ad ogni modo, mi piace l'algoritmo e per i piccoli BLKSZ sembra un'arte moderna! Freddo!
Somnium,

11
Ho sempre votato pitone.
qwr,

Invece di mescolare tutti i valori da 2 a 50, forse dovresti usare solo numeri primi?
Neil,

@Neil Probabilmente allora sembrerà più casuale e meno artistico.
Somnium,

Il BLKSZ = 10paesaggio è davvero fantastico!
mercoledì

52

C #, Winform

Modifica Modificando il modo in cui riempi l'array di coordinate puoi avere diversi modelli - vedi sotto

Ti piace questo tipo di motivo?

Paesaggio

Astratto

Bonus:

Urlare Urla strapazzata

Scambio casuale esattamente una volta tutti i pixel nella metà superiore con tutti i pixel nella metà inferiore. Ripeti la stessa procedura per riordinare (bonus).

Codice

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

Selezionare "Codice non sicuro" nella proprietà del progetto per la compilazione.

Modello complesso

corsa

Modifica la prima parte della funzione di lavoro, fino ad 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) Mi chiedo se con un approccio simile sia possibile creare schemi più complicati nell'output.
Somnium,

1
Bella idea: cosa succede alla linea di mezzo quando c'è un numero dispari di linee?
Flawr,

1
@flawr la divisione è per pixel. Se c'è un numero dispari di pixel, l'ultimo rimane invariato. Se esiste un numero dispari di righe, la metà sinistra della riga centrale è "lato superiore" e la metà destra è "lato inferiore".
edc65,

1
@ user2992539 Penso che tu possa suddividere di più, anche a scacchiera. Con più suddivisioni, l'immagine è più riconoscibile.
edc65,

7
Come la tua versione "Scramble"!)
Somnium,

43

C, sfocatura arbitraria, facilmente reversibile

In ritardo alla festa. Ecco la mia voce!

Questo metodo fa una sfocatura confusa. Lo chiamo scramblur . È estremamente semplice. In un ciclo, sceglie un pixel casuale e quindi lo scambia con un pixel vicino scelto casualmente in un modello di tela toroidale. Si specifica la distanza massima che definisce cosa significa "pixel vicino" (1 significa scegliere sempre un pixel adiacente), il numero di iterazioni e facoltativamente un seme di numero casuale. Maggiore è la distanza massima e maggiore è il numero di iterazioni, più sfocato è il risultato.

È reversibile specificando un numero negativo di iterazioni (questa è semplicemente una comodità dell'interfaccia della riga di comando; in realtà non esistono cose come iterazioni negative). Internamente, utilizza un LCPRNG personalizzato a 64 bit (generatore di numeri pseudocasuali congruenziali lineari) e genera un blocco di valori. La tabella consente di eseguire il loop attraverso il blocco in avanti o indietro, rispettivamente per rimescolare o riordinare.

dimostrazione

Per le prime due immagini, mentre scorri verso il basso, ogni immagine viene sfocata usando un offset massimo più alto: Topmost è l'immagine originale (ad esempio, offset di 0 pixel), seguita da 1, 2, 4, 8, 16, 32, 64 , 128 e infine 256. Il conteggio delle iterazioni è 10⁶ = 1.000.000 per tutte le immagini di seguito.

Per le seconde due immagini, ogni immagine viene sfocata usando un offset progressivamente più basso - ad esempio, il più sfocato al meno sfocato - da un offset massimo di 256 fino a 0. Buon divertimento!

Paesaggio Astratto

E per queste prossime due immagini, puoi vedere le progressioni a grandezza naturale qui e qui :

Breaking Bad Simpsons

Codice

L'ho hackerato insieme in circa un'ora mentre mi svegliavo questa mattina e non contiene quasi documentazione. Potrei tornare tra qualche giorno e aggiungere più documentazione in seguito se la gente lo richiede.

//=============================================================================
// 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
Ho appena passato questa risposta, sembra fantastico
Thomas,

1
Questa risposta è davvero alta. Pensi di poter spostare le immagini extra (ovvero tutto tranne le due immagini di prova, completamente sfocate) in una galleria fuori sede?
Tim S.

@TimS. - fatto! li ha ridotti a minuscole anteprime.
Todd Lehman,

42

Python 3.4

  • Bonus 1: Auto inverso: la ripetizione ripristina l'immagine originale.
  • Immagine chiave opzionale: l'immagine originale può essere ripristinata solo usando nuovamente la stessa immagine chiave.
  • Bonus 2: produzione di pattern nell'output: l'immagine chiave è approssimata nei pixel criptati.

Quando si ottiene il bonus 2, utilizzando un'immagine chiave aggiuntiva, il bonus 1 non viene perso. Il programma è ancora auto-inverso, purché venga nuovamente eseguito con la stessa immagine chiave.

Utilizzo standard

Immagine di prova 1:

Immagine di prova criptata 1

Immagine di prova 2:

Immagine di prova criptata 2

L'esecuzione del programma con un singolo file di immagine come argomento salva un file di immagine con i pixel mescolati uniformemente sull'intera immagine. L'esecuzione di nuovo con l'output criptato salva un file di immagine con il rimescolamento applicato di nuovo, il che ripristina l'originale poiché il processo di criptaggio è il suo contrario.

Il processo di scrambling è auto-inverso perché l'elenco di tutti i pixel è diviso in 2 cicli, in modo che ogni pixel sia scambiato con un solo e un altro pixel. Eseguendolo una seconda volta scambia ogni pixel con il pixel con cui è stato scambiato per la prima volta, riportando tutto a come è iniziato. Se c'è un numero dispari di pixel ce ne sarà uno che non si sposta.

Grazie alla risposta di mfvonh come primo a suggerire 2 cicli.

Utilizzo con un'immagine chiave

Scrambling Immagine di prova 1 con Immagine di prova 2 come immagine chiave

Scramble test 1 con test 2

Scrambling Test immagine 2 con Test immagine 1 come immagine chiave

Scramble test 2 con test 1

L'esecuzione del programma con un secondo argomento del file di immagine (l'immagine chiave) divide l'immagine originale in regioni in base all'immagine chiave. Ognuna di queste regioni è divisa in 2 cicli separatamente, in modo che tutto il rimescolamento avvenga all'interno delle regioni e i pixel non vengano spostati da una regione all'altra. Questo distribuisce i pixel su ogni regione e quindi le regioni diventano un colore maculato uniforme, ma con un colore medio leggermente diverso per ogni regione. Ciò fornisce un'approssimazione molto approssimativa dell'immagine chiave, con colori sbagliati.

La nuova esecuzione scambia le stesse coppie di pixel in ciascuna regione, quindi ciascuna regione viene ripristinata al suo stato originale e riappare l'immagine nel suo insieme.

Grazie alla risposta di edc65 come il primo a suggerire di dividere l'immagine in regioni. Volevo espandermi su questo per usare regioni arbitrarie, ma l'approccio di scambiare tutto nella regione 1 con tutto nella regione 2 significava che le regioni dovevano avere le stesse dimensioni. La mia soluzione è quella di mantenere le regioni isolate l'una dall'altra e semplicemente mescolare ciascuna regione in se stessa. Poiché le regioni non devono più avere dimensioni simili, diventa più semplice applicare regioni di forma arbitraria.

Codice

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

Masterizzazione di immagini JPEG

I file .jpg vengono elaborati molto rapidamente, ma a scapito di essere troppo caldi. Questo lascia un'immagine masterizzata dopo il ripristino dell'originale:

masterizzazione jpg

Ma seriamente, un formato con perdita comporterà una leggera modifica di alcuni dei colori dei pixel, il che di per sé rende l'output non valido. Quando viene utilizzata un'immagine chiave e il mescolamento dei pixel è limitato alle regioni, tutta la distorsione viene mantenuta all'interno della regione in cui si è verificata e quindi distribuita uniformemente su quella regione quando l'immagine viene ripristinata. La differenza nella distorsione media tra le regioni lascia una differenza visibile tra loro, quindi le regioni utilizzate nel processo di rimescolamento sono ancora visibili nell'immagine ripristinata.

La conversione in .png (o in qualsiasi formato senza perdita di dati) prima della confusione assicura che l'immagine non decodificata sia identica all'originale senza bruciature o distorsioni:

png senza bruciature

Piccoli dettagli

  • Una dimensione minima di 256 pixel è imposta alle regioni. Se all'immagine fosse permesso di dividere in aree troppo piccole, l'immagine originale sarebbe comunque parzialmente visibile dopo la confusione.
  • Se esiste più di una regione con un numero dispari di pixel, un pixel della seconda regione viene riassegnato alla prima e così via. Ciò significa che può esistere solo una regione con un numero dispari di pixel, quindi solo un pixel rimarrà decodificato.
  • C'è un terzo argomento facoltativo che limita il numero di regioni. Impostando questo su 2 per esempio, si otterranno immagini confuse a due toni. Questo può apparire migliore o peggiore a seconda delle immagini coinvolte. Se qui viene specificato un numero, l'immagine può essere ripristinata usando nuovamente lo stesso numero.
  • Il numero di colori distinti nell'immagine originale limita anche il numero di regioni. Se l'immagine originale ha due tonalità, indipendentemente dall'immagine chiave o dal terzo argomento, possono esserci solo un massimo di 2 regioni.

2
+1 applauso! Ci ho pensato vagamente, ma ho trovato troppo difficile da implementare.
edc65,

1
È brillante. Ho inviato una proposta, ma mi piace di più la tua a causa della caratteristica dell'immagine chiave.
Todd Lehman,

Sarei curioso di vedere come queste due immagini si sovrappongono : lardlad.com/assets/wallpaper/simpsons1920.jpg e blogs.nd.edu/oblation/files/2013/09/BreakingBad.jpg (ridimensionato a 720x450 o qualunque cosa abbia senso, e ovviamente pre-convertito in PNG per evitare la masterizzazione JPEG).
Todd Lehman,

2
@ToddLehman il mio algoritmo è limitato dalla necessità di essere il suo contrario. Se vuoi vedere alcuni approcci davvero interessanti per mescolare un'immagine per assomigliare a un'altra, dovresti guardare American Gothic nella tavolozza di Mona Lisa . Alcuni di questi programmi farebbero cose incredibili con le immagini che menzionate.
trichoplax,

2
La caratteristica dell'immagine chiave mette questa testa e le spalle sopra il resto.
Jack Aidley,

33

Ecco una trasformazione non casuale per un cambiamento

  1. Metti tutte le colonne pari a sinistra e tutte le colonne dispari a destra.
  2. ripetere i nxtempi
  3. fare lo stesso per i nytempi delle file

La trasformazione è quasi auto-inversa, ripetendo la trasformazione un totale di size_xvolte (in direzione x) restituisce l'immagine originale. Non ho capito la matematica esatta, ma l'uso di multipli interi int(log_2(size_x))produce il miglior rimescolamento con le immagini fantasma più piccole

montagne mescolate inserisci qui la descrizione dell'immagine

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

Ecco come appaiono i primi passi 20 iterazioni (nx = ny, nota l'effetto di diverse risoluzioni) inserisci qui la descrizione dell'immagine


7
Questo è un algoritmo davvero interessante. E dovresti ottenere totalmente un bonus per l'utilizzo della foto di Lena Söderberg. :)
Todd Lehman,


24

matematica

Questo è piuttosto semplice. Prendo 5 * nPixelscoppie di coordinate casuali e scambiare i due pixel (che oscura completamente l'immagine). Per decodificarlo faccio lo stesso al contrario. Ovviamente, devo seminare il PRNG per ottenere le stesse coppie di coordinate su entrambi i passaggi.

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

L'unica differenza tra le due funzioni è Reverse@in unscramble. Entrambe le funzioni accettano un vero oggetto immagine. Puoi usarli come segue:

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

oute insono identici. Ecco come scrappare:

inserisci qui la descrizione dell'immagine inserisci qui la descrizione dell'immagine


4
Grande! L'unico problema è che è più sicuro creare da soli il PRNG, perché se dopo qualche tempo Mathematica pensa di cambiare l'algoritmo PRNG, questo non decodificherà le vecchie immagini codificate!
Somnium,

1
Bello. Dovresti essere in grado di ottenere lo stesso risultato con Permute e FindPermutation.
DavidC,

Non sono sicuro di capire. È possibile inserire la permutazione precisa desiderata come elenco di cicli.
DavidC

@DavidCarraher Hm, interessante. Non dovrei ricordare la permutazione originale per l'utilizzo FindPermutationperò?
Martin Ender,

O forse qualcosa che {c, a, b}[[{2, 3, 1}]]può essere usato?
Somnium,

22

C # (+ Bonus per algoritmo simmetrico)

Funziona trovando xtale x^2 == 1 mod (number of pixels in image), e quindi moltiplicando l'indice di ciascun pixel xper trovare la sua nuova posizione. Ciò ti consente di utilizzare lo stesso algoritmo per decodificare e decodificare un'immagine.

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

prima immagine di prova, criptata

seconda immagine di prova, criptata


1
Uno intelligente) Ci sarà sempre una soluzione a quell'equazione di congruenza?
Somnium,

1
@ user2992539 Ci saranno sempre le soluzioni banali, 1(immagine originale) e modulo-1(immagine invertita / invertita). La maggior parte dei numeri ha soluzioni non banali, ma a quanto pare ci sono alcune eccezioni . (relativo alla scomposizione in fattori primi di modulo)
Tim S.

A quanto ho capito, le banali soluzioni portano a un'immagine simile a quella di input.
Somnium,

Corretto: stampa1 l'immagine originale e -1produce ad es. Imgur.com/EiE6VW2
Tim S.

19

C #, auto-inverso, nessuna casualità

Se l'immagine originale ha dimensioni che sono potenze di due, ogni riga e colonna viene scambiata con la riga e la colonna con il modello di bit invertito, ad esempio per un'immagine di larghezza 256, la riga 0xB4 viene scambiata con la riga 0x2D. Immagini di altre dimensioni sono divise in rettangoli con lati di potenze di 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;
        }
    }
}

Prima immagine:

Prima immagine criptata

Seconda immagine:

Seconda immagine rimescolata


2
Mi piace l'output "plaid" su questo.
Brian Rogers,

14

C #

Stesso metodo per rimescolare e riordinare. Gradirei suggerimenti su come migliorare questo.

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

I risultati generano un plaid psichedelico Il primo Il secondo


Bello che questo abbia qualche motivo a strisce)
Somnium

1
Potete per favore scambiare le 2 immagini? Nella domanda l'immagine delle montagne è la prima.
AL

1
Potresti includere una breve spiegazione dell'algoritmo?
trichoplax,

14

Python 2 (auto-inverso, nessuna casualità, sensibile al contesto)

Questo non vincerà alcun premio per "meno riconoscibile", ma forse può essere considerato "interessante". :-)

Volevo creare qualcosa di sensibile al contesto, in cui la confusione dei pixel dipende in realtà dall'immagine stessa.

L'idea è abbastanza semplice: ordina tutti i pixel in base a un valore arbitrario derivato dal colore del pixel, quindi scambia le posizioni del primo pixel in quell'elenco con l'ultimo, il secondo con il secondo ultimo e così via.

Sfortunatamente in questo semplice approccio c'è un problema con i pixel dello stesso colore, quindi per renderlo ancora inverso il mio programma è diventato un po 'più complicato ...

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"

Questo è il risultato: (Abs (R-128) + abs (g-128) + abs (b-128)) // 128 (Abs (R-128) + abs (g-128) + abs (b-128)) // 128

Puoi ottenere risultati abbastanza diversi modificando la funzione 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 + b + g) // 600

  • 0:

    0


3
WOW! Questo è fantastico !!! Bel lavoro!
Todd Lehman,

1
Questo è il più interessante finora. Buon lavoro!
Bebe,

12

Mathematica (+ bonus)

Questo comprime i canali di colore e confonde l'immagine come un lungo elenco di dati. Il risultato è una versione criptata ancora meno riconoscibile perché non ha la stessa distribuzione cromatica dell'originale (poiché anche quei dati sono stati criptati). Questo è più evidente nella seconda immagine confusa, ma se guardi da vicino vedrai lo stesso effetto anche nella prima. La funzione è il suo contrario.

C'è stato un commento secondo cui questo potrebbe non essere valido perché si mescola per canale. Penso che dovrebbe essere, ma non è un grosso problema. L'unica modifica necessaria per mescolare pixel interi (anziché per canale) sarebbe quella Flatten @ xdi 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]]

Spiegazione

Definisce una funzione fche accetta un array bidimensionale x. La funzione utilizza il prodotto delle dimensioni dell'immagine come seme casuale, quindi appiattisce l'array in un elenco monodimensionale f(localmente ombreggiato). Quindi crea un elenco del modulo in {1, 2, ... n}cui si ntrova la lunghezza f, che consente in modo casuale tale elenco, lo suddivide in segmenti di 2 (quindi, ad esempio {{1, 2}, {3, 4}, ...}(eliminando l'ultimo numero se le dimensioni sono entrambe dispari), quindi permuta fscambiando i valori in le posizioni indicate in ogni sottoelenco appena creato, e infine rimodella l'elenco permutato alle dimensioni originali delx . Si rimescola per canale perché oltre a comprimere le dimensioni dell'immagine ilFlattenIl comando comprime anche i dati del canale in ciascun pixel. La funzione è propriamente inversa perché i cicli includono solo due pixel ciascuno.

uso

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

f @ img1 // Image

inserisci qui la descrizione dell'immagine

f @ f @ img1 // Image

inserisci qui la descrizione dell'immagine

f @ img2 // Image

inserisci qui la descrizione dell'immagine

f @ f @ img2 // Image

inserisci qui la descrizione dell'immagine

Qui sta 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

inserisci qui la descrizione dell'immagine


1
La mia ipotesi è che questo non soddisfi i criteri, poiché scambia su una scala inferiore a pixel.
trichoplax,

1
Non credo sia una risposta valida, ma mi piace anche molto. È una svolta affascinante, quindi +1 comunque ...
trichoplax,

1
@githubphagocyte Vedi aggiornamento :)
mfvonh,

Fantastico - ho raggiunto di nuovo il +1 ma ovviamente non posso farlo due volte ...
trichoplax,

1
@githubphagocyte Oh giusto, dimentico che la breve sintassi può essere bizzarra. Sì. f @ f @ img1 // Imageè (in completa sintassi)Image[f[f[img1]]]
mfvonh

10

Matlab (+ bonus)

Fondamentalmente cambio la posizione di due pixel a caso e taggo ogni pixel che è stato cambiato in modo che non venga cambiato di nuovo. Lo stesso script può essere riutilizzato per la "decodifica" perché ogni volta ripristino il generatore di numeri casuali. Questo viene fatto fino a quando quasi tutti i pixel vengono commutati (ecco perché il passo è maggiore di 2)

EDIT: Ho appena visto che Martin Büttner ha usato un approccio simile - non avevo intenzione di copiare l'idea - ho iniziato a scrivere il mio codice quando non c'erano risposte, quindi mi dispiace. Penso ancora che la mia versione usi alcune idee diverse =) (E il mio algoritmo è molto più inefficiente se guardi il bit in cui vengono selezionate le due coordinate casuali ^^)

immagini

1 2

Codice

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

Non capisco del tutto, il tuo codice applicato una seconda volta sull'immagine crittografata lo decifrerà?
Somnium,

2
Esatto: ogni volta che vengono scambiati esattamente due pixel e non verranno scambiati di nuovo durante l'intero processo. Poiché i numeri "casuali" sono entrambi esattamente uguali (a causa del ripristino del generatore di numeri casuali), le coppie di pixel verranno scambiate di nuovo. (L'RNG fa sempre affidamento sul numero generato in precedenza per generare il successivo, spero sia chiaro.)
flawr

1
Ah, quella era in realtà la mia idea iniziale, ma poi non potevo preoccuparmi di assicurarmi che ogni pixel venisse scambiato esattamente una volta, perché dovevo mettermi al lavoro. : D +1!
Martin Ender,

3
@ user2992539 Bene, dai un'occhiata a Octave che è una bella alternativa open source per matlab, e puoi eseguire il 99% del codice matlab direttamente in ottava.
flawr

2
Penso che se strizzi gli occhi molto duramente alle tue foto puoi ancora vedere una struttura dall'input (che è dovuto al fatto di non spostare tutti i pixel). Immagino che se hai modificato il tuo algoritmo di selezione per eseguirlo in O (1) invece di O (∞), potresti risolverlo. ;)
Martin Ender,

10

Mathematica-Usa una permutazione per rimescolare e il suo inverso per riordinare.

Un'immagine jpg è una matrice tridimensionale di {r,g,b}colori di pixel. (Le 3 dimensioni strutturano l'insieme di pixel per riga, colonna e colore). Può essere appiattito in un elenco di {r,g,b}triple, quindi permutato secondo un elenco di cicli "noto" e infine riassemblato in una matrice delle dimensioni originali. Il risultato è un'immagine confusa.

La decodifica prende l'immagine rimescolata e la elabora con il rovescio della lista dei cicli. Emette, sì, l'immagine originale.

Quindi una singola funzione (nel presente caso scramble) serve per rimescolare i pixel e per riordinarli in un'immagine.

Viene immessa un'immagine insieme a un numero di seme (per garantire che il generatore di numeri casuali sia nello stesso stato per il rimescolamento e il riordinamento). Quando il parametro, reverse, è False, la funzione si confonderà. Quando è True, la funzione verrà decodificata.


corsa

I pixel vengono appiattiti e viene generato un elenco casuale di cicli. Permute utilizza i cicli per cambiare la posizione dei pixel nell'elenco appiattito.

decodificare

La stessa funzione scrambleviene utilizzata per riordinare. Tuttavia, l'ordine dell'elenco dei cicli è invertito.

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

Esempi

Lo stesso seme (37) è usato per rimescolare e riordinare.

Questo produce l'immagine confusa della montagna. L'immagine seguente mostra che la variabile scrambledMount può essere sostituita dall'immagine reale della scena montana.

scrambledMount=scramble[mountain, 37, True]

mount1


Ora corriamo l'inverso; scrambledMount viene inserito e l'immagine originale viene recuperata.

 scramble[scrambledMount, 37, True]

mount2


Stessa cosa per i cerchi:

circles1


 scramble[scrambledCircles, 37, True]

circles2


Non riesco a vedere come un'immagine possa essere una matrice tridimensionale.
edc65,

1
@ edc65, Righe x Colonne x Colori. L'immagine della montagna è 422 righe per 800 colonne di 3 colori. Se l'array viene appiattito, produce 1012800 parti di dati come array monodimensionali, ovvero come elenco.
DavidC,

@ edc65 Dovrei aggiungere che Permute è stato usato per riorganizzare i colori come triple rgb. Non l'ho più appiattito perché non ero interessato a apportare modifiche all'elenco dei colori di qualsiasi pixel. Se consideri le informazioni rgb, {r, g, b} come un elemento, stiamo parlando di un array 2D. Visto in questo modo, ha perfettamente senso sollevare la domanda (come potrebbe un'immagine essere un array tridimensionale?) Che hai sollevato. In effetti, può essere più normale considerare un'immagine come un array 2D, ignorando il fatto che gli elementi rgb aggiungono un'altra dimensione.
DavidC,

10

Pitone

Mi piace questo puzzle, mi è sembrato interessante e sono arrivato con una funzione di avvolgimento e movimento da applicare sull'immagine.

Wraped

Ho letto l'immagine come testo (da sinistra a destra, su e giù) e la scrivo come un guscio di lumaca.

Questa funzione è ciclica: c'è un in N, f ^ (n) (x) = x per esempio, per un'immagine di 4 * 2, f (f (f (x))) = x

Movimento

Prendo un numero casuale e muovo ogni colonna e ligne da esso

Codice

# 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

Immagini

Prima rotazione: inserisci qui la descrizione dell'immagine

quindi permutazione: inserisci qui la descrizione dell'immagine

E con l'ultimo rotaion: inserisci qui la descrizione dell'immagine

Come per l'altro esempio: inserisci qui la descrizione dell'immagine


2
Come si ripristina l'immagine originale?
trichoplax,

Se è solo rotazione, posso farlo per un certo periodo di tempo (dipende dalla dimensione). Tuttavia, se avessi le permutazioni non sono sicuro che sia ciclico, quindi ho solo una seconda funzione che cambia solo ciò che PM2 [_i, _j] = PM1 [i, j] è diventato PM2 [i, j] = PM1 [ _i, _j] e PM3 [(i + a)% w, (j + a)% h] = PM2 [i, j] è diventato PM3 [(ia)% w, (ja)% h] = PM2 [i, j]. Sto cercando un modo per farlo senza cambiare queste due righe
Faquarl

8

VB.NET (+ bonus)

Questo utilizza l'idea di Flawr, grazie a lui, tuttavia utilizza diversi algoritmi di scambio e controllo. Il programma codifica e decodifica allo stesso modo.

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

Immagini in uscita:


8

Dopo aver ricordato che questo sta per scambiare i pixel e non modificarli, ecco la mia soluzione per questo:

Strapazzate: inserisci qui la descrizione dell'immagine

restaurato: inserisci qui la descrizione dell'immagine

Questo viene fatto randomizzando l'ordine dei pixel, ma per poterlo ripristinare, la randomizzazione è fissa. Questo viene fatto usando uno pseudo-casuale con un seme fisso e genera un elenco di indici che descrivono quali pixel scambiare. Poiché lo scambio sarà lo stesso, lo stesso elenco ripristinerà l'immagine originale.

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

Si noti che l'utilizzo di questo algoritmo su un formato di compressione con perdita non produrrà lo stesso risultato, poiché il formato dell'immagine altererà i dati. Questo dovrebbe funzionare bene con qualsiasi codec senza perdita di dati come PNG.


8

matematica

Definiamo una funzione di supporto he la funzione di scrambling scramblecome:

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

Dopo aver caricato un'immagine, puoi chiamare scramble[img, k]dovunque ksia un numero intero, per rimescolare l'immagine. Chiamare di nuovo con -kdecodificherà. (Se lo kè 0, allora non viene apportata alcuna modifica.) In genere kdovrebbe essere scelto per essere qualcosa del genere 100, che dà un'immagine piuttosto confusa:

Esempio di output 1

Esempio di output 2


7

Matlab: Scrambling di righe e colonne basato su invarianze di somma righe / colonne

Sembrava un puzzle divertente, quindi ci ho pensato e ho pensato alla seguente funzione. Si basa sull'invarianza delle somme dei valori di pixel di riga e colonna durante lo spostamento circolare: sposta ogni riga, quindi ogni colonna, della somma totale dei valori di pixel della riga / colonna (assumendo un uint8 per il numero intero nella variabile di spostamento ). Questo può quindi essere invertito spostando ogni colonna e poi riga del loro valore di somma nella direzione opposta.

Non è carino come gli altri, ma mi piace che sia non casuale e completamente specificato dall'immagine - nessun parametro di scelta.

Inizialmente l'ho progettato per spostare ciascun canale di colore separatamente, ma poi ho notato la specifica di spostare solo i pixel completi.

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

Prima immagine di prova Immagine di prova Secont


6

Giava

Questo programma scambia casualmente pixel (crea una mappatura da pixel a pixel), ma invece della funzione casuale usa Math.sin () (numero intero x). È completamente reversibile. Con le immagini di prova crea alcuni schemi.

Parametri: numero intero (numero di passaggi, numero negativo da invertire, 0 non fa nulla), mago di input e immagine di output (può essere lo stesso). Il file di output deve essere nel formato che utilizza la compressione senza perdita.

1 passaggio: inserisci qui la descrizione dell'immagine inserisci qui la descrizione dell'immagine

100 passaggi (bastano pochi minuti per farlo): inserisci qui la descrizione dell'immagine inserisci qui la descrizione dell'immagine

Codice:

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 con PIL

Un po 'tardi per la festa, ma ho pensato che sarebbe stato divertente convertire le immagini in plaid (e ovviamente dietro). Innanzitutto spostiamo le colonne su o giù di 4 volte il numero di colonne (colonne pari su, colonne dispari su). Quindi, spostiamo le righe a sinistra o a destra di 4 volte il numero di riga (colonne pari a sinistra, colonne dispari a destra).

Il risultato è abbastanza tartanico.

Per invertire, facciamo semplicemente questi nell'ordine opposto e spostiamo di un importo opposto.

Codice

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)

risultati

Il plaid dall'immagine 1:

il 1.jpg plaidificato

The plaid form image 2:

il plaidificato 2.png


2
Molto bella! È possibile ottenere le diagonali lungo un angolo di 45 °?
Todd Lehman,

2
È possibile modificando le linee di offset in: offset = x*xsize/ysize e offset = y*ysize/xsize , purtroppo, non nasconde anche l'immagine.
jrrl,

5

Python (+ bonus) - permutazione dei pixel

In questo metodo, ogni pixel verrà posizionato in un'altra posizione, con il vincolo che l'altro pixel verrà posizionato nella prima posizione. Matematicamente, è una permutazione con durata del ciclo 2. Come tale, il metodo è proprio inverso.

In retrospettiva, è molto simile a mfvonh, ma questa presentazione è in Python e ho dovuto costruire quella permutazione da solo.

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

Prima immagine di prova: Prima immagine di prova Seconda immagine di prova: Seconda immagine di prova


5

Python 2.7 + PIL, Ispirazione dai puzzle scorrevoli

Ho appena avuto un'altra idea. Fondamentalmente, questo metodo divide un'immagine in blocchi di dimensioni uguali e quindi mescola il loro ordine. Poiché il nuovo ordine si basa su un seme fisso, è possibile ripristinare completamente il processo utilizzando lo stesso seme. Inoltre, con il parametro aggiuntivo chiamato granularità, è possibile ottenere risultati diversi e irriconoscibili.

risultati:

Originale

originale

Granularità 16

16

Granularità 13

13

Granularità 10

10

Granularità 3

3

Granularità 2

2

Granularità 1

inserisci qui la descrizione dell'immagine

Originale

originale

Granularità 16

16

Granularità 13

13

Granularità 10

10

Granularità 3

3

Granularità 2

2

Granularità 1

1

Codice:

#!/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 linee. 47 per la codifica, 47 per la decodifica.

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

inserisci qui la descrizione dell'immagine inserisci qui la descrizione dell'immagine

(convertito in jpg)

inserisci qui la descrizione dell'immagine

(originale ridimensionato)

inserisci qui la descrizione dell'immagine


3
Alcuni dei motivi originali sono visibili attraverso quelle linee. Tuttavia assomiglia quando si disegna con un dito sulla finestra appannata).
Somnium,

2
... + 184426 byte per codegolf-35005_ref.rb?
edc65,

5

Matlab con un pizzico di teoria dei gruppi (+ bonus)

In questo approccio supponiamo di avere un numero pari di pixel totali. (In caso contrario, ignoriamo solo un pixel) Quindi dobbiamo scegliere metà dei pixel da scambiare con l'altra metà. Per questo, indicizziamo tutti i pixel 0fino a 2N-1e consideriamo questi indici come un gruppo ciclico.

Tra i numeri primi cerchiamo un numero pche non sia troppo piccolo e non troppo grande, e che sia coprime 2Nall'ordine del nostro gruppo. Questo significag genera il nostro gruppo o {k*g mod 2N | k=0,1,...,2N-1} = {0,1,...,2N-1}.

Quindi scegliamo i primi Nmultipli di gcome un set e tutti gli indci rimanenti come l'altro set e scambiamo semplicemente il corrispondente set di pixel.

Se pviene scelto nel modo giusto, il primo set viene distribuito uniformemente sull'intera immagine.

I due casi di test:

Leggermente fuori tema ma interessante:

Durante il test ho notato che se lo salvi in ​​un jpg (compresso con perdita) (invece di un png compresso senza perdita) e applichi la trasformazione avanti e indietro, vedi abbastanza rapidamente artefatti della compressione, questo mostra i risultati di due riarrangiamenti consecutivi :

Come puoi vedere, la compressione jpg fa sembrare il risultato quasi in bianco e nero!

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 (+ bonus) - ripetitore di scambio pixel divide

La funzione accetta un elemento immagine e

  1. Divide i pixel per 8.
  2. Effettua uno scambio reversibile di gruppi di pixel.
  3. Richiama lo scambio se il gruppo di pixel> = 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()
}

Montagne Circles


4

Python 2.7 + PIL, Scrambler colonna / riga

Questo metodo semplicemente rimescola le righe e le colonne dell'immagine. È possibile mescolare solo una delle dimensioni o entrambe. Inoltre, l'ordine della nuova riga / colonna codificata si basa su una password. Inoltre, un'altra possibilità è quella di rimescolare l'intero array di immagini senza considerare le dimensioni.

risultati:

Rimescolando l'intera immagine:

inserisci qui la descrizione dell'immagine

inserisci qui la descrizione dell'immagine

Rimescolando le colonne:

inserisci qui la descrizione dell'immagine

inserisci qui la descrizione dell'immagine

Rimescolando le file:

inserisci qui la descrizione dell'immagine

inserisci qui la descrizione dell'immagine

Scrambling sia colonne che righe:

inserisci qui la descrizione dell'immagine

inserisci qui la descrizione dell'immagine

Ho anche provato ad applicare diverse esecuzioni all'immagine, ma i risultati finali non differivano molto, solo la difficoltà di decrittografarlo.

Codice:

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

Winforms C #

immagine1: inserisci qui la descrizione dell'immagine

Immagine 2: inserisci qui la descrizione dell'immagine

Codice sorgente:

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

Il mio algoritmo applica il riffle shuffle in una direzione e un master shuffle nell'altra (poiché i due sono inversi l'uno dall'altro), diverse iterazioni ciascuna, ma ognuna è generalizzata per essere suddivisa in un numero qualsiasi di sottogruppi anziché solo due. L'effetto è che potresti creare una chiave di permutazione multi-iterazione poiché l'immagine non verrà ripristinata senza conoscere l'esatta sequenza di riffle e master shuffles. Una sequenza può essere specificata con una serie di numeri interi, con numeri positivi che rappresentano i riffle e numeri negativi che rappresentano i master.

Ho mescolato il paesaggio con il tasto [3, -5, 2, 13, -7]:

Paesaggio 3 -5 2 13 -7

È interessante notare che alcune cose interessanti accadono da [3, -5], dove sono rimasti alcuni manufatti dell'immagine originale:

Paesaggio 3 -5

Ecco il modello astratto mescolato con il tasto [2, 3, 5, 7, -11, 13, -17]:

Cerchi 2 3 5 7-11 13-17

Se nella chiave c'è solo un parametro, il riordino non ripristinerà l'immagine:

Bad Unshuffle

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.