Susun ulang piksel dalam gambar sehingga tidak dapat dikenali dan kemudian dapatkan kembali

86

Buat program yang dapat mengatur ulang piksel dalam gambar sehingga tidak dapat dikenali. Namun program Anda harus dapat mengubahnya kembali menjadi gambar asli.

Anda dapat menulis dua fungsi - untuk encoding dan decoding, namun satu fungsi yang diterapkan berulang kali memberikan gambar asli (misalnya dalam matematika - f(x) = 1 - x) adalah bonus.

Juga menghasilkan beberapa pola dalam hasil juga memberikan bonus.

Gambar dapat direpresentasikan sebagai array 1D / 2D atau objek gambar jika bahasa Anda mendukungnya. Perhatikan bahwa Anda hanya dapat mengubah urutan piksel!

Akan logis untuk memilih kode pemenang yang menghasilkan gambar yang kurang dapat dikenali, namun saya tidak tahu bagaimana mengukurnya dengan tepat, semua cara yang dapat saya bayangkan dapat ditipu. Karenanya saya memilih pertanyaan ini untuk menjadi kontes popularitas - biarkan pengguna memilih apa jawaban terbaik!

Gambar uji 1 (800 x 422 px): Gambar uji 2 (800 x 480 px): Berikan gambar kode keluaran.

Somnium
sumber
Pertanyaannya adalah cara yang sangat panjang untuk mengatakan "Tulis algoritma enkripsi untuk gambar, yang hasilnya adalah gambar."
David Richerby
3
@ DavidRicherby ... yang menggunakan piksel / warna yang sama. Lima piksel hitam dalam "gambar biasa" -> lima piksel hitam dalam "gambar sandi".
Daniel Beck
2
@ user2992539 Baiklah, dalam hal ini Anda mungkin ingin menyatakan secara eksplisit bahwa ini digunakan sebagai tie-breaker. Kalau tidak, hanya mengatakan itu bonus tidak terlalu berarti.
Martin Ender
3
Pertanyaan ini membuat saya memikirkan peta kucing Arnold . Saya tidak berpikir itu cukup cocok untuk tujuan ini tetapi menarik dengan cara yang sama - mengulangi peta cukup sering membuat Anda kembali ke gambar aslinya.
trichoplax
4
Sekarang di tempat lain di jaringan Stack Exchange: Security.SE dari semua tempat
Gagang Pintu

Jawaban:

58

Python 2.7 (dengan PIL) - Tanpa Pseudorandomness

Saya memecah gambar menjadi 2 oleh 2 blok (mengabaikan sisanya) dan memutar setiap blok dengan 180 derajat, kemudian saya melakukan hal yang sama dengan 3 oleh 3 blok, kemudian 4, dll hingga beberapa parameter BLKSZ. Lalu saya melakukan hal yang sama untuk BLKSZ-1, lalu BLKSZ-2, semuanya kembali ke 3, lalu 2. Metode ini membalikkan dirinya dengan tepat; fungsi unscramble adalah fungsi scramble.

Kode :

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

Bergantung pada ukuran blok, Anda dapat membuat perhitungan memberantas semua kemiripan dengan gambar asli: (BLKSZ = 50) masukkan deskripsi gambar di sini masukkan deskripsi gambar di sini

Atau buat komputasi menjadi efisien: (BLKSZ = 10) masukkan deskripsi gambar di sini masukkan deskripsi gambar di sini

GH Faust
sumber
6
Kedengarannya seperti hasil terbaik jika BLKSZ sekitar setengah dari ukuran gambar. Lagi pula, saya suka algoritma dan untuk BLKSZ kecil itu terlihat seperti seni modern! Keren!
Somnium
11
Saya selalu meneguhkan python.
qwr
Alih-alih mengacak semua nilai dari 2 hingga 50, mungkin Anda harus menggunakan hanya bilangan prima?
Neil
@Neil Mungkin itu akan terlihat lebih acak dan kurang artistik.
Somnium
The BLKSZ = 10landscape benar-benar keren!
wchargin
52

C #, Winform

Sunting Mengubah cara Anda mengisi array koordinat Anda dapat memiliki pola yang berbeda - lihat di bawah

Apakah Anda menyukai pola seperti ini?

Pemandangan

Abstrak

Bonus:

Berteriak Jeritan Diacak

Tukar acak tepat satu kali semua piksel di bagian atas dengan semua piksel di bagian bawah. Ulangi prosedur yang sama untuk menguraikan (bonus).

Kode

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

Periksa 'Kode tidak aman' di properti proyek untuk dikompilasi.

Pola rumit

Berebut

Ubah bagian pertama fungsi kerja, hingga 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();
edc65
sumber
1
Menarik) Saya bertanya-tanya apakah dengan pendekatan yang sama dimungkinkan untuk membuat pola yang lebih rumit dalam output.
Somnium
1
Ide bagus - apa yang terjadi pada garis tengah ketika ada jumlah garis ganjil?
flawr
1
@ flawr, perpecahannya per pixel. Jika ada jumlah piksel ganjil, piksel terakhir tidak tersentuh. Jika ada jumlah baris ganjil, setengah kiri dari baris tengah adalah 'sisi atas' dan separuh kanan adalah 'sisi bawah'.
edc65
1
@ user2992539 Saya pikir Anda dapat membagi lagi - bahkan kotak-kotak. Dengan lebih banyak subdivisi, gambar lebih mudah dikenali.
edc65
7
Seperti versi "Perebutan" Anda!)
Somnium
43

