[Question]
  [
Create a program which can rearrange pixels in image so it can't be recognized.
However your program should able to convert it back to original image.
You can write two functions - for encoding and decoding, however one function which applied repeatedly gives original image (example in math - `f(x) = 1 - x`) is a bonus.
Also producing some pattern in output gives bonus too.
Image may be represented as 1D/2D array or image object if your language supports it.
Note that you can only change order of pixels!
Will be logical to select as winner code which produces less recognizable image, however I don't know how to measure it exactly, all ways I can imagine can be cheated. Therefore I chose this question to be popularity contest - let users choose what the best answer!
Test image 1 (800 x 422 px):
![](https://i.stack.imgur.com/0RT8s.jpg)
Test image 2 (800 x 480 px):
![](https://i.stack.imgur.com/B5TbK.png)
Please provide with code output image.
  
[Answer]
## Python 2.7 (with PIL) - No Pseudorandomness
I break the image into 2 by 2 blocks (ignoring the remainder) and rotate each block by 180 degrees, then I do the same with 3 by 3 blocks, then 4, etc. up to some parameter BLKSZ. Then I do the same for BLKSZ-1, then BLKSZ-2, all the way back down to 3, then 2. This method reverses itself exactly; the unscramble function is the scramble function.
**The code**:
```
from PIL import Image
import math
im = Image.open("ST1.png", "r")
arr = im.load() #pixel data stored in this 2D array
def rot(A, n, x1, y1): #this is the function which rotates a given block
    temple = []
    for i in range(n):
        temple.append([])
        for j in range(n):
            temple[i].append(arr[x1+i, y1+j])
    for i in range(n):
        for j in range(n):
            arr[x1+i,y1+j] = temple[n-1-i][n-1-j]
xres = 800
yres = 480
BLKSZ = 50 #blocksize
for i in range(2, BLKSZ+1):
    for j in range(int(math.floor(float(xres)/float(i)))):
        for k in range(int(math.floor(float(yres)/float(i)))):
            rot(arr, i, j*i, k*i)
for i in range(3, BLKSZ+1):
    for j in range(int(math.floor(float(xres)/float(BLKSZ+2-i)))):
        for k in range(int(math.floor(float(yres)/float(BLKSZ+2-i)))):
            rot(arr, BLKSZ+2-i, j*(BLKSZ+2-i), k*(BLKSZ+2-i))
im.save("ST1OUT "+str(BLKSZ)+".png")
print("Done!")
```
Depending on the blocksize, you can make the computation eradicate all resemblance to the original image: (BLKSZ = 50)
![enter image description here](https://i.stack.imgur.com/fF5UB.jpg)
![enter image description here](https://i.stack.imgur.com/aqXGG.png)
Or make the computation efficient: (BLKSZ = 10)
![enter image description here](https://i.stack.imgur.com/jmUZM.jpg)
![enter image description here](https://i.stack.imgur.com/fDfQz.png)
[Answer]
# C#, Winform
**Edit** Changing the way you fill the coordinates array you can have different patterns - see below
Do you like this kind of pattern?
![LandScape](https://i.stack.imgur.com/uXGVH.jpg)
![Abstract](https://i.stack.imgur.com/AwbqU.png)
Bonus:
![Scream](https://i.stack.imgur.com/UPZ15.png)
![Scream Scrambled](https://i.stack.imgur.com/ho4Na.png)
Random swap exactly one time all pixels in upper half with all pixels in lower half.
Repeat the same procedure for unscrambling (bonus).
# Code
**Scramble.cs**
```
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Windows.Forms;
using System.Drawing.Imaging;
using System.IO;
namespace Palette
{
    public partial class Scramble : Form
    {
        public Scramble()
        {
            InitializeComponent();
        }
        public struct Coord
        {
            public int x, y;
        }
        
        private void Work(Bitmap srcb, Bitmap outb)
        {
            int w = srcb.Width, h = srcb.Height;
            Coord[] coord = new Coord[w * h];
            FastBitmap fsb = new FastBitmap(srcb);
            FastBitmap fob = new FastBitmap(outb);
            fsb.LockImage();
            fob.LockImage();
            ulong seed = 0;
            int numpix = 0;
            for (int y = 0; y < h; y++)
                for (int x = 0; x < w; numpix++, x++)
                {
                    coord[numpix].x = x;
                    coord[numpix].y = y;
                    uint color = fsb.GetPixel(x, y);
                    seed += color;
                    fob.SetPixel(x, y, color);
                }
            fsb.UnlockImage();
            fob.UnlockImage();
            pbOutput.Refresh();
            Application.DoEvents();
            int half = numpix / 2;
            int limit = half;
            XorShift rng = new XorShift(seed);
            progressBar.Visible = true;
            progressBar.Maximum = limit;
            fob.LockImage();
            while (limit > 0)
            {
                int p = (int)(rng.next() % (uint)limit);
                int q = (int)(rng.next() % (uint)limit);
                uint color = fob.GetPixel(coord[p].x, coord[p].y); 
                fob.SetPixel(coord[p].x, coord[p].y, fob.GetPixel(coord[half+q].x, coord[half+q].y)); 
                fob.SetPixel(coord[half+q].x, coord[half+q].y, color); 
                limit--;
                if (p < limit)
                {
                    coord[p]=coord[limit];
                }
                if (q < limit)
                {
                    coord[half+q]=coord[half+limit];
                }
                if ((limit & 0xfff) == 0)
                {
                    progressBar.Value = limit;
                    fob.UnlockImage();
                    pbOutput.Refresh();
                    fob.LockImage();
                }
            }
            fob.UnlockImage();
            pbOutput.Refresh();
            progressBar.Visible = false; 
        }
        void DupImage(PictureBox s, PictureBox d)
        {
            if (d.Image != null)
                d.Image.Dispose();
            d.Image = new Bitmap(s.Image.Width, s.Image.Height);  
        }
        void GetImagePB(PictureBox pb, string file)
        {
            Bitmap bms = new Bitmap(file, false);
            Bitmap bmp = bms.Clone(new Rectangle(0, 0, bms.Width, bms.Height), PixelFormat.Format32bppArgb);
            bms.Dispose(); 
            if (pb.Image != null)
                pb.Image.Dispose();
            pb.Image = bmp;
        }
        private void btnOpen_Click(object sender, EventArgs e)
        {
            OpenFileDialog openFileDialog = new OpenFileDialog();
            openFileDialog.InitialDirectory = "c:\\temp\\";
            openFileDialog.Filter = "Image Files(*.BMP;*.JPG;*.PNG)|*.BMP;*.JPG;*.PNG|All files (*.*)|*.*";
            openFileDialog.FilterIndex = 1;
            openFileDialog.RestoreDirectory = true;
            if (openFileDialog.ShowDialog() == DialogResult.OK)
            {
                try
                {
                    string file = openFileDialog.FileName;
                    GetImagePB(pbInput, file);
                    pbInput.Tag = file;
                    DupImage(pbInput, pbOutput);
                    Work(pbInput.Image as Bitmap, pbOutput.Image as Bitmap);
                    file = Path.GetDirectoryName(file) + Path.DirectorySeparatorChar + Path.GetFileNameWithoutExtension(file) + ".scr.png";
                    pbOutput.Image.Save(file);
                }
                catch (Exception ex)
                {
                    MessageBox.Show("Error: Could not read file from disk. Original error: " + ex.Message);
                }
            }
        }
    }
    //Adapted from Visual C# Kicks - http://www.vcskicks.com/
    unsafe public class FastBitmap
    {
        private Bitmap workingBitmap = null;
        private int width = 0;
        private BitmapData bitmapData = null;
        private Byte* pBase = null;
        public FastBitmap(Bitmap inputBitmap)
        {
            workingBitmap = inputBitmap;
        }
        public BitmapData LockImage()
        {
            Rectangle bounds = new Rectangle(Point.Empty, workingBitmap.Size);
            width = (int)(bounds.Width * 4 + 3) & ~3;
            //Lock Image
            bitmapData = workingBitmap.LockBits(bounds, ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
            pBase = (Byte*)bitmapData.Scan0.ToPointer();
            return bitmapData;
        }
        private uint* pixelData = null;
        public uint GetPixel(int x, int y)
        {
            pixelData = (uint*)(pBase + y * width + x * 4);
            return *pixelData;
        }
        public uint GetNextPixel()
        {
            return *++pixelData;
        }
        public void GetPixelArray(int x, int y, uint[] Values, int offset, int count)
        {
            pixelData = (uint*)(pBase + y * width + x * 4);
            while (count-- > 0)
            {
                Values[offset++] = *pixelData++;
            }
        }
        public void SetPixel(int x, int y, uint color)
        {
            pixelData = (uint*)(pBase + y * width + x * 4);
            *pixelData = color;
        }
        public void SetNextPixel(uint color)
        {
            *++pixelData = color;
        }
        public void UnlockImage()
        {
            workingBitmap.UnlockBits(bitmapData);
            bitmapData = null;
            pBase = null;
        }
    }
    public class XorShift
    {
        private ulong x; /* The state must be seeded with a nonzero value. */
 
        public XorShift(ulong seed)
        {
            x = seed;
        }
        public ulong next()
        {
            x ^= x >> 12; // a
            x ^= x << 25; // b
            x ^= x >> 27; // c
            return x * 2685821657736338717L;
        }
    }
} 
```
**Scramble.designer.cs**
```
namespace Palette
{
    partial class Scramble
    {
        private System.ComponentModel.IContainer components = null;
        protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }
        private void InitializeComponent()
        {
            this.panel = new System.Windows.Forms.FlowLayoutPanel();
            this.pbInput = new System.Windows.Forms.PictureBox();
            this.pbOutput = new System.Windows.Forms.PictureBox();
            this.progressBar = new System.Windows.Forms.ProgressBar();
            this.btnOpen = new System.Windows.Forms.Button();
            this.panel.SuspendLayout();
            ((System.ComponentModel.ISupportInitialize)(this.pbInput)).BeginInit();
            ((System.ComponentModel.ISupportInitialize)(this.pbOutput)).BeginInit();
            this.SuspendLayout();
            // 
            // panel
            // 
            this.panel.AutoScroll = true;
            this.panel.AutoSize = true;
            this.panel.Controls.Add(this.pbInput);
            this.panel.Controls.Add(this.pbOutput);
            this.panel.Dock = System.Windows.Forms.DockStyle.Top;
            this.panel.Location = new System.Drawing.Point(0, 0);
            this.panel.Name = "panel";
            this.panel.Size = new System.Drawing.Size(748, 306);
            this.panel.TabIndex = 3;
            // 
            // pbInput
            // 
            this.pbInput.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
            this.pbInput.Location = new System.Drawing.Point(3, 3);
            this.pbInput.MinimumSize = new System.Drawing.Size(100, 100);
            this.pbInput.Name = "pbInput";
            this.pbInput.Size = new System.Drawing.Size(100, 300);
            this.pbInput.SizeMode = System.Windows.Forms.PictureBoxSizeMode.AutoSize;
            this.pbInput.TabIndex = 3;
            this.pbInput.TabStop = false;
            // 
            // pbOutput
            // 
            this.pbOutput.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
            this.pbOutput.Location = new System.Drawing.Point(109, 3);
            this.pbOutput.MinimumSize = new System.Drawing.Size(100, 100);
            this.pbOutput.Name = "pbOutput";
            this.pbOutput.Size = new System.Drawing.Size(100, 300);
            this.pbOutput.SizeMode = System.Windows.Forms.PictureBoxSizeMode.AutoSize;
            this.pbOutput.TabIndex = 4;
            this.pbOutput.TabStop = false;
            // 
            // progressBar
            // 
            this.progressBar.Dock = System.Windows.Forms.DockStyle.Bottom;
            this.progressBar.Location = new System.Drawing.Point(0, 465);
            this.progressBar.Name = "progressBar";
            this.progressBar.Size = new System.Drawing.Size(748, 16);
            this.progressBar.TabIndex = 5;
            // 
            // btnOpen
            // 
            this.btnOpen.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
            this.btnOpen.Location = new System.Drawing.Point(12, 429);
            this.btnOpen.Name = "btnOpen";
            this.btnOpen.Size = new System.Drawing.Size(53, 30);
            this.btnOpen.TabIndex = 6;
            this.btnOpen.Text = "Start";
            this.btnOpen.UseVisualStyleBackColor = true;
            this.btnOpen.Click += new System.EventHandler(this.btnOpen_Click);
            // 
            // Scramble
            // 
            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.BackColor = System.Drawing.SystemColors.ControlDark;
            this.ClientSize = new System.Drawing.Size(748, 481);
            this.Controls.Add(this.btnOpen);
            this.Controls.Add(this.progressBar);
            this.Controls.Add(this.panel);
            this.Name = "Scramble";
            this.Text = "Form1";
            this.panel.ResumeLayout(false);
            this.panel.PerformLayout();
            ((System.ComponentModel.ISupportInitialize)(this.pbInput)).EndInit();
            ((System.ComponentModel.ISupportInitialize)(this.pbOutput)).EndInit();
            this.ResumeLayout(false);
            this.PerformLayout();
        }
        private System.Windows.Forms.FlowLayoutPanel panel;
        private System.Windows.Forms.PictureBox pbOutput;
        private System.Windows.Forms.ProgressBar progressBar;
        private System.Windows.Forms.PictureBox pbInput;
        private System.Windows.Forms.Button btnOpen;
    }
}
```
**Program.cs**
```
using System;
using System.Collections.Generic;
using System.Windows.Forms;
namespace Palette
{
  static class Program
  {
    [STAThread]
    static void Main()
    {
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);
        Application.Run(new Scramble());
    }
  }
}
```
Check 'Unsafe code' in project property to compile.
**Complex pattern**
![Scramble](https://i.stack.imgur.com/lQ8Hd.png)
Change the first part of work function, up to 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();
```
[Answer]
## C, arbitrary blurring, easily reversible
Late to the party. Here is my entry!
This method does a scrambling blur. I call it **scramblur**. It is extremely simple. In a loop, it chooses a random pixel and then swaps it with a randomly chosen nearby pixel in a toroidal canvas model. You specify the maximum distance defining what "nearby pixel" means (1 means always choose an adjacent pixel), the number of iterations, and optionally a random number seed. The larger the maximum distance and the larger the number of iterations, the blurrier the result.
It is reversible by specifying a negative number of iterations (this is simply a command-line interface convenience; there is actually no such thing as negative iterations). Internally, it uses a custom 64-bit LCPRNG (linear congruential pseudorandom number generator) and pre-generates a block of values. The table allows looping through the block either forward or reverse for scrambling or unscrambling, respectively.
## Demo
For the first two images, as you scroll down, each image is blurred using a higher maximum offset: Topmost is the original image (e.g., 0-pixel offset), followed by 1, 2, 4, 8, 16, 32, 64, 128, and finally 256. The iteration count is 10⁶ = 1,000,000 for all images below.
For the second two images, each image is blurred using a progressively *lower* offset — e.g., most blurry to least blurry — from a maximum offset of 256 down to 0. Enjoy!
![Landscape](https://i.stack.imgur.com/9DP0z.jpg)
![Abstract](https://i.stack.imgur.com/Ptjmy.jpg)
And for these next two images, you can see the progressions full-size **[here](https://i.stack.imgur.com/KtZvy.jpg)** and **[here](https://i.stack.imgur.com/tChbT.jpg)**:
![Breaking Bad](https://i.stack.imgur.com/se8qZ.png) ![Simpsons](https://i.stack.imgur.com/w4LoP.png)
## Code
I hacked this together in about an hour while waking up this morning and it contains almost no documentation. I might come back in a few days and add more documentation later if people request it.
```
//=============================================================================
// 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;
}
```
[Answer]
# Python 3.4
* **Bonus 1:** Self inverse: repeating restores the original image.
* **Optional key image:** original image can only be restored by using the same key image again.
* **Bonus 2:** Producing pattern in the output: key image is approximated in the scrambled pixels.
When bonus 2 is achieved, by using an additional key image, bonus 1 is not lost. The program is still self inverse, provided it is run with the same key image again.
# Standard usage
### Test image 1:
![Scrambled test image 1](https://i.stack.imgur.com/k5jrq.jpg "Scrambled test image 1")
### Test image 2:
![Scrambled test image 2](https://i.stack.imgur.com/W6jLk.png "Scrambled test image 2")
Running the program with a single image file as its argument saves an image file with the pixels scrambled evenly over the whole image. Running it again with the scrambled output saves an image file with the scrambling applied again, which restores the original since the scrambling process is its own inverse.
The scrambling process is self inverse because the list of all pixels is split into 2-cycles, so that every pixel is swapped with one and only one other pixel. Running it a second time swaps every pixel with the pixel it was first swapped with, putting everything back to how it started. If there are an odd number of pixels there will be one that does not move.
**Thanks to [mfvonh's answer](https://codegolf.stackexchange.com/a/35032/20283) as the first to suggest 2-cycles.**
# Usage with a key image
### Scrambling Test image 1 with Test image 2 as the key image
![Scramble test 1 with test 2](https://i.stack.imgur.com/QIoxu.jpg "Scramble test 1 with test 2")
### Scrambling Test image 2 with Test image 1 as the key image
![Scramble test 2 with test 1](https://i.stack.imgur.com/rqvga.png "Scramble test 2 with test 1")
Running the program with a second image file argument (the key image) divides the original image into regions based on the key image. Each of these regions is divided into 2-cycles separately, so that all of the scrambling occurs within regions, and pixels are not moved from one region to another. This spreads out the pixels over each region and so the regions become a uniform speckled colour, but with a slightly different average colour for each region. This gives a very rough approximation of the key image, in the wrong colours.
Running again swaps the same pairs of pixels in each region, so each region is restored to its original state, and the image as a whole reappears.
**Thanks to [edc65's answer](https://codegolf.stackexchange.com/a/35034/20283) as the first to suggest dividing the image into regions.** I wanted to expand on this to use arbitrary regions, but the approach of swapping everything in region 1 with everything in region 2 meant that the regions had to be of identical size. My solution is to keep the regions insulated from each other, and simply shuffle each region into itself. Since regions no longer need to be of similar size it becomes simpler to apply arbitrary shaped regions.
# Code
```
import os.path
from PIL import Image   # Uses Pillow, a fork of PIL for Python 3
from random import randrange, seed
def scramble(input_image_filename, key_image_filename=None,
             number_of_regions=16777216):
    input_image_path = os.path.abspath(input_image_filename)
    input_image = Image.open(input_image_path)
    if input_image.size == (1, 1):
        raise ValueError("input image must contain more than 1 pixel")
    number_of_regions = min(int(number_of_regions),
                            number_of_colours(input_image))
    if key_image_filename:
        key_image_path = os.path.abspath(key_image_filename)
        key_image = Image.open(key_image_path)
    else:
        key_image = None
        number_of_regions = 1
    region_lists = create_region_lists(input_image, key_image,
                                       number_of_regions)
    seed(0)
    shuffle(region_lists)
    output_image = swap_pixels(input_image, region_lists)
    save_output_image(output_image, input_image_path)
def number_of_colours(image):
    return len(set(list(image.getdata())))
def create_region_lists(input_image, key_image, number_of_regions):
    template = create_template(input_image, key_image, number_of_regions)
    number_of_regions_created = len(set(template))
    region_lists = [[] for i in range(number_of_regions_created)]
    for i in range(len(template)):
        region = template[i]
        region_lists[region].append(i)
    odd_region_lists = [region_list for region_list in region_lists
                        if len(region_list) % 2]
    for i in range(len(odd_region_lists) - 1):
        odd_region_lists[i].append(odd_region_lists[i + 1].pop())
    return region_lists
def create_template(input_image, key_image, number_of_regions):
    if number_of_regions == 1:
        width, height = input_image.size
        return [0] * (width * height)
    else:
        resized_key_image = key_image.resize(input_image.size, Image.NEAREST)
        pixels = list(resized_key_image.getdata())
        pixel_measures = [measure(pixel) for pixel in pixels]
        distinct_values = list(set(pixel_measures))
        number_of_distinct_values = len(distinct_values)
        number_of_regions_created = min(number_of_regions,
                                        number_of_distinct_values)
        sorted_distinct_values = sorted(distinct_values)
        while True:
            values_per_region = (number_of_distinct_values /
                                 number_of_regions_created)
            value_to_region = {sorted_distinct_values[i]:
                               int(i // values_per_region)
                               for i in range(len(sorted_distinct_values))}
            pixel_regions = [value_to_region[pixel_measure]
                             for pixel_measure in pixel_measures]
            if no_small_pixel_regions(pixel_regions,
                                      number_of_regions_created):
                break
            else:
                number_of_regions_created //= 2
        return pixel_regions
def no_small_pixel_regions(pixel_regions, number_of_regions_created):
    counts = [0 for i in range(number_of_regions_created)]
    for value in pixel_regions:
        counts[value] += 1
    if all(counts[i] >= 256 for i in range(number_of_regions_created)):
        return True
def shuffle(region_lists):
    for region_list in region_lists:
        length = len(region_list)
        for i in range(length):
            j = randrange(length)
            region_list[i], region_list[j] = region_list[j], region_list[i]
def measure(pixel):
    '''Return a single value roughly measuring the brightness.
    Not intended as an accurate measure, simply uses primes to prevent two
    different colours from having the same measure, so that an image with
    different colours of similar brightness will still be divided into
    regions.
    '''
    if type(pixel) is int:
        return pixel
    else:
        r, g, b = pixel[:3]
        return r * 2999 + g * 5869 + b * 1151
def swap_pixels(input_image, region_lists):
    pixels = list(input_image.getdata())
    for region in region_lists:
        for i in range(0, len(region) - 1, 2):
            pixels[region[i]], pixels[region[i+1]] = (pixels[region[i+1]],
                                                      pixels[region[i]])
    scrambled_image = Image.new(input_image.mode, input_image.size)
    scrambled_image.putdata(pixels)
    return scrambled_image
def save_output_image(output_image, full_path):
    head, tail = os.path.split(full_path)
    if tail[:10] == 'scrambled_':
        augmented_tail = 'rescued_' + tail[10:]
    else:
        augmented_tail = 'scrambled_' + tail
    save_filename = os.path.join(head, augmented_tail)
    output_image.save(save_filename)
if __name__ == '__main__':
    import sys
    arguments = sys.argv[1:]
    if arguments:
        scramble(*arguments[:3])
    else:
        print('\n'
              'Arguments:\n'
              '    input image          (required)\n'
              '    key image            (optional, default None)\n'
              '    number of regions    '
              '(optional maximum - will be as high as practical otherwise)\n')
```
# JPEG image burn
.jpg files are processed very quickly, but at the cost of running too hot. This leaves a burned in after image when the original is restored:
![jpg burn](https://i.stack.imgur.com/jg31j.jpg "Evidence of jpg overheating")
But seriously, a lossy format will result in some of the pixel colours being changed slightly, which in itself makes the output invalid. When a key image is used and the shuffling of pixels is restricted to regions, all of the distortion is kept within the region it happened in, and then spread out evenly over that region when the image is restored. The difference in average distortion between regions leaves a visible difference between them, so the regions used in the scrambling process are still visible in the restored image.
Converting to .png (or any non-lossy format) before scrambling ensures that the unscrambled image is identical to the original with no burn or distortion:
![png with no burn](https://i.stack.imgur.com/35SUh.png "png with no burn")
# Little details
* A minimum size of 256 pixels is imposed on regions. If the image were allowed to split into regions that are too small, then the original image would still be partially visible after scrambling.
* If there's more than one region with an odd number of pixels then one pixel from the second region is reassigned to the first, and so on. This means there can only ever be one region with an odd number of pixels, and so only one pixel will remain unscrambled.
* There is a third optional argument which restricts the number of regions. Setting this to 2 for example will give two tone scrambled images. This may look better or worse depending on the images involved. If a number is specified here, the image can only be restored using the same number again.
* The number of distinct colours in the original image also limits the number of regions. If the original image is two tone then regardless of the key image or the third argument, there can only be a maximum of 2 regions.
[Answer]
Here is a non-random transform for a change
1. Put all even columns on the left and all odd columns on the right.
2. repeat `nx` times
3. do the same for rows `ny` times
The transformation is almost self-inverse, repeating the transformation a total of `size_x` times (in x-direction) returns the original image. I didn't figure out the exact math, but using integer multiples of `int(log_2(size_x))` produces the best shuffling with the smallest ghost images
![shuffled mountains](https://i.stack.imgur.com/zosp7.jpg)
![enter image description here](https://i.stack.imgur.com/qJSB7.png)
```
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))
```
This is how the first steps 20 iterations look like (nx=ny, note the effect of different resolutions)
![enter image description here](https://i.stack.imgur.com/Tz0BP.jpg)
[Answer]
## Mathematica
This is pretty straightforward. I pick `5 * nPixels` random coordinate pairs and swap those two pixels (which completely obscures the picture). To unscramble it I do the same in reverse. Of course, I need to seed the PRNG to get the same coordinate pairs on both steps.
```
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
   ];
```
The only difference between the two functions is `Reverse@` in `unscramble`. Both functions take an actual image object. You can use them as follows:
```
in = Import["D:\\Development\\CodeGolf\\image-scrambler\\circles.png"]
scr = scramble[im]
out = unscramble[scr]
```
`out` and `in` are identical. Here is what `scr` looks like:
![enter image description here](https://i.stack.imgur.com/bjTqo.jpg)
![enter image description here](https://i.stack.imgur.com/7RLtt.png)
[Answer]
## C# (+ Bonus for Symmetric Algorithm)
This works by finding an `x` such that `x^2 == 1 mod (number of pixels in image)`, and then multiplying each pixel's index by `x` in order to find its new location. This lets you use the exact same algorithm to scramble and unscramble an image.
```
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;
        }
    }
}
```
![first test image, scrambled](https://i.stack.imgur.com/MuZdY.jpg)
![second test image, scrambled](https://i.stack.imgur.com/XeHkl.png)
[Answer]
# C#, self-inverse, no randomness
If the original image has dimensions that are powers of two, then each row and column is exchanged with the row and column that has the reversed bit pattern, for example for a image of width 256 then row 0xB4 is exchanged with row 0x2D. Images of other sizes are split into the rectangles with sides of powers of 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;
        }
    }
}
```
First image:
![Scrambled first image](https://i.stack.imgur.com/1Ciaj.jpg)
Second image:
![Scrambled second image](https://i.stack.imgur.com/nGiei.png)
[Answer]
# Python 2 (self-inverse, no randomness, context-sensitive)
This won't win any prizes for "least recognizable", but maybe it can score as "interesting". :-)
I wanted to make something context-sensitive, where the scrambling of the pixels actually depends on the image itself.
The idea is quite simple: Sort all pixels according to some arbitrary value derived from the pixel's colour and then swap the positions of the first pixel in that list with the last, the second with the second to last, and so on.
Unfortunately in this simple approach there is a problem with pixels of the same colour, so to still make it self-inverse my program grew a bit more complicated...
```
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"
```
This is the result:
![(abs(r-128)+abs(g-128)+abs(b-128))//128](https://i.stack.imgur.com/2WQAV.jpg)
![(abs(r-128)+abs(g-128)+abs(b-128))//128](https://i.stack.imgur.com/goOQ6.png)
You can achieve quite different results by changing the hash function `f`:
* `r-g-b`:
![r-g-b](https://i.stack.imgur.com/bvle5.png)
* `r+g/2.**8+b/2.**16`:
![r+g/2.**8+b/2.**16](https://i.stack.imgur.com/bGJtE.png)
* `math.sin(r+g*2**8+b*2**16)`:
![math.sin(r+g*2**8+b*2**16)](https://i.stack.imgur.com/WgPND.png)
* `(r+g+b)//600`:
![(r+g+b)//600](https://i.stack.imgur.com/GErMI.png)
* `0`:
![0](https://i.stack.imgur.com/7PweL.png)
[Answer]
# C#
Same method for scrambling and unscrambling.
I would appreciate suggestions on improving this.
```
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;
    }
}
```
Outputs results in psychedelic plaid
![First one](https://i.stack.imgur.com/9H231.jpg)
![Second one](https://i.stack.imgur.com/agw85.png)
[Answer]
# Mathematica (+ bonus)
This collapses the color channels and scrambles the image as one long list of data. The result is an even less recognizable scrambled version because it does not have the same color distribution as the original (since that data was scrambled too). This is most obvious in the second scrambled image, but if you look closely you will see the same effect in the first as well. The function is its own inverse.
There was a comment that this may not be valid because it scrambles per channel. I think it should be, but it's not a big deal. The only change necessary to scramble whole pixels (instead of per channel) would be to change `Flatten @ x` to `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]]
```
### Explanation
Defines a function `f` that takes a 2-dimensional array `x`. The function uses the product of the image's dimensions as a random seed, and then flattens the array to a 1-dimensional list `f` (locally shadowed). Then it creates a list of the form `{1, 2, ... n}` where `n` is the length of `f`, randomly permutes that list, partitions it into segments of 2 (so, e.g., `{{1, 2}, {3, 4}, ...}` (dropping last number if the dimensions are both odd), and then permutes `f` by swapping the values at the positions indicated in each sublist just created, and finally it reshapes the permuted list back to the original dimensions of `x`. It scrambles per channel because in addition to collapsing the image dimensions the `Flatten` command also collapses the channel data in each pixel. The function is its own inverse because the cycles include only two pixels each.
### Usage
```
img1=Import@"https://i.stack.imgur.com/2C2TY.jpg"//ImageData;
img2=Import@"https://i.stack.imgur.com/B5TbK.png"//ImageData;
f @ img1 // Image
```
![enter image description here](https://i.stack.imgur.com/P513d.jpg)
```
f @ f @ img1 // Image
```
![enter image description here](https://i.stack.imgur.com/dhW1c.jpg)
```
f @ img2 // Image
```
![enter image description here](https://i.stack.imgur.com/CyFJH.png)
```
f @ f @ img2 // Image
```
![enter image description here](https://i.stack.imgur.com/ln0gK.png)
Here's using `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
```
![enter image description here](https://i.stack.imgur.com/iJ7zq.png)
[Answer]
# Matlab (+ bonus)
I basically switch the position of two pixels at random and tag each pixel that has been switched so it will not be switched again. The same script can be used again for the 'decryption' because I reset the random number generator each time. This is done untill almost all pixels are switched (thats why stepsize is greater than 2)
EDIT: Just saw that Martin Büttner used a similar approach - I did not intend to copy the idea - I began writing my code when there were no answers, so sorry for that. I still think my version uses some different ideas=) (And my algorithm is far more inefficient if you look at the bit where the two random coordinates get picked^^)
## Images
![1](https://i.stack.imgur.com/zyrk8.jpg)
![2](https://i.stack.imgur.com/iwUlw.png)
## Code
```
img = imread('shuffle_image2.bmp');
s = size(img)
rand('seed',0)
map = zeros(s(1),s(2));
for i = 1:2.1:s(1)*s(2) %can use much time if stepsize is 2 since then every pixel has to be exchanged
    while true %find two unswitched pixels
        a = floor(rand(1,2) .* [s(1),s(2)] + [1,1]);
        b = floor(rand(1,2) .* [s(1),s(2)] + [1,1]);
        if map(a(1),a(2)) == 0 && map(b(1),b(2)) == 0
            break
        end
    end
    %switch
    map(a(1),a(2)) = 1;
    map(b(1),b(2)) = 1;
    t = img(a(1),a(2),:);
    img(a(1),a(2),:) = img(b(1),b(2),:);
    img(b(1),b(2),:) = t;
end
image(img)
imwrite(img,'output2.png')
```
[Answer]
# Mathematica-Use a permutation to scramble and its inverse to unscramble.
A jpg image is an three-dimensional array of `{r,g,b}` pixel colors. (The 3 dimensions structure the set of pixels by row, column, and color). It can be flattened into a list of `{r,g,b}` triples, then permuted according to a "known" cycle list, and finally re-assembled into an array of the original dimensions. The result is a scrambled image.
Unscrambling takes the scrambled image and processes it with the reverse of the cycle list. It outputs, yes, the original image.
So a single function (in the present case, `scramble`) serves for scrambling as well as unscrambling pixels in an image.
An image is input along with a seed number (to ensure that the Random number generator will be in the same state for scrambling and unscrambling). When the parameter, reverse, is False, the function will scramble. When it is True, the function will unscramble.
---
**scramble**
The pixels are flattened and a random list of cycles is generated.
Permute uses cycles to switch positions of pixels in the flattened list.
**unscramble**
The same function, `scramble` is used for unscrambling.
However the order of the cycle list is reversed.
```
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]]
```
---
## Examples
The same seed (37) is used for scrambling and unscrambling.
This produces the scrambled image of the mountain. The picture below shows that the variable scrambledMount can be substituted by the actual image of the mountain scene.
```
scrambledMount=scramble[mountain, 37, True]
```
![mount1](https://i.stack.imgur.com/EK1i9.png)
---
Now we run the inverse; scrambledMount is entered and the original picture is recuperated.
```
 scramble[scrambledMount, 37, True]
```
![mount2](https://i.stack.imgur.com/pPqzN.png)
---
Same thing for the circles:
![circles1](https://i.stack.imgur.com/EUBR1.png)
---
```
 scramble[scrambledCircles, 37, True]
```
![circles2](https://i.stack.imgur.com/YlBm9.png)
[Answer]
# Python
I like this puzzle, he seemed interesting and I came with a wraped and Movement function to apply on the image.
### Wraped
I read the picture as a text (from left to right, up and down) and write it as a snail shell.
This function is cyclic: there is a n in N, f^(n)(x)=x
for example, for a picture of 4\*2, f(f(f(x)))=x
### Movement
I take a random number and move each column and ligne from it
## Code
```
# Opening and creating pictures
img = Image.open("/home/faquarl/Bureau/unnamed.png")
PM1 = img.load()
(w,h) = img.size
img2 = Image.new( 'RGBA', (w,h), "black") 
PM2 = img2.load()
img3 = Image.new( 'RGBA', (w,h), "black") 
PM3 = img3.load()
# Rotation
k = 0
_i=w-1
_j=h-1
_currentColMin = 0
_currentColMax = w-1
_currentLigMin = 0
_currentLigMax = h-1
_etat = 0
for i in range(w):
    for j in range(h):
        PM2[_i,_j]=PM1[i,j]
        if _etat==0:
            if _currentColMax == _currentColMin:
                _j -= 1
                _etat = 2
            else:
                _etat = 1
                _i -= 1
        elif _etat==1:
            _i -= 1
            if _j == _currentLigMax and _i == _currentColMin:
                _etat = 2
        elif _etat==2:
            _j -= 1
            _currentLigMax -= 1
            if _j == _currentLigMin and _i == _currentColMin:
                _etat = 5
            else:
                _etat = 3
        elif _etat==3:
            _j -= 1
            if _j == _currentLigMin and _i == _currentColMin:
                _etat = 4
        elif _etat==4:
            _i += 1
            _currentColMin += 1
            if _j == _currentLigMin and _i == _currentColMax:
                _etat = 7
            else:
                _etat = 5
        elif _etat==5:
            _i += 1
            if _j == _currentLigMin and _i == _currentColMax:
                _etat = 6
        elif _etat==6:
            _j += 1
            _currentLigMin += 1
            if _j == _currentLigMax and _i == _currentColMax:
                _etat = 1
            else:
                _etat = 7
        elif _etat==7:
            _j += 1
            if _j == _currentLigMax and _i == _currentColMax:
                _etat = 8
        elif _etat==8:
            _i -= 1
            _currentColMax -= 1
            if _j == _currentLigMax and _i == _currentColMin:
                _etat = 3
            else:
                _etat = 1
        k += 1
        if k == w * h:
            i = w
            j = h
# Movement
if w>h:z=w
else:z=h
rand.seed(z)
a=rand.randint(0,h)
for i in range(w):
  for j in range(h):
  if i%2==0:
    PM3[(i+a)%w,(j+a)%h]=PM2[i,j]
  else:
    PM3[(i-a)%w,(j-a)%h]=PM2[i,j]
# Rotate Again
```
## Pictures
First rotation:
![enter image description here](https://i.stack.imgur.com/PcTEf.jpg)
then permutation:
![enter image description here](https://i.stack.imgur.com/srRdm.jpg)
And with the last rotaion:
![enter image description here](https://i.stack.imgur.com/CiZpr.jpg)
As for the other example:
![enter image description here](https://i.stack.imgur.com/vrhCv.png)
[Answer]
# VB.NET (+ bonus)
This uses flawr's idea, thanks to him, however this uses different swapping and checking algorithm. Program encodes and decodes the same way.
```
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 images:
![](https://i.stack.imgur.com/R1z9u.jpg)
![](https://i.stack.imgur.com/sU0ch.png)
[Answer]
After being reminded that this is about to swap pixels and not alter them, here is my solution for this:
Scrambled:
![enter image description here](https://i.stack.imgur.com/s4xT7.jpg)
Restored:
![enter image description here](https://i.stack.imgur.com/N2CD3.jpg)
This is done by randomizing the pixel order, but to be able to restore it, the randomization is fixed. This is done by using a pseudo-random with a fixed seed and generate a list of indexes that describe which pixels to swap. As the swap will be the same, the same list will restore the original image.
```
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]));
    }
  }
}
```
Note that using this algorithm on a lossy compression format will not produce the same result, as the image format will alter the data. This should work fine with any loss-less codec like PNG.
[Answer]
# Mathematica
We define a helper function `h` and the scrambling function `scramble` as:
```
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]
        ]
      ]
    ]
  ];
```
After loading an image, you can call `scramble[img, k]` where `k` is any integer, to scramble the image. Calling again with `-k` will descramble. (If `k` is `0`, then no change is made.) Typically `k` should be chosen to be something like `100`, which gives a pretty scrambled image:
![Example output 1](https://i.stack.imgur.com/NgPhf.jpg)
![Example output 2](https://i.stack.imgur.com/5kFz9.jpg)
[Answer]
# Matlab: Row and Column Scrambling based on row/column sum invariances
This seemed like a fun puzzle, so I had a think about it and came up with the following function. It is based on the invariance of row and column pixel-value sums during circular shifting: it shifts each row, then each column, by the total sum of the row/column's pixel values (assuming a uint8 for whole number in the shift-variable). This can then be reversed by shifting each column then row by their sum-value in the opposite direction. 
It's not as pretty as the others, but I like that it is non-random and fully specified by the image - no choosing parameters.
I originally designed it to shift each color channel separately, but then I noticed the specification to only move full pixels.
```
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);
```
![First test image](https://i.stack.imgur.com/JTzwd.jpg)
![Secont test image](https://i.stack.imgur.com/uM9WW.png)
[Answer]
# Java
This program randomly swaps pixels (creates pixel to pixel mapping), but instead of random function it uses Math.sin() (integer x).
It's fully reversible. With test images it creates some patterns.
Parameters:
integer number (number of passes, negative number to reverse, 0 does nothing), input mage and output image (can be the same).
Output file should be in format that uses lossless compression.
1 pass:
![enter image description here](https://i.stack.imgur.com/28CZe.jpg)
![enter image description here](https://i.stack.imgur.com/PsdAn.png)
100 passes (it takes a few minutes to do it):
![enter image description here](https://i.stack.imgur.com/dfVSe.jpg)
![enter image description here](https://i.stack.imgur.com/a4c1X.png)
Code:
```
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
public class Test{
public static void main(String... args) {
    String in = "image.png";
    String out = in;
    int passes = 0;
    if (args.length < 1) {
        System.out.println("no paramem encryptimg, 1 pass, reading and saving image.png");
        System.out.println("Usage: pass a number. Negative - n passes of decryption, positive - n passes of encryption, 0 - do nothing");
    } else {
        passes = Integer.parseInt(args[0]);
        if (args.length > 1) {
            in = args[1];
        }
        if(args.length > 2){
            out = args[2];
        }
    }
    boolean encrypt = passes > 0;
    passes = Math.abs(passes);
    for (int a = 0; a < passes; a++) {
        BufferedImage img = null;
        try {
            img = ImageIO.read(new File(a == 0 ? in : out));
        } catch (IOException e) {
            e.printStackTrace();
            return;
        }
        int pixels[][] = new int[img.getWidth()][img.getHeight()];
        int[][] newPixels = new int[img.getWidth()][img.getHeight()];
        for (int x = 0; x < pixels.length; x++) {
            for (int y = 0; y < pixels[x].length; y++) {
                pixels[x][y] = img.getRGB(x, y);
            }
        }
        int amount = img.getWidth() * img.getHeight();
        int[] list = new int[amount];
        for (int i = 0; i < amount; i++) {
            list[i] = i;
        }
        int[] mapping = new int[amount];
        for (int i = amount - 1; i >= 0; i--) {
            int num = (Math.abs((int) (Math.sin(i) * amount))) % (i + 1);
            mapping[i] = list[num];
            list[num] = list[i];
        }
        for (int xz = 0; xz < amount; xz++) {
            int x = xz % img.getWidth();
            int z = xz / img.getWidth();
            int xzMap = mapping[xz];
            int newX = xzMap % img.getWidth();
            int newZ = xzMap / img.getWidth();
            if (encrypt) {
                newPixels[x][z] = pixels[newX][newZ];
            } else {
                newPixels[newX][newZ] = pixels[x][z];
            }
        }
        BufferedImage newImg = new BufferedImage(img.getWidth(), img.getHeight(), BufferedImage.TYPE_INT_RGB);
        for (int x = 0; x < pixels.length; x++) {
            for (int y = 0; y < pixels[x].length; y++) {
                newImg.setRGB(x, y, newPixels[x][y]);
            }
        }
        try {
            String[] s = out.split("\\.");
            ImageIO.write(newImg, s[s.length - 1],
                    new File(out));
        } catch (IOException e) {
            e.printStackTrace();
            return;
        }
    }
}
}
```
[Answer]
# Python 2.7 with PIL
A little late to the party, but I thought it would be fun to convert the images into plaids (and back of course). First we shift columns up or down by 4 times the columns number (even columns down, odd columns up). Then, we shift rows left or right by 4 times the row number (even columns left, odd columns right).
The result is quite Tartanish.
To reverse, we just do these in the opposite order and shift by the opposite amount.
## Code
```
from PIL import Image
def slideColumn (pix, tpix, x, offset, height):
  for y in range(height):
    tpix[x,(offset+y)%height] = pix[x,y]
def slideRow (pix, tpix, y, offset, width):
  for x in range(width):
    tpix[(offset+x)%width,y] = pix[x,y]
def copyPixels (source, destination, width, height):
  for x in range(width):
    for y in range(height):
      destination[x,y]=source[x,y]
def shuffleHorizontal (img, tmpimg, factor, encoding):
  xsize,ysize = img.size
  pix = img.load()
  tpix = tmpimg.load()
  for y in range(ysize):
    offset = y*factor
    if y%2==0:
      offset = xsize-offset
    offset = (xsize + offset) % xsize
    if encoding:
      slideRow(pix,tpix,y,offset,xsize)
    else:
      slideRow(pix,tpix,y,-offset,xsize)
  copyPixels(tpix,pix,xsize,ysize)
def shuffleVertical (img, tmpimg, factor, encoding):
  xsize,ysize = img.size
  pix = img.load()
  tpix = tmpimg.load()
  for x in range(xsize):
    offset = x*factor
    if x%2==0:
      offset = ysize-offset
    offset = (ysize + offset) % ysize
    if encoding:
      slideColumn(pix,tpix,x,offset,ysize)
    else:
      slideColumn(pix,tpix,x,-offset,ysize)
  copyPixels(tpix,pix,xsize,ysize)
def plaidify (img):
  tmpimg = Image.new("RGB",img.size)
  shuffleVertical(img,tmpimg,4,True)
  shuffleHorizontal(img,tmpimg,4,True)
def deplaidify (img):
  tmpimg = Image.new("RGB",img.size)
  shuffleHorizontal(img,tmpimg,4,False)
  shuffleVertical(img,tmpimg,4,False)
```
## Results
The plaid from image 1:
![the plaidified 1.jpg](https://i.stack.imgur.com/ENDdi.jpg)
The plaid form image 2:
![the plaidified 2.png](https://i.stack.imgur.com/ROjru.png)
[Answer]
## 47
94 lines. 47 for encoding, 47 for 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](http://pastebin.com/xsT7bGbN)
![enter image description here](https://i.stack.imgur.com/KO3HR.jpg)
![enter image description here](https://i.stack.imgur.com/1m9FH.png)
(converted to jpg)
![enter image description here](https://i.stack.imgur.com/ZU8TH.jpg)
(original downscaled)
![enter image description here](https://i.stack.imgur.com/x4A9F.jpg)
[Answer]
# Python (+ bonus) - permutation of the pixels
In this method, every pixel will be placed on another position, with the constraint that the other pixel will be placed on the first position. Mathematically, it is a permutation with cycle length 2. As such, the method is it's own inverse.
In retrospect, it is very similar to mfvonh, but this submission is in Python and I had to construct that permutation myself.
```
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
```
First test image:
![First test image](https://i.stack.imgur.com/yOe0C.jpg)
Second test image:
![Second test image](https://i.stack.imgur.com/ils1u.png)
[Answer]
# Python 2.7 + PIL, Inspiration from the Sliding Puzzles
Just had another idea. Basically, this method divides an image into equal sized blocks and then shuffles their order. Since the new order is based on a fixed seed, it's possible to completely revert the process using the same seed. Besides, with the additional parameter called granularity, it's possible to achieve different and unrecognizable results.
## Results:
### Original
![original](https://i.stack.imgur.com/ss93A.jpg)
### Granularity 16
![16](https://i.stack.imgur.com/erydr.png)
### Granularity 13
![13](https://i.stack.imgur.com/wXcyX.png)
### Granularity 10
![10](https://i.stack.imgur.com/jVh3X.png)
### Granularity 3
![3](https://i.stack.imgur.com/Ln90s.png)
### Granularity 2
![2](https://i.stack.imgur.com/xQLIR.jpg)
### Granularity 1
![enter image description here](https://i.stack.imgur.com/8qstN.jpg)
### Original
![original](https://i.stack.imgur.com/jkSJL.png)
### Granularity 16
![16](https://i.stack.imgur.com/zF44G.png)
### Granularity 13
![13](https://i.stack.imgur.com/KDl3V.png)
### Granularity 10
![10](https://i.stack.imgur.com/bwzGp.png)
### Granularity 3
![3](https://i.stack.imgur.com/MVr3u.png)
### Granularity 2
![2](https://i.stack.imgur.com/Uf4oo.png)
### Granularity 1
![1](https://i.stack.imgur.com/vLJ3S.png)
## Code:
```
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from PIL import Image
import random
def scramble_blocks(im,granularity,password,nshuffle):
    set_seed(password)
    width=im.size[0]
    height=im.size[1]
    block_width=find_block_dim(granularity,width)       #find the possible block dimensions
    block_height=find_block_dim(granularity,height)
    grid_width_dim=width/block_width                #dimension of the grid
    grid_height_dim=height/block_height
    nblocks=grid_width_dim*grid_height_dim          #number of blocks
    print "nblocks: ",nblocks," block width: ",block_width," block height: ",block_height
    print "image width: ",width," image height: ",height
    print "getting all the blocks ..."
    blocks=[]
    for n in xrange(nblocks): #get all the image blocks
        blocks+=[get_block(im,n,block_width,block_height)]
    print "shuffling ..."
    #shuffle the order of the blocks
    new_order=range(nblocks)
    for n in xrange(nshuffle):
        random.shuffle(new_order)
    print "building final image ..."
    new_image=im.copy()
    for n in xrange(nblocks):
        #define the target box where to paste the new block
        i=(n%grid_width_dim)*block_width                #i,j -> upper left point of the target image
        j=(n/grid_width_dim)*block_height
        box = (i,j,i+block_width,j+block_height)    
        #paste it   
        new_image.paste(blocks[new_order[n]],box)
    return new_image
#find the dimension(height or width) according to the desired granularity (a lower granularity small blocks)
def find_block_dim(granularity,dim):
    assert(granularity>0)
    candidate=0
    block_dim=1
    counter=0
    while counter!=granularity:         #while we dont achive the desired granularity
        candidate+=1
        while((dim%candidate)!=0):      
            candidate+=1
            if candidate>dim:
                counter=granularity-1
                break
        if candidate<=dim:
            block_dim=candidate         #save the current feasible lenght
        counter+=1
    assert(dim%block_dim==0 and block_dim<=dim)
    return block_dim
def unscramble_blocks(im,granularity,password,nshuffle):
    set_seed(password)
    width=im.size[0]
    height=im.size[1]
    block_width=find_block_dim(granularity,width)       #find the possible block dimensions
    block_height=find_block_dim(granularity,height)
    grid_width_dim=width/block_width                #dimension of the grid
    grid_height_dim=height/block_height
    nblocks=grid_width_dim*grid_height_dim          #number of blocks
    print "nblocks: ",nblocks," block width: ",block_width," block height: ",block_height
    print "getting all the blocks ..."
    blocks=[]
    for n in xrange(nblocks): #get all the image blocks
        blocks+=[get_block(im,n,block_width,block_height)]
    print "shuffling ..."
    #shuffle the order of the blocks
    new_order=range(nblocks)
    for n in xrange(nshuffle):
        random.shuffle(new_order)
    print "building final image ..."
    new_image=im.copy()
    for n in xrange(nblocks):
        #define the target box where to paste the new block
        i=(new_order[n]%grid_width_dim)*block_width             #i,j -> upper left point of the target image
        j=(new_order[n]/grid_width_dim)*block_height
        box = (i,j,i+block_width,j+block_height)    
        #paste it   
        new_image.paste(blocks[n],box)
    return new_image
#get a block of the image
def get_block(im,n,block_width,block_height):
    width=im.size[0]
    grid_width_dim=width/block_width                        #dimension of the grid
    i=(n%grid_width_dim)*block_width                        #i,j -> upper left point of the target block
    j=(n/grid_width_dim)*block_height
    box = (i,j,i+block_width,j+block_height)
    block_im = im.crop(box)
    return block_im
#set random seed based on the given password
def set_seed(password):
    passValue=0
    for ch in password:                 
        passValue=passValue+ord(ch)
    random.seed(passValue)
if __name__ == '__main__':
    filename="0RT8s.jpg"
    # filename="B5TbK.png"
    password="yOs0ZaKpiS"
    nshuffle=1
    granularity=1
    im=Image.open(filename)
    new_image=scramble_blocks(im,granularity,password,nshuffle)
    new_image.show()
    new_image.save(filename.split(".")[0]+"_puzzled.png")
    new_image=unscramble_blocks(new_image,granularity,password,nshuffle)
    new_image.save(filename.split(".")[0]+"_unpuzzled.png")
    new_image.show()
```
[Answer]
# Matlab with a pinch of Group Theory (+ bonus)
In this approach we assume that we have an even number of total pixels. (If not, we just ignore one pixel) So we need to choose half of the pixels to swap with the other half. For this, we index all the pixels from `0` up to `2N-1` and consider these indices as a [cyclic group.](https://en.wikipedia.org/wiki/Cyclic_group) 
Among the primes we search for a number `p` that is not too small and not too big, and that is coprime to `2N`, the order of our group. This means `g` [generates](https://en.wikipedia.org/wiki/Generating_set_of_a_group) our group or `{k*g mod 2N | k=0,1,...,2N-1} = {0,1,...,2N-1}`.
So we choose the first `N` multiples of `g` as one set, and all the remaining indeces as the other set, and just swap the corresponding set of pixels. 
If `p` is chosen the right way, the first set is evenly distributed over the whole image.
The two test cases:
![](https://i.stack.imgur.com/yjyAH.jpg)![](https://i.stack.imgur.com/ydBYb.png)
### Slightly off topic but interesting:
During testing I noticed, that if you save it to a (lossy compressed) jpg (instead of a losslessly compressed png) and apply the transformation back and forth, you quite quickly see artefacts of the compression, this shows the results of two consecutive rearrangements:
![](https://i.stack.imgur.com/E14Yq.png)
![](https://i.stack.imgur.com/M9c1L.jpg)
![](https://i.stack.imgur.com/fbl7E.jpg)
As you can see, the jpg compression makes the result look almost black and white!
```
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);
```
[Answer]
# JavaScript (+bonus) - pixel divide swap repeater
> 
> The function takes an image element and
> 
> 
> 1. Divides the pixels by 8.
> 2. Does a reversible swap of pixel groups.
> 3. Recurses swapping if the pixel group >= 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()
}
```
![Mountains](https://i.stack.imgur.com/CpNjI.jpg)
![Circles](https://i.stack.imgur.com/7iI50.png)
[Answer]
# Python 2.7 + PIL, Column/Row Scrambler
This method simply scrambles the rows and columns of the image. It's possible to scramble only one of the dimensions or both. Besides, the order of the new scrambled row/column is based on a password. Also, another possibility is scrambling the entire image array without considering the dimensions.
## Results:
### Scrambling the entire image:
![enter image description here](https://i.stack.imgur.com/ubBH2.jpg)
![enter image description here](https://i.stack.imgur.com/BI0oE.png)
### Scrambling the columns:
![enter image description here](https://i.stack.imgur.com/Axx9A.jpg)
![enter image description here](https://i.stack.imgur.com/4dy7r.png)
### Scrambling the rows:
![enter image description here](https://i.stack.imgur.com/uAN4P.png)
![enter image description here](https://i.stack.imgur.com/c94sd.png)
### Scrambling both columns and rows:
![enter image description here](https://i.stack.imgur.com/lO55c.jpg)
![enter image description here](https://i.stack.imgur.com/Ft4AW.png)
I also tried to apply several runs to the image, but the end results didn't differ much, only the difficulty to decrypt it. 
## Code:
```
from PIL import Image
import random,copy
def scramble(im,columns,rows):
    pixels =list(im.getdata())
    newOrder=range(columns*rows)        
    random.shuffle(newOrder)            #shuffle
    newpixels=copy.deepcopy(pixels)
    for i in xrange(len(pixels)):
        newpixels[i]=pixels[newOrder[i]]
    im.putdata(newpixels)
def unscramble(im,columns,rows):
    pixels =list(im.getdata())
    newOrder=range(columns*rows)        
    random.shuffle(newOrder)            #unshuffle
    newpixels=copy.deepcopy(pixels)
    for i in xrange(len(pixels)):
        newpixels[newOrder[i]]=pixels[i]
    im.putdata(newpixels)
def scramble_columns(im,columns,rows):
    pixels =list(im.getdata())
    newOrder=range(columns)     
    random.shuffle(newOrder)            #shuffle
    newpixels=[]
    for i in xrange(rows):
        for j in xrange(columns):
            newpixels+=[pixels[i*columns+newOrder[j]]]
    im.putdata(newpixels)
def unscramble_columns(im,columns,rows):
    pixels =list(im.getdata())
    newOrder=range(columns)     
    random.shuffle(newOrder)            #shuffle
    newpixels=copy.deepcopy(pixels)
    for i in xrange(rows):
        for j in xrange(columns):
            newpixels[i*columns+newOrder[j]]=pixels[i*columns+j]
    im.putdata(newpixels)
def scramble_rows(im,columns,rows):
    pixels =list(im.getdata())
    newOrder=range(rows)        
    random.shuffle(newOrder)            #shuffle the order of pixels
    newpixels=copy.deepcopy(pixels)
    for j in xrange(columns):
        for i in xrange(rows):
            newpixels[i*columns+j]=pixels[columns*newOrder[i]+j]
    im.putdata(newpixels)
def unscramble_rows(im,columns,rows):
    pixels =list(im.getdata())
    newOrder=range(rows)        
    random.shuffle(newOrder)            #shuffle the order of pixels
    newpixels=copy.deepcopy(pixels)
    for j in xrange(columns):
        for i in xrange(rows):
            newpixels[columns*newOrder[i]+j]=pixels[i*columns+j]
    im.putdata(newpixels)
#set random seed based on the given password
def set_seed(password):
    passValue=0
    for ch in password:                 
        passValue=passValue+ord(ch)
    random.seed(passValue)
def encrypt(im,columns,rows,password):
    set_seed(password)
    # scramble(im,columns,rows)
    scramble_columns(im,columns,rows)
    scramble_rows(im,columns,rows)
def decrypt(im,columns,rows,password):
    set_seed(password)
    # unscramble(im,columns,rows)
    unscramble_columns(im,columns,rows)
    unscramble_rows(im,columns,rows)
if __name__ == '__main__':
    passwords=["yOs0ZaKpiS","NA7N3v57og","Nwu2T802mZ","6B2ec75nwu","FP78XHYGmn"]
    iterations=1
    filename="0RT8s.jpg"
    im=Image.open(filename)
    size=im.size
    columns=size[0]
    rows=size[1]
    for i in range(iterations):
        encrypt(im,columns,rows,passwords[i])
    im.save(filename.split(".")[0]+"_encrypted.jpg")
    for i in range(iterations):
        decrypt(im,columns,rows,passwords[iterations-i-1])
    im.save(filename.split(".")[0]+"_decrypted.jpg")
```
[Answer]
## C# Winforms
Image1:
[![enter image description here](https://i.stack.imgur.com/A3faP.png)](https://i.stack.imgur.com/A3faP.png)
Image 2:
[![enter image description here](https://i.stack.imgur.com/zQYc5.png)](https://i.stack.imgur.com/zQYc5.png)
Source code:
```
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);
    }
}
```
[Answer]
# 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()
```
My algorithm applies the riffle shuffle in one direction and a master shuffle in the other (since the two are inverses of one another), several iterations each, but each is generalized to split into any number of subgroups instead of just two. The effect is that you could make a multi-iteration permutation key since the image won't be restored without knowing the exact sequence of riffle and master shuffles. A sequence can be specified with a series of integers, with positive numbers representing riffles and negative numbers representing masters.
I shuffled the landscape with the key [3, -5, 2, 13, -7]:
[![Landscape 3 -5 2 13 -7](https://i.stack.imgur.com/k23ni.png)](https://i.stack.imgur.com/k23ni.png)
Interestingly enough, some interesting things happen from [3, -5], where some artifacts from the original image are left over:
[![Landscape 3 -5](https://i.stack.imgur.com/kBeLV.png)](https://i.stack.imgur.com/kBeLV.png)
Here's the abstract pattern shuffled with the key [2, 3, 5, 7, -11, 13, -17]:
[![Circles 2 3 5 7 -11 13 -17](https://i.stack.imgur.com/8g7g4.png)](https://i.stack.imgur.com/8g7g4.png)
If just one parameter is wrong in the key, the unshuffle won't restore the image:
[![Bad Unshuffle](https://i.stack.imgur.com/RlLX2.png)](https://i.stack.imgur.com/RlLX2.png)
]