Zmień rozmieszczenie pikseli w obrazie, aby nie można go było rozpoznać, a następnie odzyskaj


86

Utwórz program, który może zmieniać układ pikseli w obrazie, aby nie można go było rozpoznać. Jednak Twój program powinien móc przekonwertować go z powrotem na oryginalny obraz.

Możesz napisać dwie funkcje - do kodowania i dekodowania, jednak jedna funkcja, która zastosowana wielokrotnie daje oryginalny obraz (przykład z matematyki - f(x) = 1 - x) jest bonusem.

Również wytwarzanie pewnego wzorca wyjściowego daje bonus.

Obraz może być reprezentowany jako tablica 1D / 2D lub obiekt obrazu, jeśli Twój język go obsługuje. Pamiętaj, że możesz zmienić tylko kolejność pikseli!

Logiczne będzie wybranie kodu zwycięzcy, który generuje mniej rozpoznawalny obraz, jednak nie wiem, jak dokładnie go zmierzyć, wszystkie sposoby, które mogę sobie wyobrazić, można oszukać. Dlatego wybrałem to pytanie jako konkurs popularności - pozwól użytkownikom wybrać najlepszą odpowiedź!

Obraz testowy 1 (800 x 422 px): Obraz testowy 2 (800 x 480 px): Proszę podać kod wyjściowy obrazu.


Pytanie jest bardzo odważnym sposobem powiedzenia „Napisz algorytm szyfrowania dla obrazów, których wyjściem jest obraz”.
David Richerby

3
@DavidRicherby…, który używa tych samych pikseli / kolorów. Pięć czarnych pikseli na „zwykłym obrazie” -> pięć czarnych pikseli na „obrazie szyfrowym”.
Daniel Beck

2
@ user2992539 W porządku, w takim przypadku możesz chcieć wyraźnie stwierdzić, że jest on używany jako wyłącznik remisu. W przeciwnym razie samo stwierdzenie, że jest to bonus, nie ma większego znaczenia.
Martin Ender

3
To pytanie przypomniało mi mapę kota Arnolda . Nie wydaje mi się, aby był odpowiedni do tego celu, ale jest interesujący w ten sam sposób - powtórzenie mapy wystarczająco dużo razy spowoduje powrót do pierwotnego obrazu.
trichoplax

4
Teraz gdzie indziej w sieci Stack Exchange: Security.SE wszystkich miejsc
Klamka

Odpowiedzi:


58

Python 2.7 (z PIL) - brak pseudolosowości

Rozbijam obraz na 2 na 2 bloki (ignorując resztę) i obracam każdy blok o 180 stopni, następnie robię to samo z 3 na 3 bloki, a następnie 4 itd. Do pewnego parametru BLKSZ. Następnie robię to samo dla BLKSZ-1, następnie BLKSZ-2, aż do 3, następnie 2. Ta metoda odwraca się dokładnie; funkcja rozszyfrowania jest funkcją szyfrowania.

Kod :

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

W zależności od rozmiaru bloku można sprawić, że obliczenia wyeliminują wszystkie podobieństwa do oryginalnego obrazu: (BLKSZ = 50) wprowadź opis zdjęcia tutaj wprowadź opis zdjęcia tutaj

Lub spraw, aby obliczenia były wydajne: (BLKSZ = 10) wprowadź opis zdjęcia tutaj wprowadź opis zdjęcia tutaj


6
Brzmi jak najlepsze wyniki, jeśli BLKSZ będzie miał około połowy rozmiaru obrazu. W każdym razie lubię algorytm i dla małego BLKSZ wygląda jak sztuka współczesna! Chłodny!
Somnium

11
Zawsze głosuję za pytonem.
qwr

Zamiast mieszać wszystkie wartości od 2 do 50, może powinieneś używać tylko liczb pierwszych?
Neil

@ Neil Prawdopodobnie będzie to wyglądało bardziej losowo i mniej artystycznie.
Somnium

BLKSZ = 10Krajobraz jest naprawdę cool!
wchargin

52

C #, Winform

Edytuj Zmieniając sposób wypełniania tablicy współrzędnych, możesz mieć różne wzory - patrz poniżej

Czy podoba ci się ten wzór?

Krajobraz

Abstrakcyjny

Premia:

Krzyk Krzyk Jajecznica

Losowo zamień dokładnie jeden raz wszystkie piksele w górnej połowie ze wszystkimi pikselami w dolnej połowie. Powtórz tę samą procedurę dla rozszyfrowania (bonus).

Kod

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

Zaznacz „Niebezpieczny kod” we właściwości projektu, aby go skompilować.

Złożony wzór

Rozchwytać

Zmień pierwszą część funkcji pracy, aż do 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
Ciekawe) Zastanawiam się, czy przy podobnym podejściu można tworzyć bardziej skomplikowane wzorce w wynikach.
Somnium

1
Fajny pomysł - co dzieje się ze środkową linią, gdy jest nieparzysta liczba linii?
flawr

1
@ flawr podział jest na piksel. Jeśli liczba pikseli jest nieparzysta, ostatni pozostaje nietknięty. Jeśli liczba wierszy jest nieparzysta, lewa połowa środkowego rzędu to „górna strona”, a prawa połowa to „dolna strona”.
edc65

1
@ user2992539 Myślę, że możesz podzielić więcej - nawet szachownicę. Przy większej liczbie podziałów obraz jest bardziej rozpoznawalny.
edc65

7
Podobnie jak Twoja wersja „Scramble”!)
Somnium,