C, kabur acak, mudah dibalik

Terlambat ke pesta. Inilah entri saya!

Metode ini melakukan pengacakan kabur. Saya menyebutnya scramblur . Ini sangat sederhana. Dalam satu lingkaran, ia memilih piksel acak dan kemudian menukarnya dengan piksel terdekat yang dipilih secara acak dalam model kanvas toroidal. Anda menentukan jarak maksimum yang mendefinisikan arti "piksel terdekat" (1 berarti selalu memilih piksel yang berdekatan), jumlah iterasi, dan secara opsional seed number acak. Semakin besar jarak maksimum dan semakin besar jumlah iterasi, semakin kabur hasilnya.

Ia dapat dibalik dengan menetapkan sejumlah iterasi negatif (ini hanyalah kenyamanan antarmuka baris perintah; sebenarnya tidak ada yang namanya iterasi negatif). Secara internal, ia menggunakan LCPRNG 64-bit kustom (generator angka pseudorandom linier kongruensial) dan pra-menghasilkan blok nilai. Tabel ini memungkinkan pengulangan melalui blok maju atau mundur untuk masing-masing pengacakan atau pengacakan.

Demo

Untuk dua gambar pertama, saat Anda menggulir ke bawah, setiap gambar dikaburkan menggunakan offset maksimum yang lebih tinggi: Paling atas adalah gambar asli (mis., Offset 0-piksel), diikuti oleh 1, 2, 4, 8, 16, 32, 64 , 128, dan akhirnya 256. Hitungan iterasi adalah 10⁶ = 1.000.000 untuk semua gambar di bawah.

Untuk dua gambar kedua, setiap gambar dikaburkan menggunakan offset yang semakin rendah - misalnya, paling buram hingga paling tidak kabur - dari offset maksimum 256 ke 0. Nikmati!

Pemandangan Abstrak

Dan untuk dua gambar berikut ini, Anda dapat melihat perkembangan ukuran penuh di sini dan di sini :

Hancur berantakan Simpsons

Kode

Saya meretas ini bersama dalam waktu sekitar satu jam ketika bangun pagi ini dan hampir tidak ada dokumentasi. Saya mungkin kembali dalam beberapa hari dan menambahkan lebih banyak dokumentasi nanti jika orang memintanya.

//=============================================================================
// 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;
}
Todd Lehman
sumber
4
Hanya menggulir melewati jawaban ini, tampak luar biasa
Thomas
1
Jawaban ini sangat tinggi. Apakah Anda pikir Anda dapat memindahkan gambar ekstra (yaitu semuanya kecuali dua gambar uji, sepenuhnya kabur) ke galeri di luar situs?
Tim S.
@TIM. - Selesai! menyusut ke thumbnail kecil.
Todd Lehman
42

Python 3.4

  • Bonus 1: Pembalikan diri: pengulangan mengembalikan gambar asli.
  • Gambar kunci opsional: gambar asli hanya dapat dipulihkan dengan menggunakan gambar kunci yang sama lagi.
  • Bonus 2: Memproduksi pola dalam output: gambar utama diperkirakan dalam piksel yang diacak.

Ketika bonus 2 tercapai, dengan menggunakan gambar kunci tambahan, bonus 1 tidak hilang. Program ini masih terbalik sendiri, asalkan dijalankan dengan gambar kunci yang sama lagi.

Penggunaan standar

Gambar uji 1:

Gambar uji acak 1

Gambar uji 2:

Gambar uji acak 2

Menjalankan program dengan file gambar tunggal karena argumennya menyimpan file gambar dengan piksel yang diacak secara merata di seluruh gambar. Menjalankannya kembali dengan output teracak menyimpan file gambar dengan pengacakan diterapkan lagi, yang mengembalikan yang asli karena proses pengacakan adalah kebalikannya sendiri.

Proses pengacakan adalah terbalik sendiri karena daftar semua piksel dibagi menjadi 2-siklus, sehingga setiap piksel ditukar dengan satu dan hanya satu piksel lainnya. Menjalankannya untuk kedua kali menukar setiap piksel dengan piksel yang pertama kali ditukar, mengembalikan semuanya ke awal. Jika ada jumlah piksel ganjil, akan ada piksel yang tidak bergerak.

Berkat jawaban mfvonh sebagai yang pertama menyarankan 2 siklus.

Penggunaan dengan gambar kunci

Scrambling Test image 1 dengan Test image 2 sebagai key image

Scramble test 1 dengan test 2

Scrambling Test image 2 dengan Test image 1 sebagai key image

Scramble test 2 dengan test 1

Menjalankan program dengan argumen file gambar kedua (gambar kunci) membagi gambar asli ke dalam wilayah berdasarkan gambar kunci. Masing-masing daerah ini dibagi menjadi 2 siklus secara terpisah, sehingga semua pengacakan terjadi di dalam wilayah, dan piksel tidak dipindahkan dari satu daerah ke daerah lain. Ini menyebar piksel di setiap daerah dan daerah menjadi warna berbintik seragam, tetapi dengan warna rata-rata yang sedikit berbeda untuk masing-masing daerah. Ini memberikan perkiraan kasar dari gambar kunci, dalam warna yang salah.

Berjalan kembali menukar pasangan piksel yang sama di setiap wilayah, sehingga setiap wilayah dikembalikan ke keadaan semula, dan gambar secara keseluruhan muncul kembali.

