Перестановіть пікселі на зображенні, щоб його не було розпізнано, а потім поверніть його


86

Створіть програму, яка може переставляти пікселі в зображенні, щоб її не можна було розпізнати. Однак ваша програма повинна мати змогу перетворити його на початкове зображення.

Можна написати дві функції - для кодування та декодування, однак одна функція, яка застосовується неодноразово, дає оригінальне зображення (наприклад, з математики - f(x) = 1 - x) - бонус.

Крім того, що деякий зразок у виході також дає бонус.

Зображення може бути представлене як 1D / 2D масив або об'єкт зображення, якщо ваша мова підтримує його. Зауважте, що ви можете змінювати лише порядок пікселів!

Буде логічно вибрати як код переможця, який створює менш впізнаваний образ, однак я не знаю, як його точно виміряти, всі способи, які я можу собі уявити, можуть бути обдурені. Тому я вибрав це питання конкурсом популярності - нехай користувачі вибирають, що найкраще відповісти!

Тестове зображення 1 (800 x 422 px): Тестове зображення 2 (800 x 480 px): Будь ласка, надайте вихідне зображення коду.


Питання - це дуже довгодушний спосіб сказати «Напишіть алгоритм шифрування для зображень, вихід якого - зображення».
Девід Річербі

3
@DavidRicherby…, що використовує ті самі пікселі / кольори. П'ять чорних пікселів на "простому зображенні" -> п'ять чорних пікселів у "зображенні шифру".
Даніель Бек

2
@ user2992539 Гаразд, у такому випадку ви можете чітко вказати, що це використовується як вимикач. Інакше сказати, що це бонус, не має великого сенсу.
Мартін Ендер

3
Це питання змусило мене задуматися про карту котів Арнольда . Я не думаю, що це цілком підходить для цієї мети, але це цікаво так само - повторення карти достатньо разів повертає вас до початкового зображення.
трихоплакс

4
Зараз деінде в мережі Stack Exchange: Security.SE всіх місць
Doorknob

Відповіді:


58

Python 2.7 (з PIL) - відсутність псевдовипадковості

Я розбиваю зображення на 2 на 2 блоки (ігноруючи решту) і обертаю кожен блок на 180 градусів, потім роблю те ж саме з 3 на 3 блоки, потім 4 і т. Д. До деякого параметра BLKSZ. Тоді я роблю те ж саме для BLKSZ-1, потім BLKSZ-2, повністю назад до 3, потім 2. Цей метод точно змінюється; функція розшифровки - це функція скремблювання.

Код :

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

Залежно від розміру блоку, ви можете змусити обчислити викорінити всю схожість із вихідним зображенням: (BLKSZ = 50) введіть тут опис зображення введіть тут опис зображення

Або зробіть обчислення ефективним: (BLKSZ = 10) введіть тут опис зображення введіть тут опис зображення


6
Найкращі результати виглядатимуть, якщо BLKSZ буде розміром приблизно половини зображення. У будь-якому випадку мені подобається алгоритм, і для малого BLKSZ це виглядає як сучасне мистецтво! Класно!
Сомній

11
Я завжди схвалюю пітона.
qwr

Замість того, щоб шифрувати всі значення від 2 до 50, можливо, вам слід використовувати лише прості числа?
Ніл

@Neil Ймовірно, тоді це буде виглядати більш випадково і менш артистично.
Сомній

BLKSZ = 10Пейзаж це дійсно круто!
wchargin

52

C #, Winform

Редагування Змінюючи спосіб заповнення масиву координат, ви можете мати різні шаблони - див. Нижче

Чи подобається вам такий візерунок?

LandScape

Анотація

Бонус:

Кричати Кричали сутички

Випадковий обмін рівно один раз усіма пікселями у верхній половині з усіма пікселями в нижній половині. Повторіть ту саму процедуру для розбещення (бонус).

Код

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

Поставте прапорець "Небезпечний код" у властивості проекту для компіляції.

Складний візерунок

Забій

Змініть першу частину робочої функції, аж до Application.DoEvents:

        int w = srcb.Width, h = srcb.Height;
        string Msg = "Scramble";

        Graphics gr = Graphics.FromImage(outb);

        Font f = new Font("Arial", 100, FontStyle.Bold);
        var size = gr.MeasureString(Msg, f);
        f = new Font("Arial", w / size.Width * 110, FontStyle.Bold);
        size = gr.MeasureString(Msg, f);
        gr.DrawString(Msg, f, new SolidBrush(Color.White), (w - size.Width) / 2, (h - size.Height) / 2);

        gr.Dispose();

        Coord[] coord = new Coord[w * h];
        FastBitmap fsb = new FastBitmap(srcb);
        FastBitmap fob = new FastBitmap(outb);
        fsb.LockImage();
        fob.LockImage();
        ulong seed = 1;
        int numpix = h * w;
        int c1 = 0, c2 = numpix;
        int y2 = h / 2;

        int p2 = numpix/2;

        for (int p = 0; p < p2; p++)
        {
            for (int s = 1; s > -2; s -= 2)
            {
                int y = (p2+s*p) / w;
                int x = (p2+s*p) % w;

                uint d = fob.GetPixel(x, y);
                if (d != 0)
                {
                    c2--;
                    coord[c2].x = x;
                    coord[c2].y = y;
                }
                else
                {
                    coord[c1].x = x;
                    coord[c1].y = y;
                    c1++;
                }
                fob.SetPixel(x, y, fsb.GetPixel(x, y));
            }
        }
        fsb.UnlockImage();
        fob.UnlockImage();
        pbOutput.Refresh();
        Application.DoEvents();

1
Цікаво) Мені цікаво, чи можна з подібним підходом зробити більш складні моделі у виході.
Сомній

1
Хороша ідея - що відбувається із середньою лінією, коли є непарна кількість ліній?
недолік

1
@flawr розділення на піксель. Якщо є непарна кількість пікселів, останній залишається недоторканим. Якщо є непарна кількість рядків, ліва половина середнього ряду - «верхня сторона», а права половина - «нижня сторона».
edc65

1
@ user2992539 Я думаю, ви можете поділити більше - навіть шахову дошку. З більшою кількістю підрозділів зображення є більш впізнаваним.
edc65

7
Подобається вашій версії "Scramble"!)
Somnium

43

C, довільне розмивання, легко оборотне

Пізно на вечірку. Ось мій запис!

Цей спосіб робить розмиту. Я називаю це scramblur . Це надзвичайно просто. У циклі він вибирає випадковий піксель, а потім міняє його випадковим чином обраним пікселем поблизу в тороїдальній моделі полотна. Ви вказуєте максимальну відстань, визначаючи, що означає "піксель поблизу" (1 означає, що завжди вибираєте сусідній піксель), кількість ітерацій і, можливо, набір випадкових чисел. Чим більша максимальна відстань і чим більша кількість ітерацій, тим більше розмивається результат.

