Ordnen Sie die Pixel im Bild neu an, damit es nicht erkannt wird, und holen Sie es dann zurück


86

Erstellen Sie ein Programm, mit dem Sie Pixel im Bild neu anordnen können, damit es nicht erkannt wird. Ihr Programm sollte es jedoch wieder in das Originalbild konvertieren können.

Sie können zwei Funktionen schreiben - zum Kodieren und Dekodieren, aber eine Funktion, die wiederholt angewendet wurde, ergibt das Originalbild (Beispiel in Mathe - f(x) = 1 - x ), ist ein Bonus.

Das Erzeugen eines Musters in der Ausgabe gibt auch einen Bonus.

Das Bild kann als 1D / 2D-Array oder Bildobjekt dargestellt werden, wenn Ihre Sprache dies unterstützt. Beachten Sie, dass Sie nur die Reihenfolge der Pixel ändern können!

Es wird logisch sein, als Gewinnercode einen Code zu wählen, der ein weniger erkennbares Bild erzeugt. Ich weiß jedoch nicht, wie ich es genau messen soll. Alle Möglichkeiten, die ich mir vorstellen kann, können betrogen werden. Aus diesem Grund habe ich diese Frage als Beliebtheitswettbewerb ausgewählt - lassen Sie die Benutzer die beste Antwort auswählen!

Testbild 1 (800 x 422 px): Testbild 2 (800 x 480 px): Bitte geben Sie ein Code-Ausgabebild an.


Die Frage ist eine sehr umständliche Art zu sagen: "Schreiben Sie einen Verschlüsselungsalgorithmus für Bilder, deren Ausgabe ein Bild ist."
David Richerby

3
@DavidRicherby… das die gleichen Pixel / Farben verwendet. Fünf schwarze Pixel im "einfachen Bild" -> fünf schwarze Pixel im "Chiffrierbild".
Daniel Beck

2
@ user2992539 Okay, in diesem Fall möchten Sie möglicherweise ausdrücklich angeben, dass dies als Verbindungsunterbrecher verwendet wird. Ansonsten ist es nicht sehr aussagekräftig, nur zu sagen, dass es sich um einen Bonus handelt.
Martin Ender

3
Diese Frage ließ mich an Arnolds Katzenkarte denken . Ich denke nicht, dass es für diesen Zweck gut geeignet ist, aber es ist auf die gleiche Weise interessant. Wenn Sie die Karte mehrmals wiederholen, kehren Sie zum ursprünglichen Bild zurück.
Trichoplax

4
Jetzt an anderer Stelle auf dem Stapel Exchange - Netzwerk: Security.SE aller Orte
Doorknob

Antworten:


58

Python 2.7 (mit PIL) - Keine Pseudozufälligkeit

Ich zerlege das Bild in 2 mal 2 Blöcke (den Rest ignorierend) und drehe jeden Block um 180 Grad, dann mache ich dasselbe mit 3 mal 3 Blöcken, dann 4 usw. bis zu einem gewissen Parameter BLKSZ. Dann mache ich dasselbe für BLKSZ-1, dann BLKSZ-2, bis hinunter zu 3, dann 2. Diese Methode kehrt sich genau um; Die Uncramble-Funktion ist die Scramble-Funktion.

Der Code :

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

Abhängig von der Blockgröße können Sie die Berechnung so einstellen, dass alle Ähnlichkeiten mit dem Originalbild beseitigt werden: (BLKSZ = 50) Bildbeschreibung hier eingeben Bildbeschreibung hier eingeben

Oder machen Sie die Berechnung effizient: (BLKSZ = 10) Bildbeschreibung hier eingeben Bildbeschreibung hier eingeben


6
Klingt nach den besten Ergebnissen, wenn BLKSZ etwa die Hälfte der Bildgröße hat. Wie auch immer, ich mag Algorithmen und für kleine BLKSZ sieht es aus wie eine moderne Kunst! Cool!
Somnium

11
Ich stimme Python immer zu.
Qwr

Anstatt nach allen Werten von 2 bis 50 zu suchen, sollten Sie vielleicht nur Primzahlen verwenden.
Neil

@Neil Wahrscheinlich wird es dann eher zufällig und weniger künstlerisch aussehen.
Somnium

Die BLKSZ = 10Landschaft ist wirklich cool!
Wchargin

52

C #, Winform

Bearbeiten Wenn Sie die Art und Weise ändern, in der Sie das Koordinatenfeld ausfüllen, können Sie verschiedene Muster verwenden (siehe unten)

Magst du diese Art von Muster?

Landschaft

Abstrakt

Bonus:

Schrei Scream Scrambled

Tauschen Sie alle Pixel in der oberen Hälfte genau einmal mit allen Pixeln in der unteren Hälfte aus. Wiederholen Sie das gleiche Verfahren zum Entschlüsseln (Bonus).

Code

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

Überprüfen Sie 'Unsicherer Code' in der Projekteigenschaft zum Kompilieren.

Komplexes Muster

Gerangel

Ändern Sie den ersten Teil der Arbeitsfunktion in 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
Interessant) Ich frage mich, ob es mit einem ähnlichen Ansatz möglich ist, kompliziertere Ausgabemuster zu erstellen.
Somnium

1
Gute Idee - was passiert mit der Mittellinie, wenn die Anzahl der Zeilen ungerade ist?
Fehler

1
@flawr die Teilung erfolgt pro Pixel. Wenn es eine ungerade Anzahl von Pixeln gibt, bleibt das letzte Pixel unberührt. Wenn es eine ungerade Anzahl von Reihen gibt, ist die linke Hälfte der mittleren Reihe "Oberseite" und die rechte Hälfte "Unterseite".
EDC65

1
@ user2992539 Ich denke man kann mehr unterteilen - sogar Schachbrett. Mit mehr Unterteilungen ist das Bild besser erkennbar.
EDC65