Berkat jawaban edc65 sebagai yang pertama menyarankan untuk membagi gambar menjadi daerah. Saya ingin memperluas ini untuk menggunakan daerah sewenang-wenang, tetapi pendekatan menukar segala sesuatu di wilayah 1 dengan segala sesuatu di wilayah 2 berarti bahwa wilayah tersebut harus berukuran sama. Solusi saya adalah menjaga daerah terisolasi satu sama lain, dan hanya mengocok masing-masing daerah itu sendiri. Karena daerah tidak perlu lagi memiliki ukuran yang sama, menjadi lebih mudah untuk menerapkan daerah berbentuk sewenang-wenang.

Kode

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

Gambar JPEG terbakar

File .jpg diproses dengan sangat cepat, tetapi dengan biaya terlalu panas. Ini meninggalkan gambar setelah terbakar ketika dokumen asli dipulihkan:

jpg membakar

Namun serius, format lossy akan mengakibatkan beberapa warna piksel sedikit berubah, yang dengan sendirinya membuat output menjadi tidak valid. Ketika gambar utama digunakan dan pengocokan piksel dibatasi untuk wilayah, semua distorsi disimpan di dalam wilayah di mana itu terjadi, dan kemudian menyebar secara merata di wilayah itu ketika gambar dikembalikan. Perbedaan dalam distorsi rata-rata antar daerah meninggalkan perbedaan yang terlihat di antara mereka, sehingga daerah yang digunakan dalam proses pengacakan masih terlihat dalam gambar yang dipulihkan.

Mengonversi ke .png (atau format non-lossy) sebelum pengacakan memastikan bahwa gambar yang diacak identik dengan yang asli tanpa pembakaran atau distorsi:

png tanpa luka bakar

Detail kecil

  • Ukuran minimum 256 piksel dikenakan pada kawasan. Jika gambar diizinkan untuk dipecah menjadi daerah yang terlalu kecil, maka gambar asli masih akan terlihat sebagian setelah pengacakan.
  • Jika ada lebih dari satu wilayah dengan jumlah piksel ganjil maka satu piksel dari wilayah kedua dipindahkan ke yang pertama, dan seterusnya. Ini berarti hanya ada satu wilayah dengan jumlah piksel ganjil, jadi hanya satu piksel yang akan tetap tidak teracak.
  • Ada argumen opsional ketiga yang membatasi jumlah wilayah. Menyetel ini ke 2 misalnya akan memberikan gambar acak dua nada. Ini mungkin terlihat lebih baik atau lebih buruk tergantung pada gambar yang terlibat. Jika angka ditentukan di sini, gambar hanya dapat dikembalikan menggunakan nomor yang sama lagi.
  • Jumlah warna yang berbeda dalam gambar asli juga membatasi jumlah wilayah. Jika gambar asli adalah dua nada maka terlepas dari gambar kunci atau argumen ketiga, hanya ada maksimal 2 wilayah.
trichoplax
sumber
2
+1 Tepuk tangan! Samar-samar saya memikirkan hal ini, tetapi merasa terlalu sulit untuk diterapkan.
edc65
1
Ini brilian. Saya mendapat entri, tetapi saya lebih suka entri Anda karena fitur gambar utama.
Todd Lehman
Saya ingin tahu seperti apa kedua gambar ini saling berhadapan satu sama lain: lardlad.com/assets/wallpaper/simpsons1920.jpg dan blogs.nd.edu/oblation/files/2013/09/BreakingBad.jpg (diperkecil menjadi 720x450 atau apapun yang masuk akal, dan tentu saja pra-konversi ke PNG untuk menghindari pembakaran JPEG).
Todd Lehman
2
@ToddLehman algoritma saya dibatasi oleh kebutuhan untuk menjadi kebalikannya sendiri. Jika Anda ingin melihat beberapa pendekatan yang sangat menarik untuk mengacak satu gambar agar menyerupai yang lain, Anda harus melihat American Gothic dalam palet Mona Lisa . Beberapa program itu akan melakukan hal-hal luar biasa dengan gambar yang Anda sebutkan.
trichoplax
2
Fitur gambar utama menempatkan kepala dan bahu ini di atas yang lain.
Jack Aidley
33

Berikut ini adalah transformasi non-acak untuk perubahan

  1. Letakkan semua kolom genap di sebelah kiri dan semua kolom aneh di sebelah kanan.
  2. ulangi nxkali
  3. lakukan hal yang sama untuk baris nykali

Transformasi hampir terbalik sendiri, mengulangi transformasi total size_xkali (dalam arah x) mengembalikan gambar asli. Saya tidak tahu persis matematika, tetapi menggunakan kelipatan integer int(log_2(size_x))menghasilkan pengocokan terbaik dengan gambar hantu terkecil

pegunungan yang dikocok masukkan deskripsi gambar di sini

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

Ini adalah bagaimana langkah pertama 20 iterasi terlihat seperti (nx = ny, perhatikan efek dari resolusi yang berbeda) masukkan deskripsi gambar di sini

DenDenDo
sumber
7
Itu adalah algoritma yang sangat keren. Dan Anda harus benar-benar mendapatkan bonus karena menggunakan pic Lena Söderberg. :)
Todd Lehman
Selalu upvote Lena
24

Mathematica