43

C, dowolne rozmycie, łatwo odwracalne

Późno na imprezę. Oto mój wpis!

Ta metoda powoduje rozmycie szyfrujące. Nazywam to scramblur . To jest bardzo proste. W pętli wybiera losowy piksel, a następnie zamienia go losowo wybranym pobliskim pikselem w toroidalnym modelu płótna. Użytkownik określa maksymalną odległość określającą, co oznacza „piksel w pobliżu” (1 oznacza zawsze wybór sąsiedniego piksela), liczbę iteracji i opcjonalnie ziarno liczby losowej. Im większa maksymalna odległość i większa liczba iteracji, tym bardziej rozmyty jest wynik.

Można to odwrócić, podając ujemną liczbę iteracji (jest to po prostu wygoda interfejsu wiersza poleceń; w rzeczywistości nie ma czegoś takiego jak iteracje ujemne). Wewnętrznie wykorzystuje niestandardowy 64-bitowy LCPRNG (liniowy kongruencjalny generator liczb pseudolosowych) i wstępnie generuje blok wartości. Tabela pozwala na zapętlanie bloku do przodu lub do tyłu, odpowiednio dla szyfrowania lub rozszyfrowywania.

Próbny

W przypadku pierwszych dwóch obrazów podczas przewijania w dół każdy obraz jest rozmazany przy użyciu wyższego maksymalnego przesunięcia: Najwyższy jest obraz oryginalny (np. Przesunięcie 0 pikseli), a następnie 1, 2, 4, 8, 16, 32, 64 , 128, a na koniec 256. Liczba iteracji wynosi 10⁶ = 1 000 000 dla wszystkich zdjęć poniżej.

W przypadku dwóch drugich obrazów każde zdjęcie jest rozmazane przy użyciu stopniowo zmniejszanego przesunięcia - np. Najbardziej rozmyte do najmniej rozmytego - od maksymalnego przesunięcia 256 do 0. Ciesz się!

Krajobraz Abstrakcyjny

A dla tych dwóch następnych zdjęć możesz zobaczyć postępy w pełnym rozmiarze tutaj i tutaj :

Breaking Bad Simpsonowie

Kod

Zhakowałem to razem w około godzinę podczas budzenia się rano i nie zawiera prawie żadnej dokumentacji. Mogę wrócić za kilka dni i dodać więcej dokumentacji później, jeśli ludzie tego zażądają.

//=============================================================================
// 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
Właśnie przewinąłem tę odpowiedź, wygląda niesamowicie
Thomas

1
Ta odpowiedź jest naprawdę wysoka. Czy uważasz, że możesz przenieść dodatkowe obrazy (tj. Wszystko oprócz dwóch zdjęć testowych, całkowicie zamazanych) do galerii poza witryną?
Tim S.

@TimS. - gotowy! zmniejszyłem je do małych miniatur.
Todd Lehman

42

Python 3.4

  • Premia 1: Odwrotność siebie: powtarzanie przywraca oryginalny obraz.
  • Opcjonalny obraz klucza: oryginalny obraz można przywrócić tylko przy użyciu tego samego obrazu klucza ponownie.
  • Premia 2: Tworzenie wzoru na wyjściu: obraz klucza jest przybliżony w zaszyfrowanych pikselach.

Kiedy bonus 2 zostanie osiągnięty, poprzez użycie dodatkowego obrazu klucza, bonus 1 nie zostanie utracony. Program jest nadal odwrotny, pod warunkiem, że zostanie ponownie uruchomiony z tym samym obrazem klucza.

Standardowe użycie

Obraz testowy 1:

Obraz testowy kodowany 1

Obraz testowy 2:

Obraz testowy kodowany 2

Uruchomienie programu z pojedynczym plikiem obrazu, ponieważ jego argument zapisuje plik obrazu z pikselami równomiernie zaszyfrowanymi na całym obrazie. Ponowne uruchomienie z zaszyfrowanym wyjściem zapisuje plik obrazu z ponownie zastosowanym szyfrowaniem, co przywraca oryginał, ponieważ proces szyfrowania jest odwrotny.

Proces mieszania jest odwrotny, ponieważ lista wszystkich pikseli jest podzielona na 2 cykle, dzięki czemu każdy piksel jest zamieniany na jeden i tylko jeden piksel. Uruchomienie go po raz drugi zamienia każdy piksel pikselem, z którym został zamieniony, przywracając wszystko do początku. Jeśli jest nieparzysta liczba pikseli, będzie taki, który się nie poruszy.

Dzięki odpowiedzi mfvonh jako pierwszej sugerującej 2-cykle.

Użycie z kluczowym obrazem

Mieszanie obrazu testowego 1 z obrazem testowym 2 jako obrazem kluczowym

Test mieszania 1 z testem 2

Mieszanie obrazu testowego 2 z obrazem testowym 1 jako obrazem kluczowym

Test mieszania 2 z testem 1

Uruchomienie programu z drugim argumentem pliku obrazu (obraz klucza) dzieli oryginalny obraz na regiony na podstawie obrazu klucza. Każdy z tych obszarów jest podzielony na 2 cykle osobno, dzięki czemu całe szyfrowanie odbywa się w obrębie regionów, a piksele nie są przenoszone z jednego regionu do drugiego. Rozkłada to piksele na każdy region, dzięki czemu regiony stają się jednolitym plamkowym kolorem, ale z nieco innym średnim kolorem dla każdego regionu. Daje to bardzo przybliżone przybliżenie kluczowego obrazu, w niewłaściwych kolorach.

Ponowne uruchomienie zamienia te same pary pikseli w każdym regionie, dzięki czemu każdy region jest przywracany do pierwotnego stanu, a obraz jako całość pojawia się ponownie.

Dzięki odpowiedzi edc65 jako pierwszej zasugerował podział obrazu na regiony. Chciałem rozwinąć tę kwestię, aby używać dowolnych regionów, ale podejście polegające na zamianie wszystkiego w regionie 1 na wszystko w regionie 2 oznaczało, że regiony musiały być tej samej wielkości. Moim rozwiązaniem jest utrzymanie regionów w izolacji od siebie i po prostu wtłoczenie każdego regionu w siebie. Ponieważ regiony nie muszą już mieć podobnej wielkości, łatwiej jest zastosować regiony o dowolnym kształcie.

Kod

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

Wypalanie obrazu JPEG

Pliki .jpg są przetwarzane bardzo szybko, ale kosztem zbyt wysokiej temperatury. Powoduje to wypalenie obrazu po przywróceniu oryginału:

palić jpg

Ale poważnie, format stratny spowoduje nieznaczną zmianę niektórych kolorów pikseli, co samo w sobie powoduje, że dane wyjściowe są nieprawidłowe. Gdy używany jest kluczowy obraz, a tasowanie pikseli jest ograniczone do regionów, całe zniekształcenie jest utrzymywane w regionie, w którym się zdarzyło, a następnie równomiernie rozłożone w tym regionie po przywróceniu obrazu. Różnica w średnich zniekształceniach między regionami pozostawia widoczną różnicę między nimi, więc regiony użyte w procesie mieszania są nadal widoczne na przywróconym obrazie.

Konwersja do formatu .png (lub dowolnego formatu bezstratnego) przed szyfrowaniem zapewnia, że ​​nieszyfrowany obraz jest identyczny z oryginałem bez wypalenia lub zniekształceń:

png bez oparzeń

Małe szczegóły

  • Regiony narzucają minimalny rozmiar 256 pikseli. Jeśli obraz zostałby podzielony na regiony, które są zbyt małe, oryginalny obraz byłby nadal częściowo widoczny po mieszaniu.
  • Jeśli istnieje więcej niż jeden region o nieparzystej liczbie pikseli, wówczas jeden piksel z drugiego regionu zostaje przypisany do pierwszego i tak dalej. Oznacza to, że może istnieć tylko jeden region o nieparzystej liczbie pikseli, a więc tylko jeden piksel pozostanie niezaszyfrowany.
  • Istnieje trzeci opcjonalny argument, który ogranicza liczbę regionów. Na przykład ustawienie 2 spowoduje uzyskanie zaszyfrowanych dwóch tonów. Może to wyglądać lepiej lub gorzej w zależności od zaangażowanych obrazów. Jeśli w tym miejscu podano liczbę, obraz można przywrócić tylko przy użyciu tego samego numeru.
  • Liczba wyraźnych kolorów na oryginalnym obrazie również ogranicza liczbę regionów. Jeśli oryginalny obraz ma dwa tony, to niezależnie od obrazu klucza lub trzeciego argumentu, mogą istnieć maksymalnie 2 regiony.

2
+1 oklaski! Niejasno o tym myślałem, ale zbyt trudno było go wdrożyć.
edc65

1
To jest genialne. Mam przesłany wpis, ale bardziej podoba mi się twój ze względu na kluczową funkcję obrazu.
Todd Lehman

Byłbym ciekawy, jak te dwa obrazy wyglądają ze sobą powiązane : lardlad.com/assets/wallpaper/simpsons1920.jpg i blogs.nd.edu/oblation/files/2013/09/BreakingBad.jpg (zmniejszone do 720x450 lub cokolwiek ma sens i oczywiście wstępnie przekonwertowany do formatu PNG, aby uniknąć wypalenia JPEG).
Todd Lehman

2
@ToddLehman mój algorytm jest ograniczony potrzebą bycia własną odwrotnością. Jeśli chcesz zobaczyć kilka naprawdę interesujących podejść do przetasowania jednego obrazu, aby przypominał inny, powinieneś spojrzeć na American Gothic w palecie Mona Lisa . Niektóre z tych programów zrobiłyby niesamowite rzeczy dzięki wspomnianym obrazom.
trichoplax

2
Kluczowa funkcja zdjęcia stawia tę głowę i ramiona nad resztą.
Jack Aidley

33

Oto nieprzypadkowa transformacja dla zmiany

  1. Umieść wszystkie parzyste kolumny po lewej i wszystkie nieparzyste kolumny po prawej stronie.
  2. powtarzać nxczasy
  3. zrób to samo dla nyrazy wierszy

Transformacja jest prawie odwrotna do siebie, powtarzanie transformacji ogółem size_x(w kierunku x) zwraca oryginalny obraz. Nie ustaliłem dokładnej matematyki, ale użycie liczb całkowitych całkowitych int(log_2(size_x))daje najlepsze tasowanie z najmniejszymi obrazami duchów

przetasowane góry wprowadź opis zdjęcia tutaj

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

Tak wyglądają pierwsze kroki 20 iteracji (nx = ny, zwróć uwagę na efekt różnych rozdzielczości) wprowadź opis zdjęcia tutaj


7
To naprawdę fajny algorytm. I powinieneś całkowicie otrzymać bonus za korzystanie z zdjęcia Leny Söderberg. :)
Todd Lehman

Zawsze pozytywnie oceniaj Lena

24

Matematyka

To całkiem proste. Wybieram 5 * nPixelslosowe pary współrzędnych i zamieniam te dwa piksele (co całkowicie zasłania obraz). Aby to rozszyfrować, robię to samo w odwrotnej kolejności. Oczywiście muszę zaszczepić PRNG, aby uzyskać te same pary współrzędnych na obu krokach.

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

Jedyna różnica pomiędzy tymi dwoma funkcjami jest Reverse@w unscramble. Obie funkcje przyjmują rzeczywisty obiekt obrazu. Możesz użyć ich w następujący sposób:

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

outi insą identyczne. Oto jak scrwygląda:

wprowadź opis zdjęcia tutaj wprowadź opis zdjęcia tutaj


4
Wspaniały! Jedynym problemem jest to, że bezpieczniej jest samemu stworzyć PRNG, ponieważ jeśli po pewnym czasie Mathematica zdecyduje się zmienić algorytm PRNG, nie zdekoduje on starych zakodowanych obrazów!
Somnium

1
Miły. Powinieneś być w stanie osiągnąć ten sam wynik dzięki Permute i FindPermutation.
DavidC

Nie jestem pewien, czy rozumiem. Możesz wprowadzić dokładną permutację, którą chcesz, jako listę cykli.
DavidC

@DavidCarraher Hm, ciekawe. Czy nie musiałbym jednak pamiętać oryginalnej permutacji do używania FindPermutation?
Martin Ender

A może coś, co {c, a, b}[[{2, 3, 1}]]można wykorzystać?
Somnium

22

C # (+ Bonus za algorytm symetryczny)

Działa to poprzez znalezienie xtakiego x^2 == 1 mod (number of pixels in image), a następnie pomnożenie indeksu każdego piksela przez xw celu znalezienia jego nowej lokalizacji. Pozwala to na użycie tego samego algorytmu do szyfrowania i deszyfrowania obrazu.

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

pierwszy obraz testowy, kodowany

drugi obraz testowy, kodowany


1
Sprytne) Czy zawsze będzie rozwiązanie tego równania zgodności?
Somnium

1
@ user2992539 Zawsze będą trywialne rozwiązania, 1(oryginalny obraz) i modulo-1(odwrócony / odwrócony obraz). Większość liczb ma nietrywialne rozwiązania, ale wydaje się , że istnieją pewne wyjątki . (związane z pierwszą faktoryzacją modulo)
Tim S.

Jak rozumiem, trywialne rozwiązania prowadzą do obrazu podobnego do wejściowego.
Somnium

Prawidłowo: 1wyświetla oryginalny obraz i -1wysyła np. Imgur.com/EiE6VW2
Tim S.

19

C #, samo-odwrotność, brak losowości

Jeśli oryginalny obraz ma wymiary, które są potęgami dwóch, to każdy wiersz i kolumna są wymieniane z rzędem i kolumną, która ma odwrócony wzór bitowy, na przykład dla obrazu o szerokości 256, wówczas wiersz 0xB4 jest wymieniany z rzędem 0x2D. Obrazy o innych rozmiarach są podzielone na prostokąty o bokach o sile 2.

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

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

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

Pierwsze zdjęcie:

Jajecznica na pierwszym zdjęciu

Drugi obraz:

Jajecznica drugi obraz


2
Podoba mi się wyjście „plaid” na tym.
Brian Rogers

14

DO#

Ta sama metoda szyfrowania i dekodowania. Byłbym wdzięczny za sugestie dotyczące poprawy tego.

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

Wyprowadza wyniki w psychodeliczną kratę Pierwszy Drugi


Fajnie, że ma to jakiś wzór w paski)
Somnium

1
Czy możesz zamienić 2 zdjęcia? W pytaniu pierwszy jest obraz gór.
AL

1
Czy możesz podać krótkie wyjaśnienie algorytmu?
trichoplax

14

Python 2 (samo-odwrotny, bez losowości, kontekstowy)

To nie wygra żadnych nagród za „najmniej rozpoznawalne”, ale może może być uznane za „interesujące”. :-)

Chciałem stworzyć coś kontekstowego, w którym mieszanie pikseli zależy od samego obrazu.

Pomysł jest dość prosty: posortuj wszystkie piksele według dowolnej arbitralnej wartości pochodzącej z koloru piksela, a następnie zamień pozycje pierwszego piksela na tej liście z ostatnim, drugim z drugim do ostatniego i tak dalej.

Niestety w tym prostym podejściu pojawia się problem z pikselami tego samego koloru, więc aby nadal było odwrotnie, mój program stał się nieco bardziej skomplikowany ...

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"

Oto wynik: (abs (r-128) + abs (g-128) + abs (b-128)) // 128 (abs (r-128) + abs (g-128) + abs (b-128)) // 128

Możesz osiągnąć zupełnie inne wyniki, zmieniając funkcję skrótu 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):

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

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

    (r + g + b) // 600

  • 0:

    0


3
ŁAŁ! Ten jest świetny !!! Dobra robota!
Todd Lehman,

1
To jak dotąd najbardziej interesujący. Dobra robota!
bebe

12

Matematyka (+ bonus)

Spowoduje to zwinięcie kanałów kolorów i zaszyfrowanie obrazu jako jednej długiej listy danych. Rezultatem jest jeszcze mniej rozpoznawalna zaszyfrowana wersja, ponieważ nie ma takiego samego rozkładu kolorów jak oryginał (ponieważ te dane również zostały zaszyfrowane). Jest to najbardziej oczywiste na drugim zaszyfrowanym obrazie, ale jeśli przyjrzysz się uważnie, zobaczysz ten sam efekt również na pierwszym. Funkcja jest własną odwrotnością.

Pojawił się komentarz, że może nie być poprawny, ponieważ koduje się na kanał. Myślę, że tak powinno być, ale to nie jest wielka sprawa. Jedyną zmianą konieczną do szyfrowania całych pikseli (zamiast na kanał) byłaby zmiana Flatten @ xna 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]]

Wyjaśnienie

Definiuje funkcję, fktóra przyjmuje tablicę dwuwymiarową x. Funkcja wykorzystuje iloczyn wymiarów obrazu jako losowe ziarno, a następnie spłaszcza tablicę do listy jednowymiarowej f(lokalnie zacienionej). Następnie tworzy listę postaci, w {1, 2, ... n}której njest długość f, losowo permutuje tę listę, dzieli ją na segmenty 2 (czyli np. {{1, 2}, {3, 4}, ...}(Upuszczając ostatnią liczbę, jeśli oba wymiary są nieparzyste), a następnie permutuje f, zamieniając wartości na pozycje wskazane w każdej właśnie utworzonej podlistie, a na koniec przekształca permutowaną listę z powrotem do pierwotnych wymiarów x. Szyfruje się na kanał, ponieważ oprócz zwinięcia wymiarów obrazuFlattenpolecenie również zwija dane kanału w każdym pikselu. Ta funkcja jest odwrotna, ponieważ cykle zawierają tylko dwa piksele.

Stosowanie

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

f @ img1 // Image

wprowadź opis zdjęcia tutaj

f @ f @ img1 // Image

wprowadź opis zdjęcia tutaj

f @ img2 // Image

wprowadź opis zdjęcia tutaj

f @ f @ img2 // Image

wprowadź opis zdjęcia tutaj

Oto użycie 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

wprowadź opis zdjęcia tutaj


1
Domyślam się, że to nie spełnia kryteriów, ponieważ zamienia się w skali mniejszej niż piksel.
trichoplax

1
Nie wydaje mi się, żeby to była prawidłowa odpowiedź, ale bardzo ją lubię. To fascynujący zwrot akcji, więc i tak +1 ...
trichoplax

1
@githubphagocyte Zobacz aktualizację :)
mfvonh

Świetnie - ponownie sięgnąłem po +1, ale oczywiście nie mogę zrobić tego dwa razy ...
trichoplax

1
@githubphagocyte No tak, zapomniałem, że krótka składnia może być dziwna. Tak. f @ f @ img1 // Imagejest (w pełnej składni)Image[f[f[img1]]]
mfvonh

10

Matlab (+ bonus)

Zasadniczo zmieniam losowo dwa piksele i zaznaczam każdy piksel, który został przełączony, aby nie mógł zostać ponownie przełączony. Tego samego skryptu można użyć ponownie do „deszyfrowania”, ponieważ za każdym razem resetuję generator liczb losowych. Odbywa się to do momentu, gdy prawie wszystkie piksele zostaną przełączone (dlatego wielkość kroku jest większa niż 2)

EDYCJA: Właśnie zobaczyłem, że Martin Büttner zastosował podobne podejście - nie zamierzałem kopiować pomysłu - zacząłem pisać kod, gdy nie było odpowiedzi, więc przepraszam za to. Nadal uważam, że moja wersja używa różnych pomysłów =) (A mój algorytm jest znacznie bardziej nieefektywny, jeśli spojrzysz na fragment, w którym wybierane są dwie losowe współrzędne ^^)

Zdjęcia

1 2)

Kod

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

Nie do końca rozumiem, czy Twój kod zastosowany po raz drugi na zaszyfrowanym obrazie go odszyfruje?
Somnium

2
Dokładnie: za każdym razem, gdy dokładnie dwa piksele są zamieniane i nie zostaną zamienione ponownie podczas całego procesu. Ponieważ liczby „losowe” są dokładnie takie same (z powodu zresetowania generatora liczb losowych), pary pikseli zostaną zamienione z powrotem. (RNG zawsze opiera się na poprzednio wygenerowanym numerze, mam nadzieję, że to jasne.)
flawr

1
Ha, to był właściwie mój początkowy pomysł, ale potem nie mogłem się martwić, że każdy piksel zostanie zamieniony dokładnie raz, ponieważ musiałem zabrać się do pracy. : D +1!
Martin Ender

3
@ user2992539 Cóż, sprawdź Octave, która jest przyjemną alternatywą dla Matlaba dla open source, i możesz uruchomić 99% kodu Matlab bezpośrednio w oktawie.
flawr

2
Myślę, że jeśli naprawdę mocno zmrużysz oczy, nadal widzisz pewną strukturę z wejścia (co wynika z nie przesunięcia wszystkich pikseli). Myślę, że jeśli zmieniłeś algorytm wyboru, aby działał w O (1) zamiast O (∞), możesz to naprawić. ;)
Martin Ender

10

Matematyka - użyj permutacji do szyfrowania, a jej odwrotność do rozszyfrowania.

Obraz jpg to trójwymiarowa tablica {r,g,b}kolorów pikseli. (3 wymiary tworzą zestaw pikseli według rzędu, kolumny i koloru). Można go spłaszczyć do listy {r,g,b}potrójnych, a następnie permutować zgodnie ze „znaną” listą cykli, a na końcu ponownie złożyć w szereg oryginalnych wymiarów. Rezultatem jest zakodowany obraz.

Rozszyfrowanie pobiera zaszyfrowany obraz i przetwarza go na odwrocie listy cykli. Wyprowadza, tak, oryginalny obraz.

Tak więc pojedyncza funkcja (w tym przypadku scramble) służy do szyfrowania, a także do rozszyfrowywania pikseli na obrazie.

Obraz jest wprowadzany wraz z numerem początkowym (aby upewnić się, że generator liczb losowych będzie w tym samym stanie dla szyfrowania i rozszyfrowywania). Gdy parametr „reverse” ma wartość False, funkcja będzie się mieszać. Gdy jest to prawda, funkcja rozszyfruje.


rozchwytać

Piksele są spłaszczone i generowana jest losowa lista cykli. Permute używa cykli do przełączania pozycji pikseli na spłaszczonej liście.

rozszyfrować

Ta sama funkcja scramblesłuży do rozszyfrowania. Jednak kolejność listy cykli jest odwrócona.

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

Przykłady

To samo ziarno (37) służy do mieszania i rozszyfrowywania.

To tworzy zakodowany obraz góry. Poniższy obrazek pokazuje, że zmienną scrambledMount można zastąpić faktycznym obrazem sceny górskiej.

scrambledMount=scramble[mountain, 37, True]

mount1


Teraz wykonujemy odwrotność; scrambledMount zostaje wprowadzony, a oryginalne zdjęcie zostaje odzyskane.

 scramble[scrambledMount, 37, True]

mount2


To samo dotyczy kręgów:

okręgi 1


 scramble[scrambledCircles, 37, True]

okręgi 2


Nie widzę, jak obraz może być trójwymiarową tablicą.
edc65,

1
@ edc65, Wiersze x Kolumny x Kolory. Obraz górski ma 422 rzędy na 800 kolumn na 3 kolory. Jeśli tablica jest spłaszczona, to daje 1012800 fragmentów danych jako tablicę jednowymiarową, tj. Jako listę.
DavidC

@ edc65 Powinienem dodać, że Permute został użyty do zmiany kolejności kolorów, gdy trzykrotnie rgb. Nie spłaszczyłem już tego, ponieważ nie byłem zainteresowany wprowadzaniem jakichkolwiek zmian na liście kolorów dowolnego piksela. Jeśli weźmiesz pod uwagę informacje rgb {r, g, b} jako element, to mówimy o tablicy 2D. Patrząc w ten sposób, sensowne jest postawienie pytania (w jaki sposób obraz może być trójwymiarową tablicą?). W rzeczywistości bardziej normalne może być postrzeganie obrazu jako tablicy 2D, pomijając fakt, że elementy rgb dodają inny wymiar.
DavidC

10

Pyton

Podoba mi się ta łamigłówka, wydawał się interesujący, a ja przyszedłem z zawiniętą funkcją ruchu, aby zastosować na obrazie.

Wrapped

Czytam obraz jako tekst (od lewej do prawej, w górę iw dół) i piszę go jako muszlę ślimaka.

Ta funkcja jest cykliczna: na przykład występuje w N, f ^ (n) (x) = x, dla obrazu 4 * 2, f (f (f (x))) = x

Ruch

Biorę losową liczbę i przenoszę z niej każdą kolumnę i ligne

Kod

# 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

Kino

Pierwszy obrót: wprowadź opis zdjęcia tutaj

następnie permutacja: wprowadź opis zdjęcia tutaj

I z ostatnią rotacją: wprowadź opis zdjęcia tutaj

Jeśli chodzi o inny przykład: wprowadź opis zdjęcia tutaj


2
Jak przywrócić oryginalny obraz?
trichoplax

Jeśli to tylko obrót, mogę to zrobić przez określony czas (w zależności od rozmiaru). Jednak gdybym miał permutacje, nie jestem pewien, czy jest on cykliczny, więc mam tylko drugą funkcję, która zmienia tylko to, co PM2 [_i, _j] = PM1 [i, j] stał się PM2 [i, j] = PM1 [ _i, _j] i PM3 [(i + a)% w, (j + a)% h] = PM2 [i, j] stał się PM3 [(ia)% w, (ja)% h] = PM2 [i, jot]. Szukam sposobu na zrobienie tego bez zmiany tych dwóch wierszy
Faquarl

8

VB.NET (+ bonus)

To dzięki pomysłowi flawr, dzięki niemu, wykorzystuje jednak inny algorytm zamiany i sprawdzania. Program koduje i dekoduje w ten sam sposób.

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

Obrazy wyjściowe:


8

Po przypomnieniu, że ma zamiar zamienić piksele i ich nie zmieniać, oto moje rozwiązanie tego problemu:

Jajecznica: wprowadź opis zdjęcia tutaj

Przywrócono: wprowadź opis zdjęcia tutaj

Odbywa się to przez losową kolejność pikseli, ale aby móc ją przywrócić, losowość jest ustalona. Odbywa się to poprzez użycie pseudolosowego ze stałym ziarnem i wygenerowanie listy indeksów opisujących, które piksele należy zamienić. Ponieważ zamiana będzie taka sama, ta sama lista przywróci oryginalny obraz.

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

Należy pamiętać, że użycie tego algorytmu w formacie kompresji stratnej nie da tego samego wyniku, ponieważ format obrazu zmieni dane. Powinno to działać dobrze z każdym bezstratnym kodekiem, takim jak PNG.


8

Matematyka

Definiujemy funkcję pomocnika hi funkcję szyfrowania scramblejako:

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

Po załadowaniu obrazu można nazwać scramble[img, k]gdzie kjest dowolną liczbą całkowitą, wspiąć się obraz. Ponowne wywołanie z -krozszyfruje. (Jeśli ktak 0, to nie wprowadza się żadnych zmian.) Zazwyczaj knależy wybrać coś podobnego 100, co daje dość zakodowany obraz:

Przykładowe dane wyjściowe 1

Przykładowe dane wyjściowe 2


7

Matlab: Mieszanie wierszy i kolumn na podstawie niezmienności sum wierszy / kolumn

To wydawało się zabawną łamigłówką, więc pomyślałem o tym i wymyśliłem następującą funkcję. Opiera się na niezmienności sum pikselowych wartości wierszy i kolumn podczas przesunięcia kołowego: przesuwa każdy wiersz, a następnie każdą kolumnę o całkowitą sumę wartości pikseli rzędu / kolumny (przyjmując uint8 dla liczby całkowitej w zmiennej shift ). Można to następnie odwrócić, przesuwając każdą kolumnę, a następnie rząd o ich wartość sumaryczną w przeciwnym kierunku.

Nie jest tak ładny jak inne, ale podoba mi się, że jest nieprzypadkowy i w pełni określony przez obraz - bez wybierania parametrów.

Pierwotnie zaprojektowałem go, aby przesuwał każdy kanał kolorów osobno, ale potem zauważyłem specyfikację, aby przenosić tylko pełne piksele.

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

Pierwszy obraz testowy Zdjęcie testowe Secont


6

Jawa

Ten program losowo zamienia piksele (tworzy odwzorowanie piksel na piksel), ale zamiast funkcji losowej używa Math.sin () (liczba całkowita x). Jest w pełni odwracalny. Z obrazami testowymi tworzy pewne wzory.

Parametry: liczba całkowita (liczba przebiegów, liczba ujemna do odwrócenia, 0 nic nie robi), mag wejściowy i obraz wyjściowy (mogą być takie same). Plik wyjściowy powinien być w formacie wykorzystującym bezstratną kompresję.

1 przejście: wprowadź opis zdjęcia tutaj wprowadź opis zdjęcia tutaj

100 podań (zajmuje to kilka minut): wprowadź opis zdjęcia tutaj wprowadź opis zdjęcia tutaj

Kod:

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

Trochę późno na imprezę, ale pomyślałem, że fajnie byłoby przekonwertować obrazy na pledy (i oczywiście z powrotem). Najpierw przesuwamy kolumny w górę lub w dół o 4 razy liczbę kolumn (parzyste kolumny w dół, nieparzyste kolumny w górę). Następnie przesuwamy wiersze w lewo lub w prawo o 4-krotność numeru wiersza (parzyste kolumny w lewo, nieparzyste kolumny w prawo).

Rezultat jest dość tartański.

Aby odwrócić, po prostu robimy to w odwrotnej kolejności i przesuwamy o przeciwną wartość.

Kod

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)

Wyniki

Plaid z obrazka 1:

plaidified 1.jpg

Obraz w kratę 2:

plaidified 2.png


2
Bardzo dobrze! Czy możliwe jest ustawienie przekątnych pod kątem 45 °?
Todd Lehman,

2
Jest to możliwe, zmieniając linie przesunięcia na: offset = x*xsize/ysize i, offset = y*ysize/xsize ale tak naprawdę nie ukrywa to również obrazu, niestety.
jrrl

5

Python (+ bonus) - permutacja pikseli

W tej metodzie każdy piksel zostanie umieszczony w innej pozycji, z tym że drugi piksel zostanie umieszczony w pierwszej pozycji. Matematycznie jest to permutacja o długości cyklu 2. W związku z tym metoda jest odwrotna.

Patrząc wstecz, jest bardzo podobny do mfvonh, ale to przesłanie jest w Pythonie i sam musiałem zbudować tę permutację.

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

Pierwszy obraz testowy: Pierwszy obraz testowy Drugi obraz testowy: Drugi obraz testowy


5

Python 2.7 + PIL, Inspiracja z przesuwanych puzzli

Właśnie miałem inny pomysł. Zasadniczo ta metoda dzieli obraz na bloki o równej wielkości, a następnie tasuje ich kolejność. Ponieważ nowe zamówienie opiera się na stałym ziarnie, możliwe jest całkowite cofnięcie procesu przy użyciu tego samego ziarna. Poza tym dzięki dodatkowemu parametrowi zwanemu ziarnistością możliwe jest uzyskanie różnych i nierozpoznawalnych wyników.

Wyniki:

Oryginał

oryginał

Ziarnistość 16

16

Ziarnistość 13

13

Ziarnistość 10

10

Ziarnistość 3

3)

Ziarnistość 2

2)

Ziarnistość 1

wprowadź opis zdjęcia tutaj

Oryginał

oryginał

Ziarnistość 16

16

Ziarnistość 13

13

Ziarnistość 10

10

Ziarnistość 3

3)

Ziarnistość 2

2)

Ziarnistość 1

1

Kod:

#!/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 linie. 47 do kodowania, 47 do dekodowania.

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

wprowadź opis zdjęcia tutaj wprowadź opis zdjęcia tutaj

(przekonwertowane na jpg)

wprowadź opis zdjęcia tutaj

(oryginał zmniejszony)

wprowadź opis zdjęcia tutaj


3
Część oryginalnego wzoru jest widoczna przez te linie. Jednak przypomina to, gdy rysujesz palcem na zamglonym oknie).
Somnium

2
... + 184426 bajtów dla codegolf-35005_ref.rb?
edc65

5

Matlab ze szczyptą teorii grupy (+ bonus)

W tym podejściu zakładamy, że mamy parzystą liczbę wszystkich pikseli. (Jeśli nie, po prostu ignorujemy jeden piksel). Musimy więc wybrać połowę pikseli, aby zamienić drugą połowę. Do tego mamy indeksowania wszystkich pikseli od 0góry do 2N-1i rozważenia tych indeksów jako grupy cyklicznej.

Wśród liczb pierwszych szukamy liczby, pktóra nie jest zbyt mała i nie jest zbyt duża, a więc jest zgodna 2Nz porządkiem naszej grupy. Oznacza to, że g generuje naszą grupę lub {k*g mod 2N | k=0,1,...,2N-1} = {0,1,...,2N-1}.

Wybieramy więc pierwsze Nwielokrotności gjednego zestawu, a wszystkie pozostałe indki jako drugi zestaw, i po prostu zamieniamy odpowiedni zestaw pikseli.

Jeśli pzostanie wybrany we właściwy sposób, pierwszy zestaw zostanie równomiernie rozłożony na cały obraz.

Dwa przypadki testowe:

Nieco tematyczny, ale interesujący:

Podczas testów zauważyłem, że jeśli zapiszesz go w (bezstratnie skompresowanym) jpg (zamiast bezstratnie skompresowanego png) i zastosujesz transformację tam iz powrotem, dość szybko zobaczysz artefakty kompresji, to pokazuje wyniki dwóch kolejnych przestawień :

Jak widać, kompresja jpg sprawia, że ​​wynik wygląda prawie czarno-biały!

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) - repeater dzielenia pikseli

Funkcja przyjmuje element obrazu i

  1. Dzieli piksele przez 8.
  2. Wykonuje odwracalną zamianę grup pikseli.
  3. Powraca zamiana, jeśli grupa pikseli> = 8.
function E(el){
    var V=document.createElement('canvas')
    var W=V.width=el.width,H=V.height=el.height,C=V.getContext('2d')
    C.drawImage(el,0,0)
    var id=C.getImageData(0,0,W,H),D=id.data,L=D.length,i=L/4,A=[]
    for(;--i;)A[i]=i
    function S(A){
        var L=A.length,x=L>>3,y,t,i=0,s=[]
        if(L<8)return A
        for(;i<L;i+=x)s[i/x]=S(A.slice(i,i+x))
        for(i=4;--i;)y=[6,4,7,5,1,3,0,2][i],t=s[i],s[i]=s[y],s[y]=t
        s=[].concat.apply([],s)
        return s
    }
    var N=C.createImageData(W,H),d=N.data,A=S(A)
    for(var i=0;i<L;i++)d[i]=D[(A[i>>2]*4)+(i%4)]
    C.putImageData(N,0,0)
    el.src=C.canvas.toDataURL()
}

Góry Koła


4

Python 2.7 + PIL, szyfrator kolumn / wierszy

Ta metoda po prostu szyfruje wiersze i kolumny obrazu. Możliwe jest szyfrowanie tylko jednego z wymiarów lub obu. Poza tym kolejność nowego zakodowanego wiersza / kolumny zależy od hasła. Inną możliwością jest szyfrowanie całej tablicy obrazów bez uwzględnienia wymiarów.

Wyniki:

Mieszanie całego obrazu:

wprowadź opis zdjęcia tutaj

wprowadź opis zdjęcia tutaj

Mieszanie kolumn:

wprowadź opis zdjęcia tutaj

wprowadź opis zdjęcia tutaj

Wymieszać rzędy:

wprowadź opis zdjęcia tutaj

wprowadź opis zdjęcia tutaj

Mieszanie zarówno kolumn, jak i wierszy:

wprowadź opis zdjęcia tutaj

wprowadź opis zdjęcia tutaj

Próbowałem również zastosować kilka przebiegów do obrazu, ale wyniki końcowe nie różniły się zbytnio, tylko trudność w odszyfrowaniu.

Kod:

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

Zdjęcie 1: wprowadź opis zdjęcia tutaj

Zdjęcie 2: wprowadź opis zdjęcia tutaj

Kod źródłowy:

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

Mój algorytm stosuje losowanie losowe w jednym kierunku i losowe w drugim (ponieważ oba są odwrotnymi względem siebie), po kilka iteracji, ale każda jest uogólniona w celu podzielenia na dowolną liczbę podgrup zamiast tylko dwóch. W efekcie można utworzyć klucz permutacji z wieloma iteracjami, ponieważ obraz nie zostanie przywrócony bez znajomości dokładnej kolejności losowania riffle i master. Sekwencję można określić za pomocą szeregu liczb całkowitych, przy czym liczby dodatnie reprezentują riffy, a liczby ujemne - wzorce.

Przetasowałem krajobraz kluczem [3, -5, 2, 13, -7]:

Krajobraz 3 -5 2 13 -7

Co ciekawe, niektóre interesujące rzeczy pochodzą z [3, -5], gdzie pozostały artefakty z oryginalnego obrazu:

Krajobraz 3 -5

Oto abstrakcyjny wzór tasowany klawiszem [2, 3, 5, 7, -11, 13, -17]:

Kręgi 2 3 5 7–11 13–17

Jeśli tylko jeden parametr jest niepoprawny w kluczu, odtwarzanie losowe nie przywróci obrazu:

Bad Unuffuff

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.