Rearrange pixels in image so it can't be recognized and then get it back
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)
Or make the computation efficient: (BLKSZ = 10)
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?
Bonus:
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
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();
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!
And for these next two images, you can see the progressions full-size here and here:
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;
}