Ini sangat mudah. Saya memilih 5 * nPixelspasangan koordinat acak dan menukar kedua piksel tersebut (yang sepenuhnya mengaburkan gambar). Untuk menguraikannya saya melakukan hal yang sama secara terbalik. Tentu saja, saya perlu seed PRNG untuk mendapatkan pasangan koordinat yang sama di kedua langkah.

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

Satu-satunya perbedaan antara dua fungsi ini Reverse@di unscramble. Kedua fungsi mengambil objek gambar yang sebenarnya. Anda dapat menggunakannya sebagai berikut:

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

outdan inidentik. Berikut ini scrtampilannya:

masukkan deskripsi gambar di sini masukkan deskripsi gambar di sini

Martin Ender
sumber
4
Bagus! Satu-satunya masalah adalah lebih aman untuk membuat PRNG sendiri, karena jika setelah beberapa waktu Mathematica berpikir untuk mengubah algoritma PRNG, ini tidak akan men-decode gambar lama yang disandikan!
Somnium
1
Bagus. Anda harus dapat mencapai hasil yang sama dengan Permute dan FindPermutation.
DavidC
Saya tidak yakin saya mengerti. Anda dapat memasukkan permutasi tepat yang Anda inginkan sebagai daftar siklus.
DavidC
@ David Carraher Hm, menarik. Bukankah saya harus ingat permutasi asli untuk menggunakan FindPermutation?
Martin Ender
Atau mungkin sesuatu yang {c, a, b}[[{2, 3, 1}]]bisa digunakan?
Somnium
22

C # (+ Bonus untuk Algoritma Simetris)

Ini berfungsi dengan menemukan xsedemikian rupa x^2 == 1 mod (number of pixels in image), dan kemudian mengalikan indeks setiap piksel dengan xuntuk menemukan lokasi baru. Ini memungkinkan Anda menggunakan algoritma yang sama persis untuk mengacak dan mengacak gambar.

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

gambar uji pertama, diacak

gambar uji kedua, diacak

Tim S.
sumber
1
Pandai satu) Apakah akan selalu ada solusi untuk persamaan kongruensi itu?
Somnium
1
@ user2992539 Akan selalu ada solusi sepele, 1(gambar asli) dan modulo-1(gambar terbalik / terbalik). Sebagian besar angka memiliki solusi non-sepele, tetapi ada beberapa pengecualian, tampaknya . (terkait dengan faktorisasi utama modulo)
Tim S.
Seperti yang saya mengerti solusi sepele menyebabkan gambar mirip dengan input satu.
Somnium
Benar: 1menampilkan gambar asli, dan -1menampilkan misalnya imgur.com/EiE6VW2
Tim S.
19

C #, terbalik sendiri, tidak ada keacakan

Jika gambar asli memiliki dimensi yang merupakan kekuatan dua, maka setiap baris dan kolom dipertukarkan dengan baris dan kolom yang memiliki pola bit terbalik, misalnya untuk gambar dengan lebar 256 maka baris 0xB4 dipertukarkan dengan baris 0x2D. Gambar dengan ukuran lain dibagi menjadi empat persegi panjang dengan sisi kekuatan 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;
        }
    }
}

Gambar pertama:

Gambar pertama yang diacak

Gambar kedua:

Gambar kedua yang diacak

Neil
sumber
2
Saya suka output "kotak-kotak" yang satu ini.
Brian Rogers
14

C #

Metode yang sama untuk pengacakan dan pengacakan. Saya akan sangat menghargai saran untuk meningkatkan ini.

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

Keluaran menghasilkan kotak-kotak psikedelik Pertama Yang ke dua

Ray Poward
sumber
Bagus bahwa ini memiliki beberapa pola bergaris)
Somnium
1
Bisakah Anda menukar 2 gambar? Dalam pertanyaan gambar gunung adalah yang pertama.
AL
1
Bisakah Anda memasukkan penjelasan singkat tentang algoritma?
trichoplax
14

Python 2 (terbalik sendiri, tidak ada keacakan, peka konteks)

Ini tidak akan memenangkan hadiah untuk "paling tidak dikenali", tapi mungkin itu bisa dinilai sebagai "menarik". :-)

Saya ingin membuat sesuatu yang sensitif terhadap konteks, di mana pengacakan piksel sebenarnya tergantung pada gambar itu sendiri.

Idenya cukup sederhana: Urutkan semua piksel berdasarkan nilai sewenang-wenang yang diperoleh dari warna piksel dan kemudian tukar posisi piksel pertama dalam daftar itu dengan yang terakhir, yang kedua dengan yang kedua ke terakhir, dan seterusnya.

Sayangnya dalam pendekatan sederhana ini ada masalah dengan piksel-piksel dengan warna yang sama, jadi untuk membuatnya tetap terbalik program saya tumbuh sedikit lebih rumit ...

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"

Ini hasilnya: (abs (r-128) + abs (g-128) + abs (b-128)) // 128 (abs (r-128) + abs (g-128) + abs (b-128)) // 128

Anda dapat mencapai hasil yang sangat berbeda dengan mengubah fungsi hash f:

  • r-g-b:

    rgb

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

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

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

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

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

    (r + g + b) // 600

  • 0:

    0

Emil
sumber
3
WOW! Yang ini luar biasa !!! Kerja bagus!
Todd Lehman
1
Itu yang paling menarik sejauh ini. Kerja bagus!
bebe
12

Mathematica (+ bonus)