Це оборотно, вказавши негативну кількість ітерацій (це просто зручність інтерфейсу командного рядка; насправді немає такого поняття, як негативні ітерації). Всередині нього використовується користувацький 64-розрядний LCPRNG (лінійний вбудований генератор псевдовипадкових чисел) і попередньо генерує блок значень. Таблиця дозволяє циклічно переходити через блок вперед або назад для скремблювання або розбирання відповідно.

Демо

Для перших двох зображень, коли ви прокручуєте вниз, кожне зображення розмивається за допомогою більшого максимального зміщення: Найвище - це вихідне зображення (наприклад, зміщення 0 пікселів), а потім 1, 2, 4, 8, 16, 32, 64 , 128 і, нарешті, 256. Кількість ітерацій становить 10⁶ = 1 000 000 для всіх зображень нижче.

Для двох інших зображень кожне зображення розмивається за допомогою прогресивно нижчого зміщення - наприклад, найбільш розмитого до найменшого розмитого - від максимального зміщення від 256 до 0. Насолоджуйтесь!

Пейзаж Анотація

А для цих наступних двох зображень ви можете побачити прогресії в повному розмірі тут і тут :

Ламати погано Сімпсони

Код

Я зламав це разом приблизно за годину, прокидаючись сьогодні вранці, і це майже не містить документації. Я можу повернутися через кілька днів і додати більше документації пізніше, якщо люди просять.

//=============================================================================
// SCRAMBLUR
//
// This program is a image-processing competition entry which scrambles or
// descrambles an image based on a pseudorandom process.  For more details,
// information, see:
//
//    http://codegolf.stackexchange.com/questions/35005
//
// It is assumed that you have the NETPBM package of image-processing tools
// installed on your system.  This can be obtained from:
//
//    http://netpbm.sourceforge.net/
//
// or by using your system's package manager, e.g., yum, apt-get, port, etc.
//
// Input to the program is a 24-bit PNM image (type "P6").  Output is same.
// Example command-line invocation:
//
// pngtopnm original.png  | scramblur 100  1000000 | pnmtopng >scrambled.png
// pngtopnm scrambled.png | scramblur 100 -1000000 | pnmtopng >recovered.png
//
//
// Todd S. Lehman, July 2014

#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdbool.h>
#include <string.h>

typedef uint8_t uint8;
typedef uint64_t uint64;

//-----------------------------------------------------------------------------
// PIXEL STRUCTURE

#pragma pack(push, 1)
typedef struct
{
  uint8 r, g, b;     // Red, green, and blue color components
}
Pixel;
#pragma pack(pop)

//-----------------------------------------------------------------------------
// IMAGE STRUCTURE

typedef struct
{
  int width;          // Width of image in pixels
  int height;         // Height of image in pixels
  int pixel_count;    // Total number of pixels in image (e.g., width * height)
  int maxval;         // Maximum pixel component value (e.g., 255)
  Pixel *data;        // One-dimensional array of pixels
}
Image;

//-----------------------------------------------------------------------------
// 64-BIT LCG TABLE

static const long lcg64_table_length = 1000000;  // 10⁶ entries => 8 Megabytes

static uint64 lcg64_table[lcg64_table_length];

//-----------------------------------------------------------------------------
// GET 64-BIT LCG VALUE FROM TABLE

uint64 lcg64_get(long const iteration)
{
  return lcg64_table[iteration % lcg64_table_length];
}

//-----------------------------------------------------------------------------
// INITIALIZE 64-BIT LCG TABLE

void lcg64_init(uint64 const seed)
{
  uint64 x = seed;
  for (long iteration = 0; iteration < lcg64_table_length; iteration++)
  {
    uint64 const a = UINT64_C(6364136223846793005);
    uint64 const c = UINT64_C(1442695040888963407);
    x = (x * a) + c;
    lcg64_table[iteration] = x;
  }
}

//-----------------------------------------------------------------------------
// READ BINARY PNM IMAGE

Image image_read(FILE *const file)
{
  Image image = { .data = NULL };

  char *line = NULL;
  size_t linecap = 0;

  // Read image type.  (Currently only P6 is supported here.)
  if (getline(&line, &linecap, file) < 0) goto failure;
  if (strcmp(line, "P6\n") != 0) goto failure;

  // Read width and height of image in pixels.
  {
    if (getline(&line, &linecap, file) < 0) goto failure;
    char *pwidth = &line[0];
    char *pheight = strchr(line, ' ');
    if (pheight != NULL) pheight++; else goto failure;
    image.width = atoi(pwidth);
    image.height = atoi(pheight);
    image.pixel_count = image.width * image.height;
  }

  // Read maximum color value.  (Currently only 255 is supported here.)
  {
    if (getline(&line, &linecap, file) < 0) goto failure;
    image.maxval = atoi(line);
    if (image.maxval != 255)
      goto failure;
  }

  // Allocate image buffer and read image data.
  if (!(image.data = calloc(image.pixel_count, sizeof(Pixel))))
    goto failure;

  if (fread(image.data, sizeof(Pixel), image.pixel_count, file) !=
      image.pixel_count)
    goto failure;

success:
  free(line);
  return image;

failure:
  free(line);
  free(image.data); image.data = NULL;
  return image;
}

//-----------------------------------------------------------------------------
// WRITE BINARY PNM IMAGE

void image_write(const Image image, FILE *const file)
{
  printf("P6\n");
  printf("%d %d\n", image.width, image.height);
  printf("%d\n", image.maxval);
  (void)fwrite(image.data, sizeof(Pixel), image.pixel_count, file);
}

//-----------------------------------------------------------------------------
// DISCARD IMAGE

void image_discard(Image image)
{
  free(image.data);
}

//-----------------------------------------------------------------------------
// SCRAMBLE OR UNSCRAMBLE IMAGE

void image_scramble(Image image,
                    int const max_delta,
                    long const iterations,
                    uint64 const lcg64_seed)
{
  if (max_delta == 0) return;

  int neighborhood1 = (2 * max_delta) + 1;
  int neighborhood2 = neighborhood1 * neighborhood1;

  lcg64_init(lcg64_seed);

  long iteration_start = (iterations >= 0)? 0 : -iterations;
  long iteration_end   = (iterations >= 0)? iterations : 0;
  long iteration_inc   = (iterations >= 0)? 1 : -1;

  for (long iteration = iteration_start;
       iteration != iteration_end;
       iteration += iteration_inc)
  {
    uint64 lcg64 = lcg64_get(iteration);

    // Choose random pixel.
    int pixel_index = (int)((lcg64 >> 0) % image.pixel_count);

    // Choose random pixel in the neighborhood.
    int d2 = (int)((lcg64 >> 8) % neighborhood2);
    int dx = (d2 % neighborhood1) - (neighborhood1 / 2);
    int dy = (d2 / neighborhood1) - (neighborhood1 / 2);
    int other_pixel_index = pixel_index + dx + (dy * image.width);
    while (other_pixel_index < 0)
      other_pixel_index += image.pixel_count;
    other_pixel_index %= image.pixel_count;

    // Swap pixels.
    Pixel t = image.data[pixel_index];
    image.data[pixel_index] = image.data[other_pixel_index];
    image.data[other_pixel_index] = t;
  }
}