7
Wie Ihre "Scramble" -Version!)
Somnium

43

C, willkürliche Unschärfe, leicht umkehrbar

Spät zur Party. Hier ist mein Eintrag!

Diese Methode führt eine Verwacklungsunschärfe aus. Ich nenne es Scramblur . Es ist sehr einfach. In einer Schleife wählt es ein zufälliges Pixel aus und tauscht es dann mit einem zufällig ausgewählten Pixel in der Nähe in einem torusförmigen Canvas-Modell aus. Sie legen den maximalen Abstand fest, der definiert, was "Pixel in der Nähe" bedeutet (1 bedeutet, dass immer ein benachbartes Pixel ausgewählt wird), die Anzahl der Iterationen und optional einen Startwert für eine Zufallszahl. Je größer der maximale Abstand und je größer die Anzahl der Iterationen ist, desto unschärfer ist das Ergebnis.

Sie kann durch Angabe einer negativen Anzahl von Iterationen rückgängig gemacht werden (dies ist lediglich eine bequeme Befehlszeilenschnittstelle; es gibt tatsächlich keine negativen Iterationen). Intern verwendet es ein benutzerdefiniertes 64-Bit-LCPRNG (Generator für lineare kongruente Pseudozufallszahlen) und generiert einen Werteblock vor. Die Tabelle ermöglicht das Durchlaufen des Blocks entweder vorwärts oder rückwärts zum Verwürfeln bzw. zum Aufheben der Verwürfelung.

Demo

Bei den ersten beiden Bildern wird beim Scrollen nach unten jedes Bild mit einem höheren maximalen Versatz verwischt: Oberstes Bild ist das Originalbild (z. B. 0-Pixel-Versatz), gefolgt von 1, 2, 4, 8, 16, 32, 64 , 128 und schließlich 256. Die Iterationszahl beträgt 10⁶ = 1.000.000 für alle Bilder unten.

Bei den zweiten beiden Bildern wird jedes Bild mit einem progressiv niedrigeren Versatz (z. B. vom verschwommensten zum am wenigsten verschwommenen) von maximal 256 bis hinunter zu 0 verwischt . Viel Spaß!

Landschaft Abstrakt

Und für diese beiden nächsten Bilder können Sie die Progressionen hier und hier in voller Größe sehen :

Wandlung zum Bösen Simpsons

Code

Ich habe das in ungefähr einer Stunde zusammen gehackt, als ich heute Morgen aufgewacht bin, und es enthält fast keine Dokumentation. Ich werde vielleicht in ein paar Tagen zurückkommen und später weitere Unterlagen hinzufügen, wenn die Leute es anfordern.

//=============================================================================
// 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
Nur über diese Antwort gescrollt, sieht toll aus
Thomas

1
Diese Antwort ist wirklich groß. Denken Sie, Sie könnten die zusätzlichen Bilder (dh alles außer den zwei Testbildern, die vollständig unscharf sind) in eine Galerie außerhalb des Standorts verschieben?
Tim S.

@TimS. - getan! Sie sind auf winzige Thumbnails geschrumpft.
Todd Lehman

42

Python 3.4

  • Bonus 1: Self Inverse: Wiederholen stellt das Originalbild wieder her.
  • Optionales Schlüsselbild: Das Originalbild kann nur wiederhergestellt werden, wenn dasselbe Schlüsselbild erneut verwendet wird.
  • Bonus 2: Muster in der Ausgabe erzeugen: Das Schlüsselbild wird in den verwürfelten Pixeln angenähert.

Bei Erreichen von Bonus 2 durch Verwendung eines zusätzlichen Schlüsselbildes geht Bonus 1 nicht verloren. Das Programm ist immer noch selbstinvers, sofern es erneut mit demselben Schlüsselbild ausgeführt wird.

Standardgebrauch

Testbild 1:

Verscrambeltes Testbild 1

Testbild 2:

Verscrambeltes Testbild 2

Wenn Sie das Programm mit einer einzelnen Bilddatei als Argument ausführen, wird eine Bilddatei gespeichert, deren Pixel gleichmäßig über das gesamte Bild verteilt sind. Wenn Sie es mit der verschlüsselten Ausgabe erneut ausführen, wird eine Bilddatei mit der erneut angewendeten Verschlüsselung gespeichert, wodurch das Original wiederhergestellt wird, da der Verschlüsselungsprozess seine eigene Umkehrung darstellt.

Der Scrambling-Prozess ist selbstinvers, da die Liste aller Pixel in 2 Zyklen aufgeteilt ist, so dass jedes Pixel mit einem und nur einem anderen Pixel ausgetauscht wird. Wenn Sie es ein zweites Mal ausführen, wird jedes Pixel mit dem Pixel ausgetauscht, mit dem es zuerst ausgetauscht wurde, und alles wird auf den ursprünglichen Zustand zurückgesetzt. Wenn es eine ungerade Anzahl von Pixeln gibt, wird es eines geben, das sich nicht bewegt.

Dank der Antwort von mfvonh als erstem, der 2 Zyklen vorschlägt.

Verwendung mit einem Schlüsselbild

Scrambling Testbild 1 mit Testbild 2 als Schlüsselbild

Scramble Test 1 mit Test 2

Scrambling Testbild 2 mit Testbild 1 als Schlüsselbild

Scramble Test 2 mit Test 1

Wenn Sie das Programm mit einem zweiten Argument für eine Bilddatei (dem Schlüsselbild) ausführen, wird das Originalbild basierend auf dem Schlüsselbild in Bereiche unterteilt. Jede dieser Regionen ist separat in 2 Zyklen unterteilt, so dass das gesamte Verwürfeln innerhalb von Regionen auftritt und Pixel nicht von einer Region zu einer anderen verschoben werden. Dadurch werden die Pixel über jede Region verteilt, und die Regionen erhalten eine einheitliche gesprenkelte Farbe, jedoch mit einer geringfügig unterschiedlichen Durchschnittsfarbe für jede Region. Dies gibt eine sehr grobe Annäherung an das Schlüsselbild in den falschen Farben.

Bei erneuter Ausführung werden dieselben Pixelpaare in jeder Region ausgetauscht, sodass jede Region in ihren ursprünglichen Zustand zurückgesetzt wird und das Bild als Ganzes wieder angezeigt wird.

Dank der Antwort von edc65 als erstem Vorschlag, das Bild in Regionen zu unterteilen. Ich wollte dies erweitern, um beliebige Regionen zu verwenden, aber der Ansatz, alles in Region 1 mit allem in Region 2 zu tauschen, bedeutete, dass die Regionen von identischer Größe sein mussten. Meine Lösung besteht darin, die Regionen voneinander isoliert zu halten und einfach jede Region in sich selbst zu mischen. Da Regionen nicht länger von ähnlicher Größe sein müssen, wird es einfacher, beliebig geformte Regionen anzuwenden.

Code

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

Brennen von JPEG-Bildern

.jpg-Dateien werden sehr schnell verarbeitet, jedoch auf Kosten der Überhitzung. Dies hinterlässt ein eingebranntes Nachbild, wenn das Original wiederhergestellt wird:

jpg brennen

Im Ernst, ein verlustbehaftetes Format führt dazu, dass einige der Pixelfarben geringfügig geändert werden, wodurch die Ausgabe an sich ungültig wird. Wenn ein Schlüsselbild verwendet wird und das Mischen von Pixeln auf Regionen beschränkt ist, wird die gesamte Verzerrung in der Region beibehalten, in der sie aufgetreten ist, und verteilt sich dann gleichmäßig über diese Region, wenn das Bild wiederhergestellt wird. Der Unterschied in der durchschnittlichen Verzerrung zwischen Regionen lässt einen sichtbaren Unterschied zwischen ihnen übrig, so dass die Regionen, die beim Verwürfelungsprozess verwendet werden, im wiederhergestellten Bild immer noch sichtbar sind.

Durch die Konvertierung in das PNG-Format (oder in ein verlustfreies Format) vor dem Verschlüsseln wird sichergestellt, dass das unverschlüsselte Bild mit dem Original identisch ist und weder Brennen noch Verzerren auftritt:

png ohne brennen

Kleine Details

  • Regionen sind mindestens 256 Pixel groß. Wenn das Bild in zu kleine Bereiche aufgeteilt werden könnte, wäre das Originalbild nach dem Verwürfeln teilweise noch sichtbar.
  • Wenn es mehr als eine Region mit einer ungeraden Anzahl von Pixeln gibt, wird ein Pixel aus der zweiten Region der ersten zugewiesen, und so weiter. Dies bedeutet, dass es immer nur einen Bereich mit einer ungeraden Anzahl von Pixeln geben kann, sodass nur ein Pixel unverschlüsselt bleibt.
  • Es gibt ein drittes optionales Argument, das die Anzahl der Regionen einschränkt. Wenn Sie dies beispielsweise auf 2 setzen, erhalten Sie mit zwei Tönen verwürfelte Bilder. Dies kann je nach den beteiligten Bildern besser oder schlechter aussehen. Wenn hier eine Nummer angegeben wird, kann das Image nur mit derselben Nummer wiederhergestellt werden.
  • Die Anzahl der unterschiedlichen Farben im Originalbild begrenzt auch die Anzahl der Regionen. Wenn das Originalbild zweifarbig ist, können ungeachtet des Schlüsselbilds oder des dritten Arguments nur maximal 2 Regionen vorhanden sein.

2
+1 Applaus! Ich dachte vage darüber nach, fand es aber zu schwierig, es umzusetzen.
EDC65

1
Das ist brilliant. Ich habe einen Eintrag eingereicht, aber ich mag Ihren wegen der Key-Image-Funktion besser.
Todd Lehman

Ich wäre gespannt, wie diese beiden Bilder in einem Key gegeneinander aussehen: lardlad.com/assets/wallpaper/simpsons1920.jpg und blogs.nd.edu/oblation/files/2013/09/BreakingBad.jpg (verkleinert auf 720 x 450 oder was auch immer Sinn macht, und natürlich vorkonvertiert in PNG (um das Brennen von JPEG zu vermeiden).
Todd Lehman

2
@ToddLehman mein Algorithmus ist durch die Notwendigkeit, seine eigene Inverse zu sein, begrenzt. Wenn Sie einige wirklich interessante Ansätze zum Mischen eines Bildes sehen möchten, um einem anderen zu ähneln, sollten Sie sich die amerikanische Gotik in der Palette von Mona Lisa ansehen . Einige dieser Programme würden mit den Bildern, die Sie erwähnen, erstaunliche Dinge bewirken.
Trichoplax

2
Die Key-Image-Funktion hebt diesen Kopf und diese Schultern von den anderen ab.
Jack Aidley

33

Hier ist eine nicht zufällige Transformation für eine Änderung

  1. Fügen Sie links alle geraden und rechts alle ungeraden Spalten ein.
  2. wiederhole nxmal
  3. mach das selbe für die reihen nymal

Die Transformation ist fast selbstinvertierend und wiederholt die Transformation insgesamt size_xmehrmals (in x-Richtung), um das Originalbild zurückzugeben. Ich habe die exakte Mathematik nicht herausgefunden, aber die Verwendung eines ganzzahligen Vielfachen von int(log_2(size_x))ergibt das beste Mischen mit den kleinsten Geisterbildern

schlurfte Berge Bildbeschreibung hier eingeben

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

So sehen die ersten Schritte 20 Iterationen aus (nx = ny, beachten Sie den Effekt verschiedener Auflösungen) Bildbeschreibung hier eingeben


7
Das ist ein wirklich cooler Algorithmus. Und Sie sollten sich einen Bonus für die Verwendung des Lena Söderberg-Bildes sichern. :)
Todd Lehman

Immer positiv bewertet Lena

24

Mathematica

Das ist ziemlich einfach. Ich wähle 5 * nPixelszufällige Koordinatenpaare aus und tausche diese beiden Pixel aus (was das Bild komplett verdeckt). Um es zu entschlüsseln, mache ich dasselbe in umgekehrter Reihenfolge. Natürlich muss ich den PRNG setzen, um in beiden Schritten die gleichen Koordinatenpaare zu erhalten.

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

Der einzige Unterschied zwischen den beiden Funktionen liegt Reverse@in unscramble. Beide Funktionen nehmen ein aktuelles Bildobjekt auf. Sie können sie wie folgt verwenden:

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

outund insind identisch. So scrsieht es aus:

Bildbeschreibung hier eingeben Bildbeschreibung hier eingeben


4
Toll! Das einzige Problem ist, dass es sicherer ist, PRNG selbst zu erstellen, denn wenn Mathematica nach einiger Zeit daran denkt, den PRNG-Algorithmus zu ändern, werden alte codierte Bilder nicht decodiert!
Somnium

1
Nett. Sie sollten in der Lage sein, mit Permute und FindPermutation dasselbe Ergebnis zu erzielen.
DavidC

Ich bin mir nicht sicher ob ich das verstehe. Sie können die gewünschte genaue Permutation als Liste von Zyklen eingeben.
DavidC

@ DavidCarraher Hm, interessant. Müsste ich mich nicht an die ursprüngliche Permutation für die Verwendung erinnern FindPermutation?
Martin Ender

Oder vielleicht etwas wie {c, a, b}[[{2, 3, 1}]]verwendet werden kann?
Somnium

22

C # (+ Bonus für symmetrischen Algorithmus)

Dies funktioniert, indem ein xsolches gefunden x^2 == 1 mod (number of pixels in image)und dann der Index jedes Pixels mit multipliziert wird x, um seine neue Position zu finden. Auf diese Weise können Sie mit genau demselben Algorithmus ein Bild ver- und entschlüsseln.

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

erstes Testbild, verschlüsselt

zweites Testbild, verschlüsselt


1
Clever: Wird es immer eine Lösung für diese Kongruenzgleichung geben?
Somnium

1
@ user2992539 Es wird immer die trivialen Lösungen 1(Originalbild) und modulo-1(invertiertes / umgekehrtes Bild) geben. Die meisten Zahlen haben nicht-triviale Lösungen, aber es scheint einige Ausnahmen zu geben . (im Zusammenhang mit der Primfaktorisierung von modulo)
Tim S.

Soweit ich weiß, führen triviale Lösungen zu Bildern, die denen der Eingabe ähneln.
Somnium

Richtig: Gibt1 das Originalbild aus und -1gibt z. B. imgur.com/EiE6VW2
Tim S.

19

C #, selbstinvers, keine Zufälligkeit

Wenn das Originalbild Dimensionen mit Zweierpotenzen hat, wird jede Zeile und Spalte mit der Zeile und Spalte mit dem umgekehrten Bitmuster ausgetauscht, z. B. für ein Bild mit einer Breite von 256, dann wird Zeile 0xB4 mit Zeile 0x2D ausgetauscht. Bilder anderer Größen werden in Rechtecke mit Zweierpotenzseiten aufgeteilt.

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

Erstes Bild:

Erstes Bild verschlüsselt

Zweites Bild:

Verwürfeltes zweites Bild


2
Ich mag die "Plaid" -Ausgabe auf diesem.
Brian Rogers

14

C #

Gleiche Methode für das Scrambling und Uncrambling. Ich würde mich über Verbesserungsvorschläge freuen.

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

Die Ausgabe führt zu einem psychedelischen Plaid Erster Der zweite


Schön, dass dies einige Streifenmuster hat)
Somnium

1
Kannst du bitte die 2 Bilder tauschen? Bei der Frage steht das Bergbild an erster Stelle.
AL

1
Könnten Sie eine kurze Erklärung des Algorithmus hinzufügen?
Trichoplax

14

Python 2 (selbstinvers, keine Zufälligkeit, kontextsensitiv)

Dies wird keine Preise für "am wenigsten erkennbar" gewinnen, aber vielleicht kann es als "interessant" punkten. :-)

Ich wollte etwas kontextsensitives machen, bei dem das Verwürfeln der Pixel tatsächlich vom Bild selbst abhängt.

Die Idee ist ganz einfach: Sortieren Sie alle Pixel nach einem beliebigen Wert, der sich aus der Farbe des Pixels ergibt, und tauschen Sie dann die Positionen des ersten Pixels in dieser Liste mit dem letzten, des zweiten mit dem vorletzten und so weiter.

Leider gibt es bei diesem einfachen Ansatz ein Problem mit Pixeln der gleichen Farbe, so dass mein Programm ein bisschen komplizierter wurde, um es selbstinvers zu machen ...

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"

Das ist das Ergebnis: (abs (r-128) + abs (g-128) + abs (b-128)) // 128 (abs (r-128) + abs (g-128) + abs (b-128)) // 128

Sie können ganz unterschiedliche Ergebnisse erzielen, indem Sie die Hash-Funktion ändern f:

  • r-g-b:

    rgb

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

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

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

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

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

    (r + g + b) // 600

  • 0:

    0


3
BEEINDRUCKEND! Dieser ist großartig !!! Gute Arbeit!
Todd Lehman

1
Das ist das bisher interessanteste. Gute Arbeit!
bebe

12

Mathematica (+ Bonus)

Dies reduziert die Farbkanäle und verschlüsselt das Bild als eine lange Liste von Daten. Das Ergebnis ist eine noch weniger erkennbare verschlüsselte Version, da sie nicht die gleiche Farbverteilung wie das Original aufweist (da diese Daten ebenfalls verschlüsselt wurden). Dies ist am deutlichsten in dem zweiten verschlüsselten Bild zu erkennen, aber wenn Sie genau hinsehen, werden Sie den gleichen Effekt auch in dem ersten sehen. Die Funktion ist eine eigene Inverse.

Es gab einen Kommentar, dass dies möglicherweise nicht gültig ist, da pro Kanal verschlüsselt wird. Ich denke es sollte sein, aber es ist keine große Sache. Die einzige Änderung, die notwendig ist, um ganze Pixel (anstatt pro Kanal) zu verschlüsseln, ist die Änderung Flatten @ xauf 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]]

Erläuterung

Definiert eine Funktion f, die ein zweidimensionales Array annimmt x. Die Funktion verwendet das Produkt der Bildabmessungen als zufälligen Startwert und fasst das Array dann zu einer eindimensionalen Liste f(lokal schattiert) zusammen. Dann wird eine Liste des Formulars erstellt, {1, 2, ... n}in der ndie Länge der fListe angegeben ist. Diese Liste wird zufällig permutiert, in Segmente von 2 unterteilt (z. B. " {{1, 2}, {3, 4}, ...}Letzte Zahl löschen", wenn beide Dimensionen ungerade sind). Anschließend wird die Liste permutiert, findem die Werte bei vertauscht werden Die in jeder soeben erstellten Unterliste angegebenen Positionen und schließlich die Neuformung der permutierten Liste auf die ursprünglichen reduzieren auch die Kanaldatenx . es pro Kanal verwürfelt , weil zusätzlich zu den Bildabmessungen Kollabieren derFlattenBefehl reduziert auch die Kanaldaten in jedem Pixel. Die Funktion ist ihre eigene Inverse, da die Zyklen jeweils nur zwei Pixel enthalten.

Verwendungszweck

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

f @ img1 // Image

Bildbeschreibung hier eingeben

f @ f @ img1 // Image

Bildbeschreibung hier eingeben

f @ img2 // Image

Bildbeschreibung hier eingeben

f @ f @ img2 // Image

Bildbeschreibung hier eingeben

Hier wird verwendet 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

Bildbeschreibung hier eingeben


1
Ich vermute, dass dies nicht die Kriterien erfüllt, da es auf einer kleineren als Pixel-Skala austauscht.
Trichoplax

1
Ich denke nicht, dass es eine gültige Antwort ist, aber ich mag es auch wirklich. Es ist eine faszinierende Wendung, also auf jeden
Fall

1
@githubphagocyte Siehe Update :)
mfvonh

Großartig - ich habe wieder nach +1 gegriffen, aber ich kann es natürlich nicht zweimal machen ...
trichoplax

1
@githubphagocyte Oh richtig, ich vergesse die kurze Syntax kann bizarr sein. Ja. f @ f @ img1 // Imageist (in voller Syntax)Image[f[f[img1]]]
mfvonh

10

Matlab (+ Bonus)

Grundsätzlich wechsle ich die Position von zwei Pixeln nach dem Zufallsprinzip und markiere jedes Pixel, das geschaltet wurde, damit es nicht erneut geschaltet wird. Dasselbe Skript kann erneut für die 'Entschlüsselung' verwendet werden, da ich den Zufallszahlengenerator jedes Mal zurücksetze. Dies geschieht, bis fast alle Pixel umgeschaltet sind (deshalb ist die Schrittweite größer als 2).

EDIT: Habe gerade gesehen, dass Martin Büttner einen ähnlichen Ansatz gewählt hat - ich hatte nicht vor, die Idee zu kopieren - ich habe angefangen, meinen Code zu schreiben, als es keine Antworten gab. Tut mir leid. Ich denke immer noch, dass meine Version einige andere Ideen verwendet =) (Und mein Algorithmus ist weitaus ineffizienter, wenn man sich das Bit ansieht, bei dem die zwei zufälligen Koordinaten ausgewählt werden ^^)

Bilder

1 2

Code

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

Ich verstehe nicht ganz, wird Ihr Code, der beim zweiten Mal auf das verschlüsselte Bild angewendet wird, entschlüsselt?
Somnium

2
Genau: Jedes Mal werden genau zwei Pixel ausgetauscht und werden während des gesamten Vorgangs nicht erneut ausgetauscht. Da die Zufallszahlen beide Male genau gleich sind (aufgrund des Zurücksetzens des Zufallszahlengenerators), werden die Pixelpaare zurückgetauscht. (Die RNG stützt sich immer auf die zuvor generierte Nummer, um die nächste zu generieren, ich hoffe, das ist klar.)
Fehler

1
Ha, das war eigentlich meine anfängliche Idee, aber dann konnte ich nicht die Mühe machen, sicherzustellen, dass jedes Pixel genau einmal ausgetauscht wird, weil ich mich an die Arbeit machen musste. : D +1!
Martin Ender

3
@ user2992539 Schauen Sie sich Octave an , eine nette Open Source- Alternative für Matlab, und Sie können 99% des Matlab-Codes direkt in Octave ausführen.
Fehler

2
Ich denke, wenn Sie wirklich scharf auf Ihre Bilder blinzeln, können Sie immer noch eine gewisse Struktur in der Eingabe erkennen (was darauf zurückzuführen ist, dass nicht alle Pixel bewegt werden). Ich denke, wenn Sie Ihren Auswahlalgorithmus so geändert haben, dass er in O (1) statt in O (∞) ausgeführt wird, können Sie das beheben. ;)
Martin Ender

10

Mathematica - Verwende eine Permutation zum Verwürfeln und ihre Umkehrung zum Entschlüsseln.

Ein JPG-Bild ist eine dreidimensionale Anordnung von {r,g,b}Pixelfarben. (Die drei Dimensionen strukturieren die Pixelmenge nach Zeile, Spalte und Farbe.) Es kann zu einer Liste von {r,g,b}Tripeln zusammengefasst, dann gemäß einer "bekannten" Zyklusliste permutiert und schließlich zu einem Array mit den ursprünglichen Abmessungen wieder zusammengesetzt werden. Das Ergebnis ist ein verschlüsseltes Bild.

Beim Entschlüsseln wird das verschlüsselte Bild mit der Umkehrung der Zyklusliste verarbeitet. Es gibt ja das ursprüngliche Bild aus.

Eine einzige Funktion (im vorliegenden Fall scramble) dient also zum Verwürfeln und Entschlüsseln von Pixeln in einem Bild.

Ein Bild wird zusammen mit einer Startnummer eingegeben (um sicherzustellen, dass sich der Zufallszahlengenerator beim Verwürfeln und Entwürfeln im gleichen Zustand befindet). Wenn der Parameter reverse auf False gesetzt ist, wird die Funktion verschlüsselt. Wenn es wahr ist, wird die Funktion entschlüsseln.


Gerangel

Die Pixel werden abgeflacht und eine zufällige Liste von Zyklen wird erzeugt. Permute verwendet Zyklen, um die Position von Pixeln in der reduzierten Liste zu ändern.

entschlüsseln

scrambleDieselbe Funktion wird zum Entschlüsseln verwendet. Die Reihenfolge der Zyklusliste ist jedoch umgekehrt.

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

Beispiele

Das gleiche Saatgut (37) wird zum Verwürfeln und Entwürfeln verwendet.

Dies erzeugt das verwürfelte Bild des Berges. Das Bild unten zeigt, dass die Variable scrambledMount durch das tatsächliche Bild der Bergszene ersetzt werden kann.

scrambledMount=scramble[mountain, 37, True]

mount1


Nun führen wir das Inverse aus; scrambledMount wird eingegeben und das ursprüngliche Bild wird wiederhergestellt.

 scramble[scrambledMount, 37, True]

mount2


Gleiches gilt für die Kreise:

Kreise1


 scramble[scrambledCircles, 37, True]

Kreise2


Ich kann nicht erkennen, wie ein Bild ein dreidimensionales Array sein kann.
Edc65

1
@ edc65, Zeilen x Spalten x Farben. Das Bergbild ist 422 Zeilen mal 800 Spalten mal 3 Farben. Wenn das Array abgeflacht ist, werden 1012800 Daten als eindimensionales Array, dh als Liste, ausgegeben.
DavidC

@ edc65 Ich sollte hinzufügen, dass Permute verwendet wurde, um die Farben als RGB-Tripel neu anzuordnen. Ich habe das nicht mehr abgeflacht, weil ich nicht daran interessiert war, Änderungen an der Liste der Farben eines Pixels vorzunehmen. Wenn Sie die rgb-Informationen {r, g, b} als Element betrachten, sprechen wir von einem 2D-Array. So gesehen ist es durchaus sinnvoll, die von Ihnen aufgeworfene Frage (Wie könnte ein Bild ein dreidimensionales Array sein?) Zu stellen. Tatsächlich kann es normaler sein, ein Bild als ein 2D-Array zu betrachten, ohne die Tatsache zu berücksichtigen, dass die RGB-Elemente eine weitere Dimension hinzufügen.
DavidC

10

Python

Ich mag dieses Rätsel, er schien interessant zu sein und ich kam mit einer Wickel- und Bewegungsfunktion, um es auf das Bild anzuwenden.

Gewickelt

Ich lese das Bild als Text (von links nach rechts, auf und ab) und schreibe es als Schneckenhaus.

Diese Funktion ist zyklisch: In N gibt es zum Beispiel f ^ (n) (x) = x für ein Bild von 4 * 2, f (f (f (x))) = x

Bewegung

Ich nehme eine Zufallszahl und verschiebe jede Spalte und ligne daraus

Code

# 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

Bilder

Erste Umdrehung: Bildbeschreibung hier eingeben

dann Permutation: Bildbeschreibung hier eingeben

Und beim letzten Wechsel: Bildbeschreibung hier eingeben

Wie für das andere Beispiel: Bildbeschreibung hier eingeben


2
Wie stellen Sie das ursprüngliche Image wieder her?
Trichoplax

Wenn es nur eine Rotation ist, kann ich das eine gewisse Zeit machen (abhängig von der Größe). Wenn ich die Permutationen hätte, wäre ich mir nicht sicher, ob sie zyklisch sind. Ich habe nur eine zweite Funktion, die nur ändert, was aus PM2 [_i, _j] = PM1 [i, j] PM2 [i, j] = PM1 [ _i, _j] und PM3 [(i + a)% w, (j + a)% h] = PM2 [i, j] wurden PM3 [(ia)% w, (ja)% h] = PM2 [i, j]. Ich suche nach einer Möglichkeit, ohne diese beiden Zeilen zu
ändern

8

VB.NET (+ Bonus)

Dies basiert auf der Idee von flawr, verwendet jedoch einen anderen Austausch- und Überprüfungsalgorithmus. Das Programm codiert und decodiert auf die gleiche Weise.

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

Bilder ausgeben:


8

Nachdem ich daran erinnert wurde, dass dies im Begriff ist, die Pixel zu tauschen und nicht zu ändern, ist hier meine Lösung dafür:

Verschlüsselt: Bildbeschreibung hier eingeben

Restauriert: Bildbeschreibung hier eingeben

Dies geschieht durch Randomisierung der Pixelreihenfolge. Um sie jedoch wiederherstellen zu können, ist die Randomisierung festgelegt. Dies geschieht durch Verwendung eines Pseudozufalls mit einem festen Startwert und durch Generieren einer Liste von Indizes, die beschreiben, welche Pixel ausgetauscht werden sollen. Da der Austausch identisch ist, wird mit derselben Liste das ursprüngliche Image wiederhergestellt.

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

Beachten Sie, dass die Verwendung dieses Algorithmus bei einem verlustbehafteten Komprimierungsformat nicht zum gleichen Ergebnis führt, da das Bildformat die Daten verändert. Dies sollte mit jedem verlustfreien Codec wie PNG gut funktionieren.


8

Mathematica

Wir definieren eine Hilfsfunktion hund die Verwürfelungsfunktion scrambleals:

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

Ein Bild nach dem Laden, können Sie anrufen , scramble[img, k]wo keine ganze Zahl ist, das Bild unkenntlich zu machen . Ein erneuter Anruf mit -kentschlüsselt. (Wenn kja 0, dann wird keine Änderung vorgenommen.) Normalerweise ksollte so etwas gewählt werden 100, das ein ziemlich durcheinandergebrachtes Bild ergibt:

Beispielausgabe 1

Beispielausgabe 2


7

Matlab: Zeilen- und Spalten-Scrambling basierend auf Zeilen- / Spalten-Summen-Invarianzen

Das schien ein lustiges Puzzle zu sein, also habe ich es mir überlegt und mir die folgende Funktion ausgedacht. Es basiert auf der Invarianz von Zeilen- und Spaltenpixelwertsummen während des zirkularen Verschiebens: Es verschiebt jede Zeile und dann jede Spalte um die Gesamtsumme der Pixelwerte der Zeile / Spalte (unter der Annahme eines uint8 für eine ganze Zahl in der Verschiebungsvariablen) ). Dies kann dann umgekehrt werden, indem jede Spalte und Zeile um ihren Summenwert in die entgegengesetzte Richtung verschoben wird.

Es ist nicht so hübsch wie die anderen, aber ich mag es, dass es nicht zufällig ist und vom Bild vollständig spezifiziert wird - keine Auswahlparameter.

Ich habe es ursprünglich so entworfen, dass jeder Farbkanal separat verschoben wird, aber dann ist mir aufgefallen, dass nur ganze Pixel verschoben werden sollen.

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

Erstes Testbild Zweites Testbild


6

Java

Dieses Programm tauscht zufällig Pixel aus (erstellt Pixel-zu-Pixel-Mapping), verwendet jedoch anstelle der Zufallsfunktion Math.sin () (Ganzzahl x). Es ist vollständig umkehrbar. Mit Testbildern werden einige Muster erstellt.

Parameter: Ganzzahl (Anzahl der Durchgänge, negative Zahl für Rückwärtsfahrt, 0 macht nichts), Eingabemagier und Ausgabebild (können identisch sein). Die Ausgabedatei sollte ein Format haben, das verlustfreie Komprimierung verwendet.

1 Durchgang: Bildbeschreibung hier eingeben Bildbeschreibung hier eingeben

100 Pässe (es dauert ein paar Minuten): Bildbeschreibung hier eingeben Bildbeschreibung hier eingeben

Code:

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

Ein bisschen zu spät zur Party, aber ich dachte, es würde Spaß machen, die Bilder in Plaids umzuwandeln (und natürlich zurück). Zuerst verschieben wir die Spalten um das 4-fache nach oben oder unten (gerade Spalten nach unten, ungerade Spalten nach oben). Dann verschieben wir die Zeilen nach links oder rechts um das Vierfache der Zeilennummer (gerade Spalten nach links, ungerade Spalten nach rechts).

Das Ergebnis ist ziemlich tartanisch.

Umgekehrt tun wir dies einfach in umgekehrter Reihenfolge und verschieben uns um den entgegengesetzten Betrag.

Code

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)

Ergebnisse

Das Plaid aus Bild 1:

das plaidifizierte 1.jpg

Das karierte Bild 2:

das plaidifizierte 2.png


2
Sehr schön! Ist es möglich, die Diagonalen in einem Winkel von 45 ° zu erhalten?
Todd Lehman

2
Es ist möglich, indem Sie die versetzten Linien auf: offset = x*xsize/ysize und ändern offset = y*ysize/xsize . Leider wird das Bild dadurch auch nicht wirklich ausgeblendet.
jrrl

5

Python (+ Bonus) - Permutation der Pixel

Bei dieser Methode wird jedes Pixel an einer anderen Position platziert, mit der Einschränkung, dass das andere Pixel an der ersten Position platziert wird. Mathematisch handelt es sich um eine Permutation mit der Zykluslänge 2. Die Methode ist als solche eine eigene Inverse.

Rückblickend ist es mfvonh sehr ähnlich, aber diese Vorlage ist in Python und ich musste diese Permutation selbst konstruieren.

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

Erstes Testbild: Erstes Testbild Zweites Testbild: Zweites Testbild


5

Python 2.7 + PIL, Inspiration von den Schiebepuzzles

Hatte gerade eine andere Idee. Grundsätzlich unterteilt diese Methode ein Bild in gleich große Blöcke und mischt dann deren Reihenfolge. Da die neue Reihenfolge auf einem festen Startwert basiert, kann der Vorgang mit demselben Startwert vollständig rückgängig gemacht werden. Außerdem können mit dem zusätzlichen Parameter Granularität unterschiedliche und nicht erkennbare Ergebnisse erzielt werden.

Ergebnisse:

Original

Original

Granularität 16

16

Granularität 13

13

Granularität 10

10

Granularität 3

3

Granularität 2

2

Granularität 1

Bildbeschreibung hier eingeben

Original

Original

Granularität 16

16

Granularität 13

13

Granularität 10

10

Granularität 3

3

Granularität 2

2

Granularität 1

1

Code:

#!/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 Zeilen. 47 zum Codieren, 47 zum Decodieren.

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

Bildbeschreibung hier eingeben Bildbeschreibung hier eingeben

(konvertiert zu jpg)

Bildbeschreibung hier eingeben

(Original verkleinert)

Bildbeschreibung hier eingeben


3
Ein Teil des ursprünglichen Musters ist durch diese Linien sichtbar. Es ähnelt jedoch dem Zeichnen mit einem Finger auf einem beschlagenen Fenster.
Somnium

2
... + 184426 Bytes für codegolf-35005_ref.rb?
Edc65

5

Matlab mit einer Prise Gruppentheorie (+ Bonus)

Bei diesem Ansatz nehmen wir an, dass wir eine gerade Anzahl von Gesamtpixeln haben. (Wenn nicht, ignorieren wir einfach ein Pixel.) Wir müssen also die Hälfte der Pixel auswählen, um sie mit der anderen Hälfte zu tauschen. Dazu indizieren wir alle Pixel von 0bis zu 2N-1und betrachten diese Indizes als zyklische Gruppe.

Unter den Primzahlen suchen wir nach einer Zahl p, die nicht zu klein und nicht zu groß ist und die 2Nder Ordnung unserer Gruppe entspricht. Dies bedeutet, g erzeugt unsere Gruppe oder {k*g mod 2N | k=0,1,...,2N-1} = {0,1,...,2N-1}.

Also wählen wir die ersten NVielfachen von gals eine Menge und alle verbleibenden Indices als die andere Menge und tauschen einfach die entsprechende Menge von Pixeln aus.

Bei prichtiger Wahl wird der erste Satz gleichmäßig über das gesamte Bild verteilt.

Die beiden Testfälle:

Etwas abseits des Themas, aber interessant:

Während des Tests habe ich festgestellt, dass, wenn Sie es in einem (verlustbehafteten komprimierten) JPG (anstelle eines verlustfrei komprimierten PNG) speichern und die Transformation hin und her anwenden, Sie ziemlich schnell Artefakte der Komprimierung sehen, die Ergebnisse zweier aufeinanderfolgender Neuanordnungen :

Wie Sie sehen können, sieht das Ergebnis durch die JPG-Komprimierung fast schwarzweiß aus!

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) - Pixel Divide Swap Repeater

Die Funktion nimmt ein Bildelement und

  1. Dividiert die Pixel durch 8.
  2. Führt einen reversiblen Austausch von Pixelgruppen durch.
  3. Tauschwiederholung, wenn die Pixelgruppe> = 8 ist.
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()
}

Berge Kreise


4

Python 2.7 + PIL, Spalten- / Zeilen-Scrambler

Diese Methode verschlüsselt einfach die Zeilen und Spalten des Bildes. Es ist möglich, nur eine der Dimensionen oder beide zu verschlüsseln. Außerdem basiert die Reihenfolge der neuen verschlüsselten Zeile / Spalte auf einem Passwort. Eine weitere Möglichkeit ist das Verwürfeln des gesamten Bildarrays ohne Berücksichtigung der Abmessungen.

Ergebnisse:

Scrambling das gesamte Bild:

Bildbeschreibung hier eingeben

Bildbeschreibung hier eingeben

Scrambling die Spalten:

Bildbeschreibung hier eingeben

Bildbeschreibung hier eingeben

Scrambling die Zeilen:

Bildbeschreibung hier eingeben

Bildbeschreibung hier eingeben

Scrambling sowohl Spalten als auch Zeilen:

Bildbeschreibung hier eingeben

Bildbeschreibung hier eingeben

Ich habe auch versucht, mehrere Läufe auf das Bild anzuwenden, aber die Endergebnisse unterschieden sich nicht sehr, nur die Schwierigkeit, es zu entschlüsseln.

Code:

from PIL import Image
import random,copy

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

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

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

    im.putdata(newpixels)

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

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

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

    im.putdata(newpixels)

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

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

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

    im.putdata(newpixels)

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

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

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

    im.putdata(newpixels)

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

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

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

    im.putdata(newpixels)

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

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

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

    im.putdata(newpixels)


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

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

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

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

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

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

3

C # Winforms

Bild1: Bildbeschreibung hier eingeben

Bild 2: Bildbeschreibung hier eingeben

Quellcode:

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

Mein Algorithmus wendet das Riffle-Shuffle in eine Richtung und ein Master-Shuffle in die andere Richtung an (da die beiden Inversen voneinander sind), wobei jede Iteration in eine beliebige Anzahl von Untergruppen anstatt nur in zwei aufgeteilt wird. Der Effekt ist, dass Sie einen Permutationsschlüssel mit mehreren Iterationen erstellen können, da das Bild nicht wiederhergestellt werden kann, ohne die genaue Reihenfolge von Riffle- und Master-Shuffles zu kennen. Eine Sequenz kann mit einer Reihe von Ganzzahlen angegeben werden, wobei positive Zahlen Riffles und negative Zahlen Master darstellen.

Ich habe die Landschaft mit der Taste [3, -5, 2, 13, -7] gemischt:

Landschaft 3 -5 2 13 -7

Interessanterweise passieren einige interessante Dinge aus [3, -5], wobei einige Artefakte aus dem Originalbild übrig bleiben:

Landschaft 3 -5

Hier ist das abstrakte Muster, das mit der Taste [2, 3, 5, 7, -11, 13, -17] gemischt wird:

Kreise 2 3 5 7 -11 13 -17

Wenn nur ein Parameter im Schlüssel falsch ist, wird das Image durch das Zurückmischen nicht wiederhergestellt:

Schlechtes Unshuffle

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.