Ini menciutkan saluran warna dan mengacak gambar sebagai satu daftar panjang data. Hasilnya adalah versi acak yang kurang dapat dikenali karena tidak memiliki distribusi warna yang sama dengan aslinya (karena data itu juga diacak). Ini paling jelas pada gambar kedua yang diacak, tetapi jika Anda melihat lebih dekat, Anda akan melihat efek yang sama di gambar pertama juga. Fungsi adalah kebalikannya sendiri.

Ada komentar bahwa ini mungkin tidak valid karena mengacak per saluran. Saya pikir itu harus, tetapi itu bukan masalah besar. Satu-satunya perubahan yang diperlukan untuk mengacak seluruh piksel (bukan per saluran) akan berubah Flatten @ xmenjadi 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]]

Penjelasan

Menentukan fungsi fyang mengambil array 2 dimensi x. Fungsi ini menggunakan produk dari dimensi gambar sebagai benih acak, dan kemudian meratakan array ke daftar 1 dimensi f(dibayangi secara lokal). Kemudian ia membuat daftar bentuk di {1, 2, ... n}mana npanjangnya f, secara acak mengizinkan daftar itu, mempartisi ke dalam segmen 2 (jadi, misalnya, {{1, 2}, {3, 4}, ...}(menjatuhkan angka terakhir jika dimensi keduanya ganjil), dan kemudian mengizinkan fdengan menukar nilai pada posisi yang ditunjukkan dalam setiap sublist yang baru saja dibuat, dan akhirnya membentuk kembali daftar yang diijinkan kembali ke dimensi asli dari x. Ini mengacak per saluran karena selain mengecilkan dimensi gambar yangFlattenperintah juga menciutkan data saluran di setiap piksel. Fungsi ini kebalikannya sendiri karena siklus masing-masing hanya terdiri dari dua piksel.

Pemakaian

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

f @ img1 // Image

masukkan deskripsi gambar di sini

f @ f @ img1 // Image

masukkan deskripsi gambar di sini

f @ img2 // Image

masukkan deskripsi gambar di sini

f @ f @ img2 // Image

masukkan deskripsi gambar di sini

Di sini menggunakan 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

masukkan deskripsi gambar di sini

mfvonh
sumber
1
Dugaan saya adalah bahwa ini tidak memenuhi kriteria, karena bertukar pada skala yang lebih kecil dari piksel.
trichoplax
1
Saya pikir itu bukan jawaban yang valid, tetapi saya juga sangat menyukainya. Ini twist yang menarik, jadi +1 tetap ...
trichoplax
1
@githubphagocyte Lihat pembaruan :)
mfvonh
Hebat - Saya meraih +1 lagi tapi tentu saja saya tidak bisa melakukannya dua kali ...
trichoplax
1
@githubphagocyte Oh benar, saya lupa sintaks pendek bisa aneh. Iya. f @ f @ img1 // Imageis (dalam sintaks lengkap)Image[f[f[img1]]]
mfvonh
10

Matlab (+ bonus)

Saya pada dasarnya mengganti posisi dua piksel secara acak dan menandai setiap piksel yang telah diaktifkan sehingga tidak akan beralih lagi. Script yang sama dapat digunakan lagi untuk 'dekripsi' karena saya mengatur ulang generator nomor acak setiap kali. Ini dilakukan sampai hampir semua piksel diaktifkan (karena itu ukuran stepsize lebih besar dari 2)

EDIT: Baru saja melihat bahwa Martin Büttner menggunakan pendekatan yang sama - saya tidak bermaksud menyalin ide - saya mulai menulis kode saya ketika tidak ada jawaban, sangat menyesal untuk itu. Saya masih berpikir versi saya menggunakan beberapa ide yang berbeda =) (Dan algoritma saya jauh lebih tidak efisien jika Anda melihat bit di mana dua koordinat acak dipetik ^^)

Gambar-gambar

1 2

Kode

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')
cacat
sumber
Saya tidak sepenuhnya mengerti, apakah kode Anda diterapkan kedua kali pada gambar yang dienkripsi akan mendekripsi?
Somnium
2
Tepat: Setiap kali tepat dua piksel ditukar, dan tidak akan ditukar lagi selama seluruh proses. Karena angka 'acak' keduanya sama persis (karena pengaturan ulang generator angka acak), pasangan piksel akan ditukar kembali. (RNG selalu mengandalkan nomor yang dihasilkan sebelumnya untuk menghasilkan yang berikutnya, saya harap ini jelas.)
flawr
1
Ha, itu sebenarnya ide awal saya, tapi kemudian saya tidak bisa diganggu untuk memastikan setiap piksel ditukar tepat sekali, karena saya harus mulai bekerja. : D +1!
Martin Ender
3
@ user2992539 Yah periksa Oktaf yang merupakan alternatif opensource yang bagus untuk matlab, dan Anda dapat menjalankan 99% kode matlab langsung dalam oktaf.
flawr
2
Saya pikir jika Anda juling sangat keras pada gambar Anda, Anda masih dapat melihat beberapa struktur dari input (yang disebabkan tidak bergerak semua piksel). Saya kira jika Anda mengubah algoritme pilihan Anda untuk berjalan di O (1) alih-alih O (∞), Anda bisa memperbaikinya. ;)
Martin Ender
10

Mathematica-Gunakan permutasi untuk mengacak dan kebalikannya untuk menguraikan.

Gambar jpg adalah susunan {r,g,b}warna piksel tiga dimensi . (3 dimensi menyusun kumpulan piksel berdasarkan baris, kolom, dan warna). Ini dapat diratakan menjadi daftar {r,g,b}tiga kali lipat, kemudian diijinkan sesuai dengan daftar siklus "diketahui", dan akhirnya dirakit kembali ke dalam array dimensi asli. Hasilnya adalah gambar acak.

Unscrambling mengambil gambar yang diacak dan memprosesnya dengan kebalikan dari daftar siklus. Ini menghasilkan, ya, gambar asli.

Jadi satu fungsi (dalam kasus ini, scramble) berfungsi untuk mengacak serta menguraikan piksel dalam suatu gambar.

Sebuah gambar dimasukkan bersama dengan nomor benih (untuk memastikan bahwa generator angka acak akan berada dalam kondisi yang sama untuk pengacakan dan pengacakan). Saat parameter, mundur, salah, fungsi akan berebut. Ketika Benar, fungsi akan terurai.


berebut

Pixel diratakan dan daftar siklus acak dihasilkan. Izinkan menggunakan siklus untuk mengganti posisi piksel dalam daftar yang diratakan.

menguraikan

Fungsi yang sama, scrambledigunakan untuk menguraikan. Namun urutan daftar siklus dibalik.

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

Contohnya

Benih yang sama (37) digunakan untuk pengacakan dan pengacakan.

Ini menghasilkan citra gunung yang acak-acakan. Gambar di bawah ini menunjukkan bahwa variabel scrambledMount dapat diganti dengan gambar sebenarnya dari pemandangan gunung.

scrambledMount=scramble[mountain, 37, True]

mount1


Sekarang kita menjalankan invers; scrambledMount dimasukkan dan gambar aslinya dipulihkan.

 scramble[scrambledMount, 37, True]

mount2


Hal yang sama untuk lingkaran:

lingkaran1


 scramble[scrambledCircles, 37, True]

lingkaran2

DavidC
sumber
Saya tidak bisa melihat bagaimana sebuah gambar bisa menjadi array 3 dimensi.
edc65
1
@ edc65, Baris x Kolom x Warna. Gambar gunung adalah 422 baris dengan 800 kolom dengan 3 warna. Jika array diratakan, itu menghasilkan 1012800 lembar data sebagai array satu dimensi, yaitu sebagai daftar.
DavidC
@ edc65 Saya harus menambahkan bahwa Permute digunakan untuk mengatur ulang warna sebagai rgb tiga kali lipat. Saya tidak meratakannya lagi karena saya tidak tertarik membuat perubahan apa pun dalam daftar warna piksel apa pun. Jika Anda menganggap info rgb, {r, g, b} sebagai elemen, maka kita berbicara tentang array 2D. Dilihat dengan cara ini, sangat masuk akal untuk mengajukan pertanyaan (Bagaimana mungkin gambar menjadi array 3 dimensi?) Yang Anda angkat. Bahkan, mungkin lebih normal untuk menganggap gambar sebagai array 2D, mengabaikan fakta bahwa elemen rgb menambah dimensi lain.
DavidC
10

Python

Saya suka teka-teki ini, ia tampak menarik dan saya datang dengan fungsi Gerakan dan bungkus untuk diterapkan pada gambar.

Terbungkus

Saya membaca gambar sebagai teks (dari kiri ke kanan, atas dan bawah) dan menuliskannya sebagai cangkang siput.

Fungsi ini adalah siklik: ada dalam N, f ^ (n) (x) = x misalnya, untuk gambar 4 * 2, f (f (f (x))) = x

Gerakan

Saya mengambil nomor acak dan memindahkan setiap kolom dan ligne darinya

Kode

# 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

Foto-foto

Rotasi pertama: masukkan deskripsi gambar di sini

lalu permutasi: masukkan deskripsi gambar di sini

Dan dengan rotasi terakhir: masukkan deskripsi gambar di sini

Adapun contoh lainnya: masukkan deskripsi gambar di sini

Faquarl
sumber
2
Bagaimana Anda mengembalikan gambar asli?
trichoplax
Jika hanya rotasi, saya dapat melakukannya dalam jumlah waktu tertentu (tergantung ukurannya). Namun, jika saya memiliki permutasi, saya tidak yakin apakah itu siklik jadi saya hanya memiliki fungsi kedua yang hanya berubah adalah apa yang PM2 [_i, _j] = PM1 [i, j] menjadi PM2 [i, j] = PM1 [ _i, _j] dan PM3 [(i + a)% w, (j + a)% h] = PM2 [i, j] menjadi PM3 [(ia))% w, (ja)% h] = PM2 [i, j]. Saya mencari cara untuk melakukannya tanpa mengubah dua baris ini
Faquarl
8

VB.NET (+ bonus)

Ini menggunakan ide flawr, terima kasih kepadanya, namun ini menggunakan algoritma swapping dan checking yang berbeda. Program mengkodekan dan mendekode dengan cara yang sama.

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

Output gambar:

Somnium
sumber
8

Setelah diingatkan bahwa ini akan menukar piksel dan tidak mengubahnya, berikut ini solusi saya untuk ini:

Diacak: masukkan deskripsi gambar di sini

Pulih: masukkan deskripsi gambar di sini

Ini dilakukan dengan mengacak urutan piksel, tetapi untuk dapat mengembalikannya, pengacakan diperbaiki. Ini dilakukan dengan menggunakan pseudo-random dengan seed tetap dan menghasilkan daftar indeks yang menggambarkan piksel mana yang akan ditukar. Karena swap akan sama, daftar yang sama akan mengembalikan gambar aslinya.

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

Perhatikan bahwa menggunakan algoritma ini pada format kompresi lossy tidak akan menghasilkan hasil yang sama, karena format gambar akan mengubah data. Ini harus bekerja dengan baik dengan codec loss-less seperti PNG.

Dua
sumber
8

Mathematica

Kami mendefinisikan fungsi pembantu hdan fungsi pengacak scramblesebagai:

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

Setelah memuat gambar, Anda dapat memanggil bilangan bulat scramble[img, k]mana ksaja, untuk mengacak gambar. Menelepon lagi dengan -kakan menguraikan. (Jika kini 0, maka tidak ada perubahan yang dibuat.) Biasanya kharus dipilih menjadi sesuatu seperti 100, yang memberikan gambar yang cukup bergegas:

Contoh output 1

Contoh output 2

Frxstrem
sumber
7

Matlab: Perebutan Baris dan Kolom berdasarkan pada invariances jumlah baris / kolom

Ini tampak seperti teka-teki yang menyenangkan, jadi saya memikirkannya dan membuat fungsi berikut. Ini didasarkan pada invariansi jumlah nilai piksel baris dan kolom selama pemindahan sirkuler: ia menggeser setiap baris, lalu setiap kolom, dengan jumlah total nilai piksel baris / kolom (dengan asumsi uint8 untuk seluruh angka dalam variabel-shift) ). Ini kemudian dapat dibalik dengan menggeser setiap kolom lalu baris dengan nilai penjumlahannya di arah yang berlawanan.

Ini tidak secantik yang lain, tapi saya suka itu tidak acak dan sepenuhnya ditentukan oleh gambar - tidak ada parameter pemilihan.

Saya awalnya mendesainnya untuk menggeser setiap saluran warna secara terpisah, tetapi kemudian saya perhatikan spesifikasinya hanya memindahkan piksel penuh.

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

Gambar uji pertama Secont gambar uji

Hugh Nolan
sumber
6

Jawa

Program ini secara acak menukar piksel (membuat pemetaan piksel ke piksel), tetapi alih-alih fungsi acak ia menggunakan Math.sin () (integer x). Ini sepenuhnya reversibel. Dengan gambar uji itu menciptakan beberapa pola.

Parameter: angka integer (jumlah lintasan, angka negatif untuk dibalik, 0 tidak melakukan apa-apa), input mage dan gambar output (bisa sama). File output harus dalam format yang menggunakan kompresi lossless.

1 lulus: masukkan deskripsi gambar di sini masukkan deskripsi gambar di sini

100 berlalu (dibutuhkan beberapa menit untuk melakukannya): masukkan deskripsi gambar di sini masukkan deskripsi gambar di sini

Kode:

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;
        }
    }
}
}
barteks2x
sumber
6

Python 2.7 dengan PIL

Sedikit terlambat ke pesta, tapi saya pikir akan menyenangkan untuk mengubah gambar menjadi plaids (dan tentu saja kembali). Pertama kita menggeser kolom ke atas atau ke bawah sebanyak 4 kali jumlah kolom (bahkan kolom ke bawah, kolom aneh ke atas). Kemudian, kami menggeser baris ke kiri atau kanan sebanyak 4 kali jumlah baris (bahkan kolom kiri, kolom aneh kanan).

Hasilnya cukup Tartanish.

Untuk membalikkan, kami hanya melakukan ini dalam urutan yang berlawanan dan bergeser dengan jumlah yang berlawanan.

Kode

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)

Hasil

Kotak-kotak dari gambar 1:

1.jpg kotak-kotak

Gambar bentuk kotak-kotak 2:

2.png kotak-kotak

jrrl
sumber
2
Sangat bagus! Apakah mungkin untuk mendapatkan diagonal sepanjang sudut 45 °?
Todd Lehman
2
Itu dimungkinkan dengan mengubah garis offset ke: offset = x*xsize/ysize dan offset = y*ysize/xsize Tapi, sayangnya itu tidak menyembunyikan gambar juga, sayangnya.
jrrl
5

Python (+ bonus) - permutasi piksel

Dalam metode ini, setiap piksel akan ditempatkan di posisi lain, dengan batasan bahwa piksel lainnya akan ditempatkan di posisi pertama. Secara matematis, ini adalah permutasi dengan panjang siklus 2. Dengan demikian, metode ini adalah kebalikannya sendiri.

Dalam retrospeksi, ini sangat mirip dengan mfvonh, tetapi pengiriman ini menggunakan Python dan saya harus membangun permutasi itu sendiri.

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

Gambar uji pertama: Gambar uji pertama Gambar uji kedua: Gambar uji kedua

Dave
sumber
5

Python 2.7 + PIL, Inspirasi dari Sliding Puzzles

Punya ide lain. Pada dasarnya, metode ini membagi gambar menjadi blok berukuran sama dan kemudian mengocok pesanan mereka. Karena pesanan baru didasarkan pada benih tetap, dimungkinkan untuk sepenuhnya mengembalikan proses menggunakan benih yang sama. Selain itu, dengan parameter tambahan yang disebut granularity, dimungkinkan untuk mencapai hasil yang berbeda dan tidak dapat dikenali.

Hasil:

Asli

asli

Granularitas 16

16

Granularitas 13

13

Perincian 10

10

Perincian 3

3

Perincian 2

2

Rincian 1

masukkan deskripsi gambar di sini

Asli

asli

Granularitas 16

16

Granularitas 13

13

Perincian 10

10

Perincian 3

3

Perincian 2

2

Rincian 1

1

Kode:

#!/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()
AlexPnt
sumber
5

47

94 baris. 47 untuk encoding, 47 untuk decoding.

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

masukkan deskripsi gambar di sini masukkan deskripsi gambar di sini

(dikonversi ke jpg)

masukkan deskripsi gambar di sini

(asli diturunkan)

masukkan deskripsi gambar di sini

blutorange
sumber
3
Beberapa pola asli terlihat melalui garis-garis itu. Namun itu menyerupai ketika Anda menggambar dengan jari di jendela berkabut).
Somnium
2
... + 184426 byte untuk codegolf-35005_ref.rb?
edc65
5

Matlab dengan sejumput Teori Grup (+ bonus)

Dalam pendekatan ini kami menganggap bahwa kami memiliki jumlah piksel total yang genap. (Jika tidak, kita abaikan saja satu piksel) Jadi kita perlu memilih setengah dari piksel untuk bertukar dengan setengah lainnya. Untuk ini, kami mengindeks semua piksel dari 0hingga 2N-1dan menganggap indeks ini sebagai grup siklik.

Di antara bilangan prima kita mencari angka pyang tidak terlalu kecil dan tidak terlalu besar, dan itu adalah 2Nperintah untuk , kelompok kita. Ini berarti g menghasilkan grup kami atau {k*g mod 2N | k=0,1,...,2N-1} = {0,1,...,2N-1}.

Jadi kami memilih Nkelipatan pertama gsebagai satu set, dan semua indeces yang tersisa sebagai set lainnya, dan hanya menukar set piksel yang sesuai.

Jika pdipilih dengan cara yang benar, set pertama didistribusikan secara merata ke seluruh gambar.

Dua uji kasus:

Sedikit keluar dari topik tetapi menarik:

Selama pengujian saya perhatikan, bahwa jika Anda menyimpannya ke jpg (lossy terkompresi) (bukan png terkompresi lossless) dan menerapkan transformasi bolak-balik, Anda cukup cepat melihat artefak kompresi, ini menunjukkan hasil dari dua pengaturan ulang berturut-turut :

Seperti yang Anda lihat, kompresi jpg membuat hasilnya terlihat hampir hitam dan putih!

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);
cacat
sumber
4

JavaScript (+ bonus) - pixel repeater swap divide pixel

Fungsi mengambil elemen gambar dan

  1. Membagi piksel dengan 8.
  2. Apakah swap grup pixel dapat dibalik.
  3. Pertukaran berulang jika grup piksel> = 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()
}

Pegunungan Lingkaran

wolfhammer
sumber
4

Python 2.7 + PIL, Pengacak Kolom / Baris

Metode ini hanya mengacak baris dan kolom gambar. Dimungkinkan untuk mengacak hanya satu dimensi atau keduanya. Selain itu, urutan baris / kolom orak baru didasarkan pada kata sandi. Juga, kemungkinan lain adalah mengacak seluruh array gambar tanpa mempertimbangkan dimensi.

Hasil:

Mengacak seluruh gambar:

masukkan deskripsi gambar di sini

masukkan deskripsi gambar di sini

Mengacak kolom:

masukkan deskripsi gambar di sini

masukkan deskripsi gambar di sini

Mengacak baris:

masukkan deskripsi gambar di sini

masukkan deskripsi gambar di sini

Mengacak kolom dan baris:

masukkan deskripsi gambar di sini

masukkan deskripsi gambar di sini

Saya juga mencoba menerapkan beberapa run ke gambar, tetapi hasil akhirnya tidak banyak berbeda, hanya kesulitan untuk mendekripsi itu.

Kode:

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")
AlexPnt
sumber
3

C # Winforms

Image1: masukkan deskripsi gambar di sini

Gambar 2: masukkan deskripsi gambar di sini

Kode sumber:

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);
    }
}
Johan du Toit
sumber
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()

Algoritme saya menerapkan riffle shuffle dalam satu arah dan master shuffle di yang lain (karena keduanya adalah invers satu sama lain), masing-masing beberapa iterasi, tetapi masing-masing digeneralisasikan untuk dibagi menjadi sejumlah subkelompok alih-alih hanya dua. Efeknya adalah Anda bisa membuat kunci permutasi multi-iterasi karena gambar tidak akan dipulihkan tanpa mengetahui urutan yang tepat dari riffle dan master shuffles. Urutan dapat ditentukan dengan serangkaian bilangan bulat, dengan angka positif mewakili riffle dan angka negatif mewakili master.

Saya mengocok lanskap dengan tombol [3, -5, 2, 13, -7]:

Lansekap 3 -5 2 13 -7

Yang cukup menarik, beberapa hal menarik terjadi dari [3, -5], di mana beberapa artefak dari gambar asli ditinggalkan:

Lansekap 3 -5

Inilah pola abstrak yang dikocok dengan tombol [2, 3, 5, 7, -11, 13, -17]:

Lingkaran 2 3 5 7 -11 13 -17

Jika salah satu parameter salah dalam kunci, unshuffle tidak akan mengembalikan gambar:

Pembongkaran buruk

Beefster
sumber