//-----------------------------------------------------------------------------
int main(const int argc, char const *const argv[])
{
  int max_delta     = (argc > 1)? atoi(argv[1]) : 1;
  long iterations   = (argc > 2)? atol(argv[2]) : 1000000;
  uint64 lcg64_seed = (argc > 3)? (uint64)strtoull(argv[3], NULL, 10) : 0;

  Image image = image_read(stdin);
  if (!image.data) { fprintf(stderr, "Invalid input\n"), exit(1); }

  image_scramble(image, max_delta, iterations, lcg64_seed);

  image_write(image, stdout);

  image_discard(image);

  return 0;
}

4
Щойно прокручуючи повз цю відповідь, виглядає приголомшливо
Томас

1
Ця відповідь справді висока. Як ви думаєте, ви могли перемістити зайві зображення (тобто все, окрім двох тестових зображень, повністю розмитих) до галереї поза сайтом?
Тім С.

@TimS. - зробили! зменшилися до мініатюрних ескізів.
Тодд Леман

42

Python 3.4

  • Бонус 1: Самоповернення: повторення відновлює вихідний образ.
  • Необов’язкове зображення ключа: відновити оригінальне зображення можна лише за допомогою повторного використання того ж ключового зображення.
  • Бонус 2: Створення шаблону на виході: ключове зображення наближається до скремблірованих пікселів.

Якщо бонус 2 досягнуто, використовуючи додаткове зображення ключа, бонус 1 не втрачається. Програма все ще самообернена, за умови, що вона знову запускається з тим самим ключовим зображенням.

Стандартне використання

Тестове зображення 1:

Скремльоване тестове зображення 1

Тестове зображення 2:

Скремльоване тестове зображення 2

Запуск програми з одним файлом зображення в якості аргументу зберігає файл зображення з пікселями, які рівномірно прошиті по всьому зображенню. Запустивши його знову за допомогою скремтованого виводу, зберігається файл зображення з повторно застосованим скремблюванням, що відновлює оригінал, оскільки процес скремблювання є власним оберненим.

Процес скремблювання є власне оберненим, оскільки список усіх пікселів розбивається на 2-цикли, так що кожен піксель підміняється одним і лише одним іншим пікселем. Запустивши його, міняє кожен піксель тим пікселем, на який він вперше помінявся, і повертає все до того, як воно починалося. Якщо є непарна кількість пікселів, буде такий, який не рухається.

Завдяки відповіді mfvonh як перший запропонував 2-цикли.

Використання з ключовим зображенням

Скремтування тестового зображення 1 із тестовим зображенням 2 як основне зображення

Тест на боротьбу 1 із тестом 2

Скремтування тестового зображення 2 із ключовим зображенням тестового зображення 1

Перевірка тесту 2 з тестом 1

Запуск програми з аргументом другого файлу зображень (ключове зображення) ділить вихідне зображення на регіони на основі ключового зображення. Кожна з цих областей розділена на 2-цикли окремо, так що все скремблювання відбувається в межах регіонів, а пікселі не переміщуються з однієї області в іншу. Це поширює пікселі по кожному регіону, і тому регіони набувають рівномірного крапчастого кольору, але з дещо іншим середнім кольором для кожного регіону. Це дає дуже приблизну апроксимацію ключового зображення в неправильних кольорах.

При повторному запуску змінюється однакові пари пікселів у кожному регіоні, тому кожна область відновлюється до початкового стану, а зображення в цілому з’являється знову.

Завдяки відповіді edc65 як перший запропонував розділити зображення на регіони. Я хотів розширити це на використання довільних регіонів, але підхід заміни всього в регіоні 1 на все в регіоні 2 означав, що регіони повинні бути однакового розміру. Моє рішення полягає в тому, щоб утримувати регіони, ізольовані один від одного, і просто переміщувати кожен регіон у себе. Оскільки регіони більше не мають бути подібними за розмірами, простіше застосовувати регіони довільної форми.

Код

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

.jpg файли обробляються дуже швидко, але ціною занадто гарячих. Це залишає згоріле зображення після відновлення оригіналу:

jpg записати

Але якщо серйозно, то втрачений формат призведе до того, що деякі кольори пікселів будуть трохи змінені, що саме по собі робить вихід недійсним. Якщо використовується ключове зображення і переміщення пікселів обмежується регіонами, всі спотворення зберігаються в межах області, в якій він відбувся, а потім рівномірно розподіляються по цій області, коли зображення відновлено. Різниця в середньому спотворенні між регіонами залишає помітну різницю між ними, тому регіони, використані в процесі скремблювання, все ще помітні у відновленому зображенні.

Перетворення в .png (або будь-який формат, що не втрачає втрату) перед скремблюванням гарантує, що нешифроване зображення буде ідентичним оригіналу без запису чи спотворення:

PNG без опіку

Маленькі деталі

  • Мінімальний розмір 256 пікселів накладений на регіони. Якби зображення було дозволено розділити на занадто малі регіони, то початкове зображення все одно буде частково видно після скремблювання.
  • Якщо є більше однієї області з непарною кількістю пікселів, то один піксель з другого регіону перепризначається першому тощо. Це означає, що може існувати лише одна область з непарною кількістю пікселів, і тому лише один піксель залишатиметься незашифрованим.
  • Є третій необов’язковий аргумент, який обмежує кількість регіонів. Наприклад, встановивши це значення 2, ви отримаєте два тонально скремпованих зображення. Це може виглядати краще або гірше, залежно від зображень, що беруть участь. Якщо тут вказано число, зображення можна відновити лише за допомогою того самого номера.
  • Кількість чітких кольорів у вихідному зображенні також обмежує кількість регіонів. Якщо вихідне зображення є двоколірним, то незалежно від ключового зображення або третього аргументу, може бути лише 2 області.

2
+1 Оплески! Я туманно думав про це, але реалізував це занадто важко.
edc65

1
Це геніально. У мене подано запис, але мені подобається ваш краще через функцію ключового зображення.
Тодд Леман

Мені буде цікаво, як виглядають ці два зображення, націлені один на одного: lardlad.com/assets/wallpaper/simpsons1920.jpg та blogs.nd.edu/oblation/files/2013/09/BreakingBad.jpg (зменшено до 720x450 або все, що має сенс, і звичайно попередньо перетворений на PNG, щоб уникнути запису JPEG)
Тодд Леман

2
@ToddLehman мій алгоритм обмежений необхідністю бути власним оберненим. Якщо ви хочете побачити кілька справді цікавих підходів до переміщення одного зображення, що нагадує інше, вам слід поглянути на американську готику в палітрі Mona Lisa . Деякі з цих програм могли б зробити дивовижні речі із згаданими вами образами.
трихоплакс

2
Ключова особливість зображення ставить цю голову та плечі вище інших.
Джек Едлі

33

Ось невипадкове перетворення для зміни

  1. Поставте всі парні стовпчики зліва та всі непарні стовпці праворуч.
  2. повторити nxрази
  3. зробити те ж саме для рядків nyразів

Трансформація майже самооборотна, повторюючи перетворення в цілому size_xраз (у напрямку x) повертає початкове зображення. Я не з'ясував точну математику, але використання цілих кратних чисел int(log_2(size_x))дає найкращі перетасування з найменшими зображеннями привидів

перетасовували гори введіть тут опис зображення

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

Ось так виглядають перші кроки 20 ітерацій (nx = ny, зверніть увагу на ефект різних дозволів) введіть тут опис зображення


7
Це дійсно класний алгоритм. І ви повинні повністю отримати бонус за використання картинки Lena Söderberg. :)
Тодд Леман

Завжди підтримуйте Лену

24

Математика

Це досить просто. Я вибираю 5 * nPixelsвипадкові пари координат і поміняю місцями ці два пікселі (що повністю затьмарює малюнок). Щоб розшифрувати це, я роблю те саме в зворотному порядку. Звичайно, мені потрібно засіяти PRNG, щоб отримати однакові координатні пари на обох кроках.

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

Єдина різниця між двома функціями - Reverse@в unscramble. Обидві функції приймають власне об’єкт зображення. Ви можете використовувати їх наступним чином:

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

outі inоднакові. Ось як scrвиглядає:

введіть тут опис зображення введіть тут опис зображення


4
Чудово! Проблема полягає лише в тому, що безпечніше зробити PRNG самостійно, адже якщо через деякий час Mathematica думає змінити алгоритм PRNG, це не розшифрує старі закодовані зображення!
Сомній

1
Приємно. Ви повинні мати можливість досягти такого ж результату за допомогою Permute та FindPermutation.
DavidC

Я не впевнений, що розумію. Ви можете ввести точну перестановку, яку ви хочете, як список циклів.
DavidC

@DavidCarraher Гм, цікаво. Хіба я не мав би пам'ятати оригінальну перестановку для використання, FindPermutationхоча?
Мартін Ендер

А може щось, як {c, a, b}[[{2, 3, 1}]]можна використати?
Сомній

22

C # (+ бонус за симетричний алгоритм)

Це працює, знайшовши xтаке, щоx^2 == 1 mod (number of pixels in image) , а потім помноживши індекс кожного пікселя на x, щоб знайти його нове місцезнаходження. Це дозволяє використовувати той самий алгоритм для того, щоб скремпувати та розшифрувати зображення.

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

перший тестовий образ, скремблірований

друге тестове зображення, скрембліровано


1
Розумна одна) Чи завжди знайдеться рішення рівняння конгруентності?
Сомній

1
@ user2992539 Завжди знайдуться тривіальні рішення 1(оригінальне зображення) та modulo-1(перевернене / перевернуте зображення). Більшість номерів мають нетривіальні рішення, але є деякі винятки, здається . (пов'язана з основним факторизацією modulo)
Тім С.

Як я розумію, тривіальні рішення призводять до зображення, подібного до вхідного.
Сомній

Правильно: 1виводить оригінальне зображення та -1виводить напр. Imgur.com/EiE6VW2
Tim S.

19

C #, самообернена, без випадковості

Якщо вихідне зображення має розміри, що мають потужність двох, то кожен рядок і стовпець обмінюються рядком і стовпцем, який має зворотний бітовий малюнок, наприклад для зображення шириною 256, тоді рядок 0xB4 обмінюється на рядок 0x2D. Зображення інших розмірів розбиті на прямокутники зі сторонами потужності 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;
        }
    }
}

Перше зображення:

Перший образ скремблів

Друге зображення:

Накреслене друге зображення


2
Мені подобається вихід "плед" на цьому.
Брайан Роджерс

14

C #

Той самий спосіб скрембінгу та розборювання. Буду вдячний для пропозицій щодо покращення цього.

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

Вихідні результати призводять до психоделічного пледа Перший Другий


Приємно, що в цьому
кольорі

1
Чи можете ви поміняти місцями 2 зображення? У питанні зображення гір є першим.
AL

1
Не могли б ви включити коротке пояснення алгоритму?
трихоплакс

14

Python 2 (самоінверсія, без випадковості, залежність від контексту)

Це не виграє жодних призів за "найменш впізнавану", але, можливо, це може бути оцінено як "цікаво". :-)

Я хотів зробити щось контекстно-залежне, де скремблювання пікселів насправді залежить від самого зображення.

Ідея досить проста: сортуйте всі пікселі за деяким довільним значенням, отриманим від кольору пікселя, а потім поміняйте місцями позиції першого пікселя в цьому списку з останнім, другого з другим на останній тощо.

На жаль, у такому простому підході є проблема з пікселями одного кольору, тому, щоб все-таки зробити його самооберненою, моя програма стала дещо складнішою ...

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"

Це результат: (abs (r-128) + abs (g-128) + abs (b-128)) // 128 (abs (r-128) + abs (g-128) + abs (b-128)) // 128

Ви можете досягти зовсім інших результатів, змінивши хеш-функцію f:

  • r-g-b:

    rgb

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

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

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

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

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

    (r + g + b) // 600

  • 0:

    0


3
ОЦЕ ТАК! Цей чудовий !!! Хороша робота!
Тодд Леман

1
Це найцікавіше досі. Гарна робота!
bebe

12

Математика (+ бонус)

Це згортає кольорові канали і кодує зображення як один довгий список даних. У результаті виходить ще менш впізнавана скремблірована версія, оскільки вона не має такого ж розподілу кольорів, як оригінал (оскільки ці дані також були скрембліровані). Це найбільш очевидно на другому скремтованому зображенні, але якщо ви придивитесь уважніше, ви побачите той же ефект і в першому. Функція - це своя зворотна.

Був коментар, що це може бути неправдивим, оскільки воно котирується на канал. Я думаю, що так і має бути, але це не велика справа. Єдина зміна, необхідна для скремтування цілих пікселів (замість кожного каналу), - це змінити Flatten @ xна 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]]

Пояснення

Визначає функцію, fяка займає двовимірний масив x. Функція використовує добуток розмірів зображення як випадкове насіння, а потім вирівнює масив до одновимірного списку f(локально затіненого). Потім він створює список форми, {1, 2, ... n}де nдовжина f, довільно переставляє цей список, розділяє його на сегменти 2 (так, наприклад, {{1, 2}, {3, 4}, ...}(випадання останнього числа, якщо розміри є непарними), а потім перестановлює fшляхом заміни значень на позиції, зазначені у кожному щойно створеному підсписі, і, нарешті, він перетворює перестановлений список назад до початкових розмірів x. Він кодиться на канал, тому що, крім згортання розмірів зображення,Flattenкоманда також згортає дані каналу в кожному пікселі. Функція є власною оберненою, оскільки цикли включають всього два пікселі.

Використання

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

f @ img1 // Image

введіть тут опис зображення

f @ f @ img1 // Image

введіть тут опис зображення

f @ img2 // Image

введіть тут опис зображення

f @ f @ img2 // Image

введіть тут опис зображення

Ось використання 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

введіть тут опис зображення


1
Я гадаю, що це не відповідає критеріям, оскільки він змінюється на менший, ніж піксельний масштаб.
трихоплакс

1
Я не думаю, що це правильна відповідь, але мені теж дуже подобається. Це захоплюючий поворот, тож +1 все одно ...
trichoplax

1
@githubphagocyte Дивіться оновлення :)
mfvonh

Чудово - я знову дотягнувся до +1, але, звичайно, не можу це зробити двічі ...
trichoplax

1
@githubphagocyte Так, я забуваю, що короткий синтаксис може бути химерним. Так. f @ f @ img1 // Imageis (у повному синтаксисі)Image[f[f[img1]]]
mfvonh

10

Matlab (+ бонус)

Я в основному перемикаю положення двох пікселів навмання та тегую кожен переключений піксель, щоб він не перемикався знову. Цей самий скрипт можна знову використовувати для 'розшифровки', оскільки я щоразу скидаю генератор випадкових чисел. Це робиться до тих пір, поки майже всі пікселі не перемикаються (ось чому крок більше 2)

EDIT: Щойно побачив, що Мартін Бюттнер застосував подібний підхід - я не мав наміру копіювати ідею - я почав писати свій код, коли відповіді не було, тож шкода цього. Я все ще думаю, що моя версія використовує деякі різні ідеї =) (І мій алгоритм набагато неефективніший, якщо подивитися на біт, де обираються дві випадкові координати ^^)

Зображення

1 2

Код

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

Я не повністю розумію, чи ваш код, застосований вдруге на зашифрованому зображенні, розшифрує його?
Сомній

2
Саме так: кожного разу рівно два пікселі заміняються, і вони не будуть замінені знову протягом усього процесу. Оскільки "випадкові" числа обидва рази точно однакові (через скидання генератора випадкових чисел), піксельні пари повернуться назад. (RNG завжди покладається на попереднє згенероване число для генерації наступного, я сподіваюся, це зрозуміло.)
flawr

1
Ха, це насправді була моя початкова ідея, але тоді я не міг потрудитися переконатися, що кожен піксель поміняється рівно один раз, тому що мені довелося працювати. : D +1!
Мартін Ендер

3
@ user2992539 Добре ознайомтеся з Octave, яка є приємною альтернативою openstream для matlab , і ви можете запустити 99% коду matlab безпосередньо в октаві.
недолік

2
Я думаю, якщо ти дуже сильно косишся до своїх фотографій, ти все одно можеш побачити якусь структуру з вхідних даних (що пов'язано з не переміщенням усіх пікселів). Я думаю, якщо ви змінили алгоритм вибору для запуску в O (1) замість O (∞), ви могли б це виправити. ;)
Мартін Ендер

10

Mathematica - Використовуйте перестановку для кодування, а її зворотну до нешифрованої.

Зображення jpg - це тривимірний масив {r,g,b}кольорів пікселів. (3 виміри структурують набір пікселів за рядками, стовпцями та кольорами). Він може бути сплющений до списку {r,g,b}трійки, потім перестановлений відповідно до "відомого" списку циклів і, нарешті, знову зібраний у масив вихідних розмірів. Результат - скремблирований образ.

Розшифровка знімає скрембоване зображення та обробляє його зворотним боком списку циклів. Він виводить, так, оригінальне зображення.

Таким чином, одна функція (в даному випадку scramble) служить для скремблювання, а також розшифровки пікселів на зображенні.

Зображення вводиться разом із початковим номером (щоб гарантувати, що генератор випадкових чисел буде у тому ж стані для скремблювання та розбирання). Коли параметр, зворотний, значення False, функція буде кодируватися. Коли це правда, функція буде скасовуватися.


сутичка

Пікселі вирівнюються і формується випадковий список циклів. Permute використовує цикли для перемикання позицій пікселів у сплющеному списку.

нерозбірливий

Ця ж функція scrambleвикористовується для розборювання. Однак порядок списку циклів змінено.

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

Приклади

Це ж насіння (37) використовується для скремтування та розчісування.

Це створює скремтований образ гори. На малюнку нижче видно, що змінна scrambledMount може бути замінена фактичним зображенням гірської сцени.

scrambledMount=scramble[mountain, 37, True]

кріплення1


Тепер запускаємо зворотну; scrambledMount вводиться і вихідне зображення відновлюється.

 scramble[scrambledMount, 37, True]

mount2


Те саме для кіл:

кола1


 scramble[scrambledCircles, 37, True]

кола2


Я не бачу, як зображення може бути тривимірним масивом.
edc65

1
@ edc65, рядки x стовпці х кольори. Зображення гори складає 422 ряди на 800 стовпців на 3 кольори. Якщо масив сплющений, він видає 1012800 фрагментів даних як одновимірний масив, тобто як список.
DavidC

@ edc65 Я повинен додати, що Permute використовувався для перестановки кольорів як rgb трійки. Я більше не розгладжував це, тому що мені не було цікаво вносити будь-які зміни в список кольорів будь-якого пікселя. Якщо ви розглядаєте інформацію про rgb, {r, g, b} як елемент, то мова йде про 2D-масив. Розглянуте таким чином, має повний сенс поставити питання (як зображення може бути тривимірним масивом?), Яке ви порушили. Насправді, може бути нормальніше розглядати зображення як двовимірний масив, не зважаючи на те, що елементи rgb додають іншого виміру.
DavidC

10

Пітон

Мені подобається ця головоломка, він здався цікавим, і я прийшов із загорненою функцією та рухом, щоб застосувати зображення.

Загорнутий

Я читаю малюнок як текст (зліва направо, вгору та вниз) і записую його як равликову оболонку.

Ця функція є циклічною: є в N, f ^ (n) (x) = x, наприклад, для картини 4 * 2, f (f (f (x)))) = x

Рух

Я беру випадкове число і переміщую з нього кожен стовпчик і ligne

Код

# 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

Картинки

Перша ротація: введіть тут опис зображення

то перестановка: введіть тут опис зображення

І з останньою ротацією: введіть тут опис зображення

Що стосується іншого прикладу: введіть тут опис зображення


2
Як відновити вихідний образ?
трихоплакс

Якщо це просто обертання, я можу це робити певний час (залежить від розміру). Однак, якби у мене були перестановки, я не впевнений, чи це циклічно, тому у мене просто друга функція, яка змінюється лише тим, що PM2 [_i, _j] = PM1 [i, j] став PM2 [i, j] = PM1 [ _i, _j] і PM3 [(i + a)% w, (j + a)% h] = PM2 [i, j] стали PM3 [(ia)% w, (ja)% h] = PM2 [i, j]. Я шукаю спосіб це зробити, не змінюючи цих двох рядків
Faquarl

8

VB.NET (+ бонус)

Завдяки йому використовується ідея flawr, проте він використовує різні алгоритми заміни та перевірки. Програма кодує і декодує так само.

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

Вихідні зображення:


8

Після того, як нагадали, що ось-ось слід міняти пікселі, а не змінювати їх, ось моє рішення для цього:

Зашифровано: введіть тут опис зображення

Відновлено: введіть тут опис зображення

Це робиться шляхом рандомізації порядку пікселів, але, щоб мати можливість відновити його, рандомізація виправлена. Це робиться за допомогою псевдовипадкової з фіксованим насінням та генерує список індексів, які описують, на які пікселі міняти місцями. Оскільки підміна буде однаковою, той самий список відновить початкове зображення.

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

Зауважте, що використання цього алгоритму у форматі стиснення втрат не дасть такого ж результату, оскільки формат зображення змінить дані. Це має добре працювати з будь-яким кодеком без втрат, як PNG.


8

Математика

Ми визначаємо хелперну функцію hта функцію скремблінгу scrambleяк:

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

Завантаживши зображення, ви можете зателефонувати, scramble[img, k]де kє будь-яке ціле число, щоб скрутити зображення. Знову зателефонувавши за допомогою, -kбуде скасовано. (Якщо kє 0, то ніяких змін не відбувається.) Як правило , kслід вибирати що - щось на зразок 100, що дає досить зашифроване зображення:

Приклад виводу 1

Приклад виводу 2


7

Матлаб: скремтування рядків та стовпців на основі інваріацій суми рядків / стовпців

Це здавалося веселою головоломкою, тому я подумав над цим і придумав наступну функцію. Він заснований на інваріантності сум піксельних значень рядків і стовпців під час кругового зміщення: він зміщує кожен рядок, а потім кожен стовпець, на загальну суму значень пікселів рядка / стовпця (якщо вважати uint8 для цілого числа у змінній shift) ). Потім це можна змінити шляхом зміщення кожного стовпця, а потім рядка на їх сумарне значення у зворотному напрямку.

Це не так красиво, як інші, але мені подобається, що воно є невипадковим і повністю задане зображенням - не вибираючи параметрів.

Спочатку я розробив його для зміщення кожного кольорового каналу окремо, але потім помітив специфікацію лише для переміщення повних пікселів.

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

Перше тестове зображення Зображення тесту Secont


6

Java

Ця програма випадковим чином заміняє пікселі (створює відображення пікселів на пікселі), але замість випадкової функції використовує Math.sin () (ціле число x). Це повністю оборотно. За допомогою тестових зображень це створює деякі візерунки.

Параметри: ціле число (кількість проходів, негативне число для зворотного зв’язку, 0 не робить нічого), вхідний маг і вихідний образ (можуть бути однаковими). Вихідний файл повинен бути у форматі, що використовує стиснення без втрат.

1 прохід: введіть тут опис зображення введіть тут опис зображення

100 проходів (на це потрібно кілька хвилин): введіть тут опис зображення введіть тут опис зображення

Код:

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;

public class Test{

public static void main(String... args) {
    String in = "image.png";
    String out = in;
    int passes = 0;
    if (args.length < 1) {
        System.out.println("no paramem encryptimg, 1 pass, reading and saving image.png");
        System.out.println("Usage: pass a number. Negative - n passes of decryption, positive - n passes of encryption, 0 - do nothing");
    } else {
        passes = Integer.parseInt(args[0]);
        if (args.length > 1) {
            in = args[1];
        }
        if(args.length > 2){
            out = args[2];
        }
    }
    boolean encrypt = passes > 0;
    passes = Math.abs(passes);
    for (int a = 0; a < passes; a++) {
        BufferedImage img = null;
        try {
            img = ImageIO.read(new File(a == 0 ? in : out));
        } catch (IOException e) {
            e.printStackTrace();
            return;
        }
        int pixels[][] = new int[img.getWidth()][img.getHeight()];
        int[][] newPixels = new int[img.getWidth()][img.getHeight()];
        for (int x = 0; x < pixels.length; x++) {
            for (int y = 0; y < pixels[x].length; y++) {
                pixels[x][y] = img.getRGB(x, y);
            }
        }
        int amount = img.getWidth() * img.getHeight();
        int[] list = new int[amount];
        for (int i = 0; i < amount; i++) {
            list[i] = i;
        }
        int[] mapping = new int[amount];
        for (int i = amount - 1; i >= 0; i--) {
            int num = (Math.abs((int) (Math.sin(i) * amount))) % (i + 1);
            mapping[i] = list[num];
            list[num] = list[i];
        }
        for (int xz = 0; xz < amount; xz++) {
            int x = xz % img.getWidth();
            int z = xz / img.getWidth();
            int xzMap = mapping[xz];
            int newX = xzMap % img.getWidth();
            int newZ = xzMap / img.getWidth();
            if (encrypt) {
                newPixels[x][z] = pixels[newX][newZ];
            } else {
                newPixels[newX][newZ] = pixels[x][z];
            }
        }
        BufferedImage newImg = new BufferedImage(img.getWidth(), img.getHeight(), BufferedImage.TYPE_INT_RGB);
        for (int x = 0; x < pixels.length; x++) {
            for (int y = 0; y < pixels[x].length; y++) {
                newImg.setRGB(x, y, newPixels[x][y]);
            }
        }

        try {
            String[] s = out.split("\\.");
            ImageIO.write(newImg, s[s.length - 1],
                    new File(out));
        } catch (IOException e) {
            e.printStackTrace();
            return;
        }
    }
}
}

6

Python 2.7 з PIL

Трохи запізнюємось на вечірку, але я думав, що буде весело перетворювати зображення в плед (і назад, звичайно). Спочатку переносимо стовпчики вгору або вниз на 4 рази більше числа стовпців (парні стовпці вниз, непарні стовпчики вгору). Потім зміщуємо рядки вліво або вправо на 4 рази номер рядка (парні стовпчики зліва, непарні стовпчики праворуч).

Результат - досить тартанський.

Щоб повернути назад, ми просто робимо це в протилежному порядку і зміщуємо на протилежну суму.

Код

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)

Результати

Плед із зображення 1:

пледифікований 1.jpg

Зображення форми плед 2:

пледифікований 2.png


2
Дуже хороша! Чи можна дістати діагоналі під кутом 45 °?
Тодд Леман

2
Це можливо, змінивши лінії зміщення на: offset = x*xsize/ysize і, offset = y*ysize/xsize але, на жаль, це зображення також не приховує.
jrrl

5

Python (+ бонус) - перестановка пікселів

У цьому способі кожен піксель буде розміщений в іншій позиції, з тим обмеженням, що інший піксель буде розміщений на першій позиції. Математично це перестановка з довжиною циклу 2. Як такий, метод є власною оберненою.

В ретроспективі він дуже схожий на mfvonh, але це уявлення є в Python, і мені довелося побудувати цю перестановку самостійно.

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

Перше тестове зображення: Перше тестове зображення Друге тестове зображення: Друге тестове зображення


5

Python 2.7 + PIL, натхнення від розсувних головоломок

Просто була інша ідея. В основному цей метод ділить зображення на блоки однакового розміру, а потім переміщує їх порядок. Оскільки новий порядок базується на фіксованому насінні, процес можна повністю відновити за допомогою того ж насіння. Крім того, за допомогою додаткового параметра, який називається деталізація, можна досягти різних та невпізнаваних результатів.

Результати:

Оригінал

оригінальний

Гранулярність 16

16

Гранулярність 13

13

Гранулярність 10

10

Гранулярність 3

3

Гранулярність 2

2

Гранулярність 1

введіть тут опис зображення

Оригінал

оригінальний

Гранулярність 16

16

Гранулярність 13

13

Гранулярність 10

10

Гранулярність 3

3

Гранулярність 2

2

Гранулярність 1

1

Код:

#!/usr/bin/env python
# -*- coding: utf-8 -*-

from PIL import Image
import random

def scramble_blocks(im,granularity,password,nshuffle):
    set_seed(password)
    width=im.size[0]
    height=im.size[1]

    block_width=find_block_dim(granularity,width)       #find the possible block dimensions
    block_height=find_block_dim(granularity,height)

    grid_width_dim=width/block_width                #dimension of the grid
    grid_height_dim=height/block_height

    nblocks=grid_width_dim*grid_height_dim          #number of blocks

    print "nblocks: ",nblocks," block width: ",block_width," block height: ",block_height
    print "image width: ",width," image height: ",height
    print "getting all the blocks ..."
    blocks=[]
    for n in xrange(nblocks): #get all the image blocks
        blocks+=[get_block(im,n,block_width,block_height)]

    print "shuffling ..."
    #shuffle the order of the blocks
    new_order=range(nblocks)
    for n in xrange(nshuffle):
        random.shuffle(new_order)

    print "building final image ..."
    new_image=im.copy()
    for n in xrange(nblocks):
        #define the target box where to paste the new block
        i=(n%grid_width_dim)*block_width                #i,j -> upper left point of the target image
        j=(n/grid_width_dim)*block_height
        box = (i,j,i+block_width,j+block_height)    

        #paste it   
        new_image.paste(blocks[new_order[n]],box)

    return new_image



#find the dimension(height or width) according to the desired granularity (a lower granularity small blocks)
def find_block_dim(granularity,dim):
    assert(granularity>0)
    candidate=0
    block_dim=1
    counter=0
    while counter!=granularity:         #while we dont achive the desired granularity
        candidate+=1
        while((dim%candidate)!=0):      
            candidate+=1
            if candidate>dim:
                counter=granularity-1
                break

        if candidate<=dim:
            block_dim=candidate         #save the current feasible lenght

        counter+=1

    assert(dim%block_dim==0 and block_dim<=dim)
    return block_dim

def unscramble_blocks(im,granularity,password,nshuffle):
    set_seed(password)
    width=im.size[0]
    height=im.size[1]

    block_width=find_block_dim(granularity,width)       #find the possible block dimensions
    block_height=find_block_dim(granularity,height)

    grid_width_dim=width/block_width                #dimension of the grid
    grid_height_dim=height/block_height

    nblocks=grid_width_dim*grid_height_dim          #number of blocks

    print "nblocks: ",nblocks," block width: ",block_width," block height: ",block_height
    print "getting all the blocks ..."
    blocks=[]
    for n in xrange(nblocks): #get all the image blocks
        blocks+=[get_block(im,n,block_width,block_height)]

    print "shuffling ..."
    #shuffle the order of the blocks
    new_order=range(nblocks)
    for n in xrange(nshuffle):
        random.shuffle(new_order)

    print "building final image ..."
    new_image=im.copy()
    for n in xrange(nblocks):
        #define the target box where to paste the new block
        i=(new_order[n]%grid_width_dim)*block_width             #i,j -> upper left point of the target image
        j=(new_order[n]/grid_width_dim)*block_height
        box = (i,j,i+block_width,j+block_height)    

        #paste it   
        new_image.paste(blocks[n],box)

    return new_image

#get a block of the image
def get_block(im,n,block_width,block_height):

    width=im.size[0]

    grid_width_dim=width/block_width                        #dimension of the grid

    i=(n%grid_width_dim)*block_width                        #i,j -> upper left point of the target block
    j=(n/grid_width_dim)*block_height

    box = (i,j,i+block_width,j+block_height)
    block_im = im.crop(box)
    return block_im

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


if __name__ == '__main__':

    filename="0RT8s.jpg"
    # filename="B5TbK.png"
    password="yOs0ZaKpiS"
    nshuffle=1
    granularity=1

    im=Image.open(filename)

    new_image=scramble_blocks(im,granularity,password,nshuffle)
    new_image.show()
    new_image.save(filename.split(".")[0]+"_puzzled.png")

    new_image=unscramble_blocks(new_image,granularity,password,nshuffle)
    new_image.save(filename.split(".")[0]+"_unpuzzled.png")
    new_image.show()

5

47

94 рядки. 47 для кодування, 47 для декодування.

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

введіть тут опис зображення введіть тут опис зображення

(перетворено в jpg)

введіть тут опис зображення

(оригінальний зменшений масштаб)

введіть тут опис зображення


3
Частина оригінального малюнка видно через ці рядки. Однак вона нагадує, коли ви малюєте пальцем по туманеному вікні).
Сомній

2
... + 184426 байт для codegolf-35005_ref.rb?
edc65

5

Matlab з дрібкою теорії групи (+ бонус)

У такому підході ми припускаємо, що ми маємо парну кількість загальної пікселі. (Якщо ні, ми просто ігноруємо один піксель). Тому нам потрібно вибрати половину пікселів, щоб поміняти їх іншою половиною. Для цього ми індексуємо всі пікселі від 0до 2N-1і вважаємо ці показники циклічною групою.

Серед праймерів ми шукаємо число, pяке не надто мале і не надто велике, і це однозначно до 2Nпорядку нашої групи. Це означає, що g генерує нашу групу або {k*g mod 2N | k=0,1,...,2N-1} = {0,1,...,2N-1}.

Таким чином, ми вибираємо перші Nкратні gяк один набір, а всі інші індекси, як інші, і просто поміняємо відповідний набір пікселів.

Якщо pобрано правильний шлях, перший набір рівномірно розподіляється по всьому зображенню.

Два тестові справи:

Трохи поза темою, але цікаво:

Під час тестування я помітив, що якщо ви збережете його в (стислий збиток) jpg (замість стисненого PNG без втрат) і застосуйте перетворення вперед і назад, ви досить швидко побачите артефакти стиснення, це показує результати двох послідовних перестановок :

Як бачите, стиснення jpg робить результат майже чорним і білим!

clc;clear;
inputname = 'codegolf_rearrange_pixels2.png';
inputname = 'codegolf_rearrange_pixels2_swapped.png';
outputname = 'codegolf_rearrange_pixels2_swapped.png';

%read image
src = imread(inputname);

%separate into channels
red = src(:,:,1);
green = src(:,:,2);
blue = src(:,:,3);

Ntotal = numel(red(:));  %number of pixels
Nswap = floor(Ntotal/2); %how many pairs we can swap

%find big enough generator
factors = unique(factor(Ntotal));
possible_gen = primes(max(size(red)));
eliminated = setdiff(possible_gen,factors);
if mod(numel(eliminated),2)==0 %make length odd for median
    eliminated = [1,eliminated];
end
generator = median(eliminated);

%set up the swapping vectors
swapindices1 = 1+mod((1:Nswap)*generator, Ntotal);
swapindices2 = setdiff(1:Ntotal,swapindices1);
swapindices2 = swapindices2(1:numel(swapindices1)); %make sure both have the same length

%swap the pixels
red([swapindices1,swapindices2]) = red([swapindices2,swapindices1]);
green([swapindices1,swapindices2]) = green([swapindices2,swapindices1]);
blue([swapindices1,swapindices2]) = blue([swapindices2,swapindices1]);

%write and display
output = cat(3,red,green,blue);
imwrite(output,outputname);
subplot(2,1,1);
imshow(src)
subplot(2,1,2);
imshow(output);

4

JavaScript (+ бонус) - повторник повторного заміни пікселів

Функція приймає елемент зображення і

  1. Ділить пікселі на 8.
  2. Здійснює зворотний обмін піксельними групами.
  3. Якщо замінюється група пікселів> = 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()
}

Гори Кола


4

Python 2.7 + PIL, скремблер для стовпців / рядків

Цей метод просто перебирає рядки та стовпці зображення. Можна скремтувати лише один із вимірів або обидва. Крім того, порядок нового скремтованого рядка / стовпця базується на паролі. Крім того, ще одна можливість - це скремтування всього масиву зображень без врахування розмірів.

Результати:

Скремтування всього зображення:

введіть тут опис зображення

введіть тут опис зображення

Розшифровка стовпців:

введіть тут опис зображення

введіть тут опис зображення

Розбирання рядків:

введіть тут опис зображення

введіть тут опис зображення

Розшифровка обох стовпців та рядків:

введіть тут опис зображення

введіть тут опис зображення

Я також спробував застосувати кілька зображень до зображення, але кінцеві результати не сильно відрізнялися, лише складнощі розшифрувати його.

Код:

from PIL import Image
import random,copy

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

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

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

    im.putdata(newpixels)

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

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

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

    im.putdata(newpixels)

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

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

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

    im.putdata(newpixels)

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

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

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

    im.putdata(newpixels)

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

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

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

    im.putdata(newpixels)

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

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

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

    im.putdata(newpixels)


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

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

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

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

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

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

3

C # Winforms

Зображення1: введіть тут опис зображення

Зображення 2: введіть тут опис зображення

Вихідний код:

class Program
{
    public static void codec(String src, String trg, bool enc)
    {
        Bitmap bmp = new Bitmap(src);
        Bitmap dst = new Bitmap(bmp.Width, bmp.Height);

        List<Point> points = new List<Point>();
        for (int y = 0; y < bmp.Height; y++)
            for (int x = 0; x < bmp.Width; x++)
                points.Add(new Point(x, y));

        for (int y = 0; y < bmp.Height; y++)
        {
            for (int x = 0; x < bmp.Width; x++)
            {
                int py = Convert.ToInt32(y + 45 * Math.Sin(2.0 * Math.PI * x / 128.0));
                int px = Convert.ToInt32(x + 45 * Math.Sin(2.0 * Math.PI * y / 128.0));

                px = px < 0 ? 0 : px;
                py = py < 0 ? 0 : py;
                px = px >= bmp.Width ? bmp.Width - 1 : px;
                py = py >= bmp.Height ? bmp.Height - 1 : py;

                int srcIndex = x + y * bmp.Width;
                int dstIndex = px + py * bmp.Width;

                Point temp = points[srcIndex];
                points[srcIndex] = points[dstIndex];
                points[dstIndex] = temp;
            }
        }

        for (int y = 0; y < bmp.Height; y++)
        {
            for (int x = 0; x < bmp.Width; x++)
            {
                Point p = points[x + y * bmp.Width];
                if (enc)
                    dst.SetPixel(x, y, bmp.GetPixel(p.X, p.Y));
                else
                    dst.SetPixel(p.X, p.Y, bmp.GetPixel(x, y));
            }
        }

        dst.Save(trg);
    }


    static void Main(string[] args)
    {
        // encode
        codec(@"c:\shared\test.png", @"c:\shared\test_enc.png", true);

        // decode
        codec(@"c:\shared\test_enc.png", @"c:\shared\test_dec.png", false);
    }
}

1

Python 3.6 + pypng

Стрілець / 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()

Мій алгоритм застосовує переміщення стрілецької стрілки в одному напрямку, а головне переміщення в іншому (оскільки обидва є обертаннями одна для одної), кілька ітерацій кожна, але кожна узагальнена для поділу на будь-яку кількість підгруп замість лише двох. Ефект полягає в тому, що ви можете зробити ключ з перестановкою багато ітерацій, оскільки зображення не буде відновлено, не знаючи точної послідовності переміщення рифлетів та майстрів. Послідовність може бути визначена з низкою цілих чисел, причому додатні числа представляють рушниці, а негативні числа - майстри.

Я перемістив пейзаж ключем [3, -5, 2, 13, -7]:

Пейзаж 3 -5 2 13 -7

Цікаво, що деякі цікаві речі трапляються з [3, -5], де залишилися деякі артефакти з оригінального зображення:

Пейзаж 3 -5

Ось абстрактний зразок, змішаний з ключем [2, 3, 5, 7, -11, 13, -17]:

Кола 2 3 5 7 -11 13 -17

Якщо лише один параметр невірний у ключі, відміна зображення не відновить зображення:

Погана розминка

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.