Réorganisez les pixels dans l'image afin qu'elle ne puisse pas être reconnue, puis récupérez-la


86

Créez un programme capable de réorganiser les pixels dans une image de sorte qu'il ne puisse pas être reconnu. Cependant, votre programme devrait pouvoir le reconvertir en image originale.

Vous pouvez écrire deux fonctions - pour l’encodage et le décodage. Cependant, une fonction appliquée de manière répétée à l’image originale (exemple en mathématiques - f(x) = 1 - x) est un bonus.

Produire également un motif en sortie donne aussi un bonus.

L'image peut être représentée sous forme de tableau 1D / 2D ou d'objet image si votre langage le prend en charge. Notez que vous ne pouvez changer que l'ordre des pixels!

Sera logique de choisir comme code gagnant qui produit une image moins reconnaissable. Cependant, je ne sais pas comment le mesurer exactement, toutes les manières que je peux imaginer peuvent être trompées. C'est pourquoi j'ai choisi cette question comme concours de popularité. Laissez les utilisateurs choisir la meilleure réponse!

Image de test 1 (800 x 422 px): Image de test 2 (800 x 480 px): Veuillez fournir une image de sortie du code.


La question est une façon très longue de dire "Écris un algorithme de chiffrement pour les images, dont le résultat est une image".
David Richerby le

3
@DavidRicherby… qui utilise les mêmes pixels / couleurs. Cinq pixels noirs dans "l'image simple" -> cinq pixels noirs dans "l'image chiffrée".
Daniel Beck

2
@ user2992539 Très bien, dans ce cas, vous voudrez peut-être indiquer explicitement que ceci est utilisé comme condition de départage. Sinon, le simple fait de dire que c'est un bonus n'a pas beaucoup de sens.
Martin Ender

3
Cette question m'a fait penser à la carte du chat d' Arnold . Je ne pense pas que cela convienne tout à fait, mais c'est intéressant de la même manière: répéter la carte suffisamment de fois pour revenir à l'image d'origine.
Trichoplax

4
Maintenant ailleurs sur le réseau Stack Exchange: Security.SE of all places
Doorknob

Réponses:


58

Python 2.7 (avec PIL) - Pas de pseudo-aléatoire

Je casse l'image en blocs de 2 sur 2 (sans tenir compte du reste) et tourne chaque bloc de 180 degrés, puis je fais la même chose avec des blocs de 3 sur 3, puis de 4, etc., jusqu'à un paramètre BLKSZ. Ensuite, je fais la même chose pour BLKSZ-1, puis BLKSZ-2, puis 3 et 2. Cette méthode s’inverse exactement; la fonction Unscramble est la fonction Scramble.

Le code :

from PIL import Image
import math

im = Image.open("ST1.png", "r")
arr = im.load() #pixel data stored in this 2D array

def rot(A, n, x1, y1): #this is the function which rotates a given block
    temple = []
    for i in range(n):
        temple.append([])
        for j in range(n):
            temple[i].append(arr[x1+i, y1+j])
    for i in range(n):
        for j in range(n):
            arr[x1+i,y1+j] = temple[n-1-i][n-1-j]


xres = 800
yres = 480
BLKSZ = 50 #blocksize
for i in range(2, BLKSZ+1):
    for j in range(int(math.floor(float(xres)/float(i)))):
        for k in range(int(math.floor(float(yres)/float(i)))):
            rot(arr, i, j*i, k*i)
for i in range(3, BLKSZ+1):
    for j in range(int(math.floor(float(xres)/float(BLKSZ+2-i)))):
        for k in range(int(math.floor(float(yres)/float(BLKSZ+2-i)))):
            rot(arr, BLKSZ+2-i, j*(BLKSZ+2-i), k*(BLKSZ+2-i))

im.save("ST1OUT "+str(BLKSZ)+".png")

print("Done!")

En fonction de la taille des blocs, vous pouvez faire en sorte que le calcul supprime toute ressemblance avec l'image d'origine: (BLKSZ = 50) entrez la description de l'image ici entrez la description de l'image ici

Ou rendre le calcul efficace: (BLKSZ = 10) entrez la description de l'image ici entrez la description de l'image ici


6
Il semble que les meilleurs résultats seront si BLKSZ aura environ la moitié de la taille de l’image. Quoi qu'il en soit, j'aime bien les algorithmes et pour les petits BLKSZ, cela ressemble à un art moderne! Cool!
Somnium

11
Je vote toujours python.
qwr

Au lieu de brouiller toutes les valeurs de 2 à 50, vous devriez peut-être utiliser uniquement des nombres premiers?
Neil

@ Neil Il est probable que cela aura l'air plus aléatoire et moins artistique.
Somnium

Le BLKSZ = 10paysage est vraiment cool!
wchargin

52

C #, Winform

Modifier En changeant la manière dont vous remplissez le tableau de coordonnées, vous pouvez avoir différents modèles - voir ci-dessous

Aimez-vous ce genre de motif?

Paysage

Abstrait

Prime:

Crier Scream Scrambled

Échange aléatoire exactement une fois tous les pixels de la moitié supérieure et tous les pixels de la moitié inférieure. Répétez la même procédure pour déchiffrer (bonus).

Code

Scramble.cs

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Windows.Forms;
using System.Drawing.Imaging;
using System.IO;

namespace Palette
{
    public partial class Scramble : Form
    {
        public Scramble()
        {
            InitializeComponent();
        }

        public struct Coord
        {
            public int x, y;
        }

        private void Work(Bitmap srcb, Bitmap outb)
        {
            int w = srcb.Width, h = srcb.Height;
            Coord[] coord = new Coord[w * h];

            FastBitmap fsb = new FastBitmap(srcb);
            FastBitmap fob = new FastBitmap(outb);
            fsb.LockImage();
            fob.LockImage();
            ulong seed = 0;
            int numpix = 0;
            for (int y = 0; y < h; y++)
                for (int x = 0; x < w; numpix++, x++)
                {
                    coord[numpix].x = x;
                    coord[numpix].y = y;
                    uint color = fsb.GetPixel(x, y);
                    seed += color;
                    fob.SetPixel(x, y, color);
                }
            fsb.UnlockImage();
            fob.UnlockImage();
            pbOutput.Refresh();
            Application.DoEvents();

            int half = numpix / 2;
            int limit = half;
            XorShift rng = new XorShift(seed);
            progressBar.Visible = true;
            progressBar.Maximum = limit;

            fob.LockImage();
            while (limit > 0)
            {
                int p = (int)(rng.next() % (uint)limit);
                int q = (int)(rng.next() % (uint)limit);
                uint color = fob.GetPixel(coord[p].x, coord[p].y); 
                fob.SetPixel(coord[p].x, coord[p].y, fob.GetPixel(coord[half+q].x, coord[half+q].y)); 
                fob.SetPixel(coord[half+q].x, coord[half+q].y, color); 
                limit--;
                if (p < limit)
                {
                    coord[p]=coord[limit];
                }
                if (q < limit)
                {
                    coord[half+q]=coord[half+limit];
                }
                if ((limit & 0xfff) == 0)
                {
                    progressBar.Value = limit;
                    fob.UnlockImage();
                    pbOutput.Refresh();
                    fob.LockImage();
                }
            }
            fob.UnlockImage();
            pbOutput.Refresh();
            progressBar.Visible = false; 
        }

        void DupImage(PictureBox s, PictureBox d)
        {
            if (d.Image != null)
                d.Image.Dispose();
            d.Image = new Bitmap(s.Image.Width, s.Image.Height);  
        }

        void GetImagePB(PictureBox pb, string file)
        {
            Bitmap bms = new Bitmap(file, false);
            Bitmap bmp = bms.Clone(new Rectangle(0, 0, bms.Width, bms.Height), PixelFormat.Format32bppArgb);
            bms.Dispose(); 
            if (pb.Image != null)
                pb.Image.Dispose();
            pb.Image = bmp;
        }

        private void btnOpen_Click(object sender, EventArgs e)
        {
            OpenFileDialog openFileDialog = new OpenFileDialog();

            openFileDialog.InitialDirectory = "c:\\temp\\";
            openFileDialog.Filter = "Image Files(*.BMP;*.JPG;*.PNG)|*.BMP;*.JPG;*.PNG|All files (*.*)|*.*";
            openFileDialog.FilterIndex = 1;
            openFileDialog.RestoreDirectory = true;

            if (openFileDialog.ShowDialog() == DialogResult.OK)
            {
                try
                {
                    string file = openFileDialog.FileName;
                    GetImagePB(pbInput, file);
                    pbInput.Tag = file;
                    DupImage(pbInput, pbOutput);
                    Work(pbInput.Image as Bitmap, pbOutput.Image as Bitmap);
                    file = Path.GetDirectoryName(file) + Path.DirectorySeparatorChar + Path.GetFileNameWithoutExtension(file) + ".scr.png";
                    pbOutput.Image.Save(file);
                }
                catch (Exception ex)
                {
                    MessageBox.Show("Error: Could not read file from disk. Original error: " + ex.Message);
                }

            }
        }
    }

    //Adapted from Visual C# Kicks - http://www.vcskicks.com/
    unsafe public class FastBitmap
    {
        private Bitmap workingBitmap = null;
        private int width = 0;
        private BitmapData bitmapData = null;
        private Byte* pBase = null;

        public FastBitmap(Bitmap inputBitmap)
        {
            workingBitmap = inputBitmap;
        }

        public BitmapData LockImage()
        {
            Rectangle bounds = new Rectangle(Point.Empty, workingBitmap.Size);

            width = (int)(bounds.Width * 4 + 3) & ~3;

            //Lock Image
            bitmapData = workingBitmap.LockBits(bounds, ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
            pBase = (Byte*)bitmapData.Scan0.ToPointer();
            return bitmapData;
        }

        private uint* pixelData = null;

        public uint GetPixel(int x, int y)
        {
            pixelData = (uint*)(pBase + y * width + x * 4);
            return *pixelData;
        }

        public uint GetNextPixel()
        {
            return *++pixelData;
        }

        public void GetPixelArray(int x, int y, uint[] Values, int offset, int count)
        {
            pixelData = (uint*)(pBase + y * width + x * 4);
            while (count-- > 0)
            {
                Values[offset++] = *pixelData++;
            }
        }

        public void SetPixel(int x, int y, uint color)
        {
            pixelData = (uint*)(pBase + y * width + x * 4);
            *pixelData = color;
        }

        public void SetNextPixel(uint color)
        {
            *++pixelData = color;
        }

        public void UnlockImage()
        {
            workingBitmap.UnlockBits(bitmapData);
            bitmapData = null;
            pBase = null;
        }
    }

    public class XorShift
    {
        private ulong x; /* The state must be seeded with a nonzero value. */

        public XorShift(ulong seed)
        {
            x = seed;
        }

        public ulong next()
        {
            x ^= x >> 12; // a
            x ^= x << 25; // b
            x ^= x >> 27; // c
            return x * 2685821657736338717L;
        }
    }
} 

Scramble.designer.cs

namespace Palette
{
    partial class Scramble
    {
        private System.ComponentModel.IContainer components = null;

        protected override void Dispose(bool disposing)
        {
            if (disposing && (components != null))
            {
                components.Dispose();
            }
            base.Dispose(disposing);
        }

        private void InitializeComponent()
        {
            this.panel = new System.Windows.Forms.FlowLayoutPanel();
            this.pbInput = new System.Windows.Forms.PictureBox();
            this.pbOutput = new System.Windows.Forms.PictureBox();
            this.progressBar = new System.Windows.Forms.ProgressBar();
            this.btnOpen = new System.Windows.Forms.Button();
            this.panel.SuspendLayout();
            ((System.ComponentModel.ISupportInitialize)(this.pbInput)).BeginInit();
            ((System.ComponentModel.ISupportInitialize)(this.pbOutput)).BeginInit();
            this.SuspendLayout();
            // 
            // panel
            // 
            this.panel.AutoScroll = true;
            this.panel.AutoSize = true;
            this.panel.Controls.Add(this.pbInput);
            this.panel.Controls.Add(this.pbOutput);
            this.panel.Dock = System.Windows.Forms.DockStyle.Top;
            this.panel.Location = new System.Drawing.Point(0, 0);
            this.panel.Name = "panel";
            this.panel.Size = new System.Drawing.Size(748, 306);
            this.panel.TabIndex = 3;
            // 
            // pbInput
            // 
            this.pbInput.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
            this.pbInput.Location = new System.Drawing.Point(3, 3);
            this.pbInput.MinimumSize = new System.Drawing.Size(100, 100);
            this.pbInput.Name = "pbInput";
            this.pbInput.Size = new System.Drawing.Size(100, 300);
            this.pbInput.SizeMode = System.Windows.Forms.PictureBoxSizeMode.AutoSize;
            this.pbInput.TabIndex = 3;
            this.pbInput.TabStop = false;
            // 
            // pbOutput
            // 
            this.pbOutput.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
            this.pbOutput.Location = new System.Drawing.Point(109, 3);
            this.pbOutput.MinimumSize = new System.Drawing.Size(100, 100);
            this.pbOutput.Name = "pbOutput";
            this.pbOutput.Size = new System.Drawing.Size(100, 300);
            this.pbOutput.SizeMode = System.Windows.Forms.PictureBoxSizeMode.AutoSize;
            this.pbOutput.TabIndex = 4;
            this.pbOutput.TabStop = false;
            // 
            // progressBar
            // 
            this.progressBar.Dock = System.Windows.Forms.DockStyle.Bottom;
            this.progressBar.Location = new System.Drawing.Point(0, 465);
            this.progressBar.Name = "progressBar";
            this.progressBar.Size = new System.Drawing.Size(748, 16);
            this.progressBar.TabIndex = 5;
            // 
            // btnOpen
            // 
            this.btnOpen.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
            this.btnOpen.Location = new System.Drawing.Point(12, 429);
            this.btnOpen.Name = "btnOpen";
            this.btnOpen.Size = new System.Drawing.Size(53, 30);
            this.btnOpen.TabIndex = 6;
            this.btnOpen.Text = "Start";
            this.btnOpen.UseVisualStyleBackColor = true;
            this.btnOpen.Click += new System.EventHandler(this.btnOpen_Click);
            // 
            // Scramble
            // 
            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
            this.BackColor = System.Drawing.SystemColors.ControlDark;
            this.ClientSize = new System.Drawing.Size(748, 481);
            this.Controls.Add(this.btnOpen);
            this.Controls.Add(this.progressBar);
            this.Controls.Add(this.panel);
            this.Name = "Scramble";
            this.Text = "Form1";
            this.panel.ResumeLayout(false);
            this.panel.PerformLayout();
            ((System.ComponentModel.ISupportInitialize)(this.pbInput)).EndInit();
            ((System.ComponentModel.ISupportInitialize)(this.pbOutput)).EndInit();
            this.ResumeLayout(false);
            this.PerformLayout();

        }


        private System.Windows.Forms.FlowLayoutPanel panel;
        private System.Windows.Forms.PictureBox pbOutput;
        private System.Windows.Forms.ProgressBar progressBar;
        private System.Windows.Forms.PictureBox pbInput;
        private System.Windows.Forms.Button btnOpen;
    }
}

Program.cs

using System;
using System.Collections.Generic;
using System.Windows.Forms;

namespace Palette
{
  static class Program
  {
    [STAThread]
    static void Main()
    {
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);
        Application.Run(new Scramble());
    }
  }
}

Cochez 'Code non sécurisé' dans la propriété du projet à compiler.

Modèle complexe

Brouiller

Modifiez la première partie de la fonction de travail, jusqu’à 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
Intéressant) Je me demande si, avec une approche similaire, il est possible de créer des modèles plus compliqués de production.
Somnium

1
Belle idée - qu'advient-il de la ligne médiane quand il y a un nombre impair de lignes?
flawr

1
@flawr la division est par pixel. S'il y a un nombre impair de pixels, le dernier est laissé intact. S'il y a un nombre impair de rangées, la moitié gauche de la rangée du milieu est «côté supérieur» et la moitié droite est «côté inférieur».
Edc65

1
@ user2992539 Je pense que vous pouvez subdiviser davantage, même en damier. Avec plus de subdivisions, l'image est plus reconnaissable.
Edc65

7
Comme votre version "Scramble"!)
Somnium

43

C, flou arbitraire, facilement réversible

En retard à la fête. Voici mon entrée!

Cette méthode crée un flou de brouillage. Je l'appelle scramblur . C'est extrêmement simple. Dans une boucle, il choisit un pixel aléatoire, puis l’échange avec un pixel voisin choisi au hasard dans un modèle de toile toroïdale. Vous spécifiez la distance maximale définissant ce que "pixel voisin" signifie (1 signifie de toujours choisir un pixel adjacent), le nombre d'itérations et, éventuellement, un nombre de départ aléatoire. Plus la distance maximale est grande et plus le nombre d'itérations est grand, plus le résultat est flou.

Il est réversible en spécifiant un nombre négatif d'itérations (il s'agit simplement d'une commodité d'interface de ligne de commande; les itérations négatives n'existent pas en réalité). En interne, il utilise un LCPRNG personnalisé (générateur de nombres pseudo-aléatoires congruentiels linéaires) sur 64 bits et pré-génère un bloc de valeurs. La table permet de parcourir le bloc en avant ou en arrière pour l'embrouillage ou le désembrouillage, respectivement.

Démo

Lorsque vous faites défiler l'écran vers le bas pour les deux premières images, chaque image est floue avec un décalage maximum plus élevé: Plus haute est l'image d'origine (par exemple, décalage 0 pixel), suivi de 1, 2, 4, 8, 16, 32, 64 , 128 et enfin 256. Le nombre d'itérations est 10 = 1 000 000 pour toutes les images ci-dessous.

Pour les deux secondes images, chaque image est floue en utilisant un décalage progressivement plus faible - par exemple, le plus flou au moins flou - d'un offset maximum de 256 à 0. Enjoy!

Paysage Abstrait

Et pour ces deux images suivantes, vous pouvez voir les progressions en taille réelle ici et ici :

Breaking Bad Simpsons

Code

Au bout d’une heure environ, j’ai piraté le problème ensemble au réveil et il ne contient presque aucune documentation. Je pourrais revenir dans quelques jours et ajouter plus de documentation plus tard si les gens le demandent.

//=============================================================================
// 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
Juste passé de cette réponse, semble génial
Thomas

1
Cette réponse est vraiment grande. Pensez-vous que vous pourriez déplacer les images supplémentaires (c.-à-d. Tout sauf les deux images de test entièrement floues) vers une galerie hors site?
Tim S.

@TimS. - terminé! les réduire à de minuscules vignettes.
Todd Lehman

42

Python 3.4

  • Bonus 1: Inverse de soi: répéter répète l'image d'origine.
  • Image clé optionnelle: l'image d'origine ne peut être restaurée qu'en utilisant à nouveau la même image clé.
  • Bonus 2: Motif de production dans la sortie: l'image clé est approchée en pixels brouillés.

Lorsque le bonus 2 est atteint, en utilisant une image clé supplémentaire, le bonus 1 n'est pas perdu. Le programme est toujours auto-inverse, à condition qu'il soit exécuté à nouveau avec la même image clé.

Utilisation standard

Image test 1:

Image test brouillée 1

Test image 2:

Image test brouillée 2

L'exécution du programme avec un seul fichier image comme argument enregistre un fichier image avec les pixels embrouillés de manière uniforme sur toute l'image. Le relancer avec la sortie brouillée enregistre un fichier image avec le brouillage appliqué à nouveau, ce qui restaure le fichier original puisque le processus de brouillage est son propre inverse.

Le processus d'embrouillage est auto-inverse car la liste de tous les pixels est divisée en deux cycles, de sorte que chaque pixel est échangé avec un et un seul autre pixel. Son exécution une seconde fois permet d'échanger chaque pixel avec le pixel avec lequel elle a été échangée pour la première fois, en reprenant le principe initial. S'il y a un nombre impair de pixels, il y en aura un qui ne bouge pas.

Merci à la réponse de mfvonh en tant que premier à suggérer 2 cycles.

Utilisation avec une image clé

Scrambling Test image 1 avec l'image test 2 comme image clé

Scramble test 1 avec test 2

Scrambling Test image 2 avec l'image test 1 comme image clé

Scramble test 2 avec test 1

L'exécution du programme avec un deuxième argument de fichier image (l'image clé) divise l'image d'origine en régions basées sur l'image clé. Chacune de ces régions est divisée en 2 cycles séparément, de sorte que tout le brouillage se produit dans les régions et que les pixels ne sont pas déplacés d'une région à une autre. Cela répartit les pixels sur chaque région et les régions deviennent alors une couleur uniforme mouchetée, mais avec une couleur moyenne légèrement différente pour chaque région. Cela donne une approximation très approximative de l'image clé, dans les mauvaises couleurs.

Une nouvelle exécution permute les mêmes paires de pixels dans chaque région. Ainsi, chaque région retrouve son état d'origine et l'image dans son ensemble réapparaît.

Grâce à la réponse de edc65, le premier à suggérer de diviser l’image en régions. Je voulais développer cela pour utiliser des régions arbitraires, mais l'approche consistant à tout permuter dans la région 1 avec tout dans la région 2 impliquait que les régions soient de taille identique. Ma solution est de garder les régions isolées les unes des autres et de mélanger simplement chaque région. Puisque les régions n'ont plus besoin d'être de taille similaire, il devient plus simple d'appliquer des régions de forme arbitraire.

Code

import os.path
from PIL import Image   # Uses Pillow, a fork of PIL for Python 3
from random import randrange, seed


def scramble(input_image_filename, key_image_filename=None,
             number_of_regions=16777216):
    input_image_path = os.path.abspath(input_image_filename)
    input_image = Image.open(input_image_path)
    if input_image.size == (1, 1):
        raise ValueError("input image must contain more than 1 pixel")
    number_of_regions = min(int(number_of_regions),
                            number_of_colours(input_image))
    if key_image_filename:
        key_image_path = os.path.abspath(key_image_filename)
        key_image = Image.open(key_image_path)
    else:
        key_image = None
        number_of_regions = 1
    region_lists = create_region_lists(input_image, key_image,
                                       number_of_regions)
    seed(0)
    shuffle(region_lists)
    output_image = swap_pixels(input_image, region_lists)
    save_output_image(output_image, input_image_path)


def number_of_colours(image):
    return len(set(list(image.getdata())))


def create_region_lists(input_image, key_image, number_of_regions):
    template = create_template(input_image, key_image, number_of_regions)
    number_of_regions_created = len(set(template))
    region_lists = [[] for i in range(number_of_regions_created)]
    for i in range(len(template)):
        region = template[i]
        region_lists[region].append(i)
    odd_region_lists = [region_list for region_list in region_lists
                        if len(region_list) % 2]
    for i in range(len(odd_region_lists) - 1):
        odd_region_lists[i].append(odd_region_lists[i + 1].pop())
    return region_lists


def create_template(input_image, key_image, number_of_regions):
    if number_of_regions == 1:
        width, height = input_image.size
        return [0] * (width * height)
    else:
        resized_key_image = key_image.resize(input_image.size, Image.NEAREST)
        pixels = list(resized_key_image.getdata())
        pixel_measures = [measure(pixel) for pixel in pixels]
        distinct_values = list(set(pixel_measures))
        number_of_distinct_values = len(distinct_values)
        number_of_regions_created = min(number_of_regions,
                                        number_of_distinct_values)
        sorted_distinct_values = sorted(distinct_values)
        while True:
            values_per_region = (number_of_distinct_values /
                                 number_of_regions_created)
            value_to_region = {sorted_distinct_values[i]:
                               int(i // values_per_region)
                               for i in range(len(sorted_distinct_values))}
            pixel_regions = [value_to_region[pixel_measure]
                             for pixel_measure in pixel_measures]
            if no_small_pixel_regions(pixel_regions,
                                      number_of_regions_created):
                break
            else:
                number_of_regions_created //= 2
        return pixel_regions


def no_small_pixel_regions(pixel_regions, number_of_regions_created):
    counts = [0 for i in range(number_of_regions_created)]
    for value in pixel_regions:
        counts[value] += 1
    if all(counts[i] >= 256 for i in range(number_of_regions_created)):
        return True


def shuffle(region_lists):
    for region_list in region_lists:
        length = len(region_list)
        for i in range(length):
            j = randrange(length)
            region_list[i], region_list[j] = region_list[j], region_list[i]


def measure(pixel):
    '''Return a single value roughly measuring the brightness.

    Not intended as an accurate measure, simply uses primes to prevent two
    different colours from having the same measure, so that an image with
    different colours of similar brightness will still be divided into
    regions.
    '''
    if type(pixel) is int:
        return pixel
    else:
        r, g, b = pixel[:3]
        return r * 2999 + g * 5869 + b * 1151


def swap_pixels(input_image, region_lists):
    pixels = list(input_image.getdata())
    for region in region_lists:
        for i in range(0, len(region) - 1, 2):
            pixels[region[i]], pixels[region[i+1]] = (pixels[region[i+1]],
                                                      pixels[region[i]])
    scrambled_image = Image.new(input_image.mode, input_image.size)
    scrambled_image.putdata(pixels)
    return scrambled_image


def save_output_image(output_image, full_path):
    head, tail = os.path.split(full_path)
    if tail[:10] == 'scrambled_':
        augmented_tail = 'rescued_' + tail[10:]
    else:
        augmented_tail = 'scrambled_' + tail
    save_filename = os.path.join(head, augmented_tail)
    output_image.save(save_filename)


if __name__ == '__main__':
    import sys
    arguments = sys.argv[1:]
    if arguments:
        scramble(*arguments[:3])
    else:
        print('\n'
              'Arguments:\n'
              '    input image          (required)\n'
              '    key image            (optional, default None)\n'
              '    number of regions    '
              '(optional maximum - will be as high as practical otherwise)\n')

Gravure d'image JPEG

Les fichiers .jpg sont traités très rapidement, mais au prix d'une surchauffe. Cela laisse une image après gravure lorsque l'original est restauré:

jpg burn

Mais sérieusement, un format avec perte entraînera une légère modification de certaines couleurs de pixel, ce qui rend la sortie invalide. Lorsqu'une image clé est utilisée et que le mélange de pixels est limité à des régions, toute la distorsion est conservée dans la région dans laquelle elle s'est produite, puis répartie uniformément sur cette région lorsque l'image est restaurée. La différence de distorsion moyenne entre les régions laisse une différence visible entre elles, de sorte que les régions utilisées dans le processus d'embrouillage sont toujours visibles dans l'image restaurée.

La conversion au format .png (ou à tout autre format sans perte) avant le brouillage garantit que l'image non brouillée est identique à l'original, sans gravure ni distorsion:

png sans brûlure

Petits détails

  • Une taille minimale de 256 pixels est imposée aux régions. Si l’on permettait à l’image de se diviser en régions trop petites, l’image d’origine serait toujours partiellement visible après le brouillage.
  • S'il y a plus d'une région avec un nombre impair de pixels, un pixel de la deuxième région est réaffecté à la première, et ainsi de suite. Cela signifie qu'il ne peut jamais y avoir qu'une seule région avec un nombre impair de pixels et qu'un seul pixel restera non brouillé.
  • Il existe un troisième argument optionnel qui limite le nombre de régions. Le réglage de cette valeur sur 2, par exemple, donnera des images codées à deux tons. Cela peut sembler meilleur ou pire selon les images impliquées. Si un numéro est spécifié ici, l'image ne peut être restaurée qu'avec le même numéro.
  • Le nombre de couleurs distinctes dans l'image d'origine limite également le nombre de régions. Si l'image d'origine est à deux tons, quelle que soit l'image clé ou le troisième argument, il ne peut y avoir qu'un maximum de 2 régions.

2
+1 applaudissements! J'y ai vaguement pensé, mais je l'ai trouvé trop difficile à mettre en œuvre.
Edc65

1
C'est génial. J'ai une entrée soumise, mais j'aime mieux la tienne à cause de la fonctionnalité d'image clé.
Todd Lehman

Je serais curieux de savoir à quoi ressemblent ces deux images: lardlad.com/assets/wallpaper/simpsons1920.jpg et blogs.nd.edu/oblation/files/2013/09/BreakingBad.jpg (au format 720x450 ou tout ce qui est logique, et bien sûr pré-converti au format PNG pour éviter la gravure JPEG).
Todd Lehman

2
@ToddLehman Mon algorithme est limité par la nécessité d'être son propre inverse. Si vous voulez voir des approches vraiment intéressantes pour mélanger une image pour en faire une autre, vous devriez regarder le gothique américain dans la palette de Mona Lisa . Certains de ces programmes feraient des choses incroyables avec les images que vous mentionnez.
Trichoplax

2
La fonctionnalité d’image clé place cette tête et ces épaules au-dessus du reste.
Jack Aidley

33

Voici une transformation non aléatoire pour un changement

  1. Placez toutes les colonnes paires à gauche et toutes les colonnes impaires à droite.
  2. répéter des nxtemps
  3. faire la même chose pour les lignes nyfois

La transformation est presque auto-inverse, en répétant la transformation un total de size_xfois (dans la direction x) renvoie l'image d'origine. Je n'ai pas compris le calcul exact, mais utiliser des multiples entiers pour int(log_2(size_x))produire le meilleur brassage avec les plus petites images fantômes

montagnes mélangées entrez la description de l'image ici

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

Voici à quoi ressemblent les premières étapes 20 itérations (nx = ny, notez l’effet de différentes résolutions) entrez la description de l'image ici


7
C'est un algorithme vraiment cool. Et vous devriez vraiment avoir un bonus pour utiliser la photo de Lena Söderberg. :)
Todd Lehman

Vote positif toujours Lena

24

Mathematica

C'est assez simple. Je choisis 5 * nPixelsdes paires de coordonnées aléatoires et permute ces deux pixels (ce qui obscurcit complètement l'image). Pour le déchiffrer, je fais la même chose en sens inverse. Bien sûr, il faut que je sème le PRNG pour obtenir les mêmes paires de coordonnées sur les deux étapes.

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

La seule différence entre les deux fonctions est Reverse@dans unscramble. Les deux fonctions prennent un objet image réel. Vous pouvez les utiliser comme suit:

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

outet insont identiques. Voici à quoi scrressemble:

entrez la description de l'image ici entrez la description de l'image ici


4
Génial! Le seul problème est qu'il est plus sûr de créer PRNG vous-même, car si après un certain temps, Mathematica pense changer d'algorithme PRNG, il ne décodera pas les anciennes images codées!
Somnium

1
Agréable. Vous devriez pouvoir obtenir le même résultat avec Permute et FindPermutation.
DavidC

Je ne suis pas sûr de comprendre. Vous pouvez entrer la permutation précise souhaitée sous forme de liste de cycles.
DavidC

@ David Carraher Hm, intéressant. Est-ce que je ne devrais pas me souvenir de la permutation initiale pour utiliser FindPermutationsi?
Martin Ender

Ou peut-être que quelque chose {c, a, b}[[{2, 3, 1}]]peut être utilisé?
Somnium

22

C # (+ Bonus pour l'algorithme symétrique)

Cela fonctionne en trouvant un xtel que x^2 == 1 mod (number of pixels in image), puis en multipliant l'index de chaque pixel par xafin de trouver son nouvel emplacement. Cela vous permet d'utiliser exactement le même algorithme pour brouiller et décoder une image.

using System.Drawing;
using System.IO;
using System.Numerics;

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

        static void ScrambleUnscramble(string fileName)
        {
            using (var origImage = new Bitmap(fileName))
            using (var newImage = new Bitmap(origImage))
            {
                BigInteger totalPixels = origImage.Width * origImage.Height;
                BigInteger modSquare = GetSquareRootOf1(totalPixels);
                for (var x = 0; x < origImage.Width; x++)
                {
                    for (var y = 0; y < origImage.Height; y++)
                    {
                        var newNum = modSquare * GetPixelNumber(new Point(x, y), origImage.Size) % totalPixels;
                        var newPoint = GetPoint(newNum, origImage.Size);
                        newImage.SetPixel(newPoint.X, newPoint.Y, origImage.GetPixel(x, y));
                    }
                }
                newImage.Save("scrambled-" + Path.GetFileName(fileName));
            }
        }

        static BigInteger GetPixelNumber(Point point, Size totalSize)
        {
            return totalSize.Width * point.Y + point.X;
        }

        static Point GetPoint(BigInteger pixelNumber, Size totalSize)
        {
            return new Point((int)(pixelNumber % totalSize.Width), (int)(pixelNumber / totalSize.Width));
        }

        static BigInteger GetSquareRootOf1(BigInteger modulo)
        {
            for (var i = (BigInteger)2; i < modulo - 1; i++)
            {
                if ((i * i) % modulo == 1)
                    return i;
            }
            return modulo - 1;
        }
    }
}

première image de test, brouillée

deuxième image de test, brouillée


1
Intelligent) Y aura-t-il toujours une solution à cette équation de congruence?
Somnium

1
@ user2992539 Il y aura toujours des solutions triviales, 1(image d'origine) et modulo-1(image inversée / inversée). La plupart des numéros ont des solutions non triviales, mais il semble y avoir quelques exceptions . (lié à la factorisation principale de modulo)
Tim S.

Si je comprends bien, les solutions triviales conduisent à une image similaire à celle introduite.
Somnium

Correct: 1affiche l'image d'origine et, -1par exemple, imgur.com/EiE6VW2
Tim S.

19

C #, auto-inverse, pas de hasard

Si l'image d'origine a des dimensions d'une puissance égale à deux, chaque ligne et colonne est remplacée par la ligne et la colonne contenant le motif de bits inversé, par exemple pour une image de largeur 256, la ligne 0xB4 est remplacée par la ligne 0x2D. Les images d'autres tailles sont divisées en rectangles avec des côtés de puissance de 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;
        }
    }
}

Première image:

Brouillé première image

Deuxième image:

Deuxième image brouillée


2
J'aime la sortie "plaid" sur celui-ci.
Brian Rogers

14

C #

Même méthode pour le brouillage et le débrouillage. J'apprécierais des suggestions pour améliorer ceci.

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

Les résultats ont pour résultat un plaid psychédélique Premier Deuxième


Bien que cela ait un motif rayé)
Somnium

1
Pouvez-vous s'il vous plaît échanger les 2 images? Dans la question, l'image de la montagne est la première.
AL le

1
Pourriez-vous inclure une brève explication de l'algorithme?
Trichoplax

14

Python 2 (auto-inverse, pas d’aléatoire, sensible au contexte)

Cela ne remportera aucun prix pour "moins reconnaissable", mais peut-être qu'il peut marquer comme "intéressant". :-)

Je voulais faire quelque chose de sensible au contexte, où le brouillage des pixels dépend en réalité de l'image elle-même.

L'idée est assez simple: triez tous les pixels en fonction d'une valeur arbitraire dérivée de la couleur du pixel, puis permutez les positions du premier pixel de cette liste avec le dernier, le second avec l'avant-dernier, et ainsi de suite.

Malheureusement, dans cette approche simple, il y a un problème avec les pixels de la même couleur, donc pour continuer à l'inverse, mon programme est devenu un peu plus compliqué ...

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"

Voici le résultat: (abs (r-128) + abs (g-128) + abs (b-128)) // 128 (abs (r-128) + abs (g-128) + abs (b-128)) // 128

Vous pouvez obtenir des résultats très différents en modifiant la fonction de hachage 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
HOU LA LA! Celui-ci est génial !!! Bon travail!
Todd Lehman

1
C'est le plus intéressant à ce jour. Bon travail!
bebe

12

Mathematica (+ bonus)

Cela réduit les canaux de couleur et brouille l'image en une longue liste de données. Le résultat est une version cryptée encore moins reconnaissable car elle ne présente pas la même distribution de couleurs que la version originale (car ces données ont également été cryptées). Ceci est plus évident dans la deuxième image brouillée, mais si vous regardez de plus près, vous verrez le même effet dans la première. La fonction est son propre inverse.

Il y avait un commentaire que cela peut ne pas être valide parce que ça brouille par canal. Je pense que ça devrait l'être, mais ce n'est pas grave. Le seul changement nécessaire pour brouiller les pixels entiers (au lieu de par canal) serait de passer 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]]

Explication

Définit une fonction fqui prend un tableau à 2 dimensions x. La fonction utilise le produit des dimensions de l'image comme une graine aléatoire, puis aplatit le tableau en une liste à 1 dimension f(ombré localement). Ensuite, il crée une liste du formulaire {1, 2, ... n}nest la longueur de f, permute aléatoirement cette liste, le partitionne en segments de 2 (donc, par exemple, {{1, 2}, {3, 4}, ...}(en supprimant le dernier numéro si les dimensions sont toutes les deux impaires), puis permute fen échangeant les valeurs à les positions indiquées dans chaque sous-liste qui vient d'être créée, et finalement elle remodèle la liste permutée aux dimensions d'origine de x. Elle brouille par canal carFlattenLa commande réduit également les données du canal dans chaque pixel. La fonction est son propre inverse car les cycles n'incluent que deux pixels chacun.

Usage

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

f @ img1 // Image

entrez la description de l'image ici

f @ f @ img1 // Image

entrez la description de l'image ici

f @ img2 // Image

entrez la description de l'image ici

f @ f @ img2 // Image

entrez la description de l'image ici

Voici en utilisant 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

entrez la description de l'image ici


1
Mon hypothèse est que cela ne répond pas aux critères, car il bascule à une échelle inférieure à pixel.
Trichoplax

1
Je ne pense pas que ce soit une réponse valable, mais je l’aime aussi beaucoup. C'est une tournure fascinante, donc +1 quand même ...
trichoplax

1
@githubphagocyte Voir la mise à jour :)
mfvonh le

Génial - J'ai encore atteint le +1 mais bien sûr, je ne peux pas le faire deux fois ...
trichoplax

1
@ githubphagocyte Ah oui, j'oublie que la syntaxe courte peut être bizarre. Oui. f @ f @ img1 // Imageest (en syntaxe complète)Image[f[f[img1]]]
mfvonh

10

Matlab (+ bonus)

En gros, je change de façon aléatoire la position de deux pixels et je marque chaque pixel qui a été activé pour qu’il ne le soit plus. Le même script peut être utilisé à nouveau pour le «décryptage» car je réinitialise le générateur de nombres aléatoires à chaque fois. Ceci est fait jusqu'à ce que presque tous les pixels soient activés (c'est pourquoi stepize est supérieur à 2)

EDIT: Je viens de voir que Martin Büttner a utilisé une approche similaire - je n’avais pas l’intention de copier l’idée - j’ai commencé à écrire mon code alors qu’il n’y avait pas de réponse, donc désolé pour cela. Je pense toujours que ma version utilise des idées différentes =) (Et mon algorithme est bien plus inefficace si vous regardez le point où les deux coordonnées aléatoires sont choisies ^^)

Images

1 2

Code

img = imread('shuffle_image2.bmp');
s = size(img)
rand('seed',0)
map = zeros(s(1),s(2));
for i = 1:2.1:s(1)*s(2) %can use much time if stepsize is 2 since then every pixel has to be exchanged
    while true %find two unswitched pixels
        a = floor(rand(1,2) .* [s(1),s(2)] + [1,1]);
        b = floor(rand(1,2) .* [s(1),s(2)] + [1,1]);
        if map(a(1),a(2)) == 0 && map(b(1),b(2)) == 0
            break
        end
    end
    %switch
    map(a(1),a(2)) = 1;
    map(b(1),b(2)) = 1;
    t = img(a(1),a(2),:);
    img(a(1),a(2),:) = img(b(1),b(2),:);
    img(b(1),b(2),:) = t;
end
image(img)
imwrite(img,'output2.png')

Je ne comprends pas très bien, votre code appliqué une seconde fois sur une image cryptée le déchiffrera-t-il?
Somnium

2
Exactement: chaque fois, exactement deux pixels sont échangés et ne seront pas échangés pendant tout le processus. Étant donné que les nombres «aléatoires» sont les deux fois exactement identiques (en raison de la réinitialisation du générateur de nombres aléatoires), les paires de pixels seront échangées. (Le RNG s'appuie toujours sur le numéro généré précédemment pour générer le prochain, j'espère que c'est clair.)
2014

1
Ha, c’était en fait mon idée de départ, mais je ne pouvais pas être dérangé pour que chaque pixel soit échangé exactement une fois, car je devais me mettre au travail. : D +1!
Martin Ender

3
@ user2992539 Eh bien, vérifiez Octave, qui est une bonne alternative opensource pour matlab, et vous pouvez exécuter 99% du code matlab directement en octave.
mardi

2
Je pense que si vous plissez les yeux sur vos photos, vous pouvez toujours voir une structure à partir de l'entrée (ce qui est dû au fait que tous les pixels ne sont pas déplacés). J'imagine que si vous changiez votre algorithme de sélection pour qu'il s'exécute dans O (1) au lieu de O (), vous pourriez résoudre ce problème. ;)
Martin Ender

10

Mathematica-Utilise une permutation pour brouiller et son inverse pour déchiffrer.

Une image jpg est un tableau tridimensionnel de {r,g,b}couleurs de pixels. (Les 3 dimensions structurent l'ensemble des pixels par rangée, colonne et couleur). Il peut être mis à plat dans une liste de {r,g,b}triples, puis permuté selon une liste de cycles "connus", et enfin réassemblé dans un tableau des dimensions d'origine. Le résultat est une image brouillée.

Le désembrouillage prend l'image brouillée et la traite au verso de la liste des cycles. Il produit, oui, l'image d'origine.

Ainsi, une seule fonction (dans le cas présent scramble) sert à brouiller et à brouiller les pixels d'une image.

Une image est entrée avec un numéro de départ (pour s'assurer que le générateur de nombres aléatoires sera dans le même état pour l'embrouillage et le désembrouillage). Lorsque le paramètre reverse est False, la fonction sera brouillée. Quand il est vrai, la fonction va déchiffrer.


brouiller

Les pixels sont aplatis et une liste aléatoire de cycles est générée. Permute utilise des cycles pour changer la position des pixels dans la liste aplatie.

déchiffrer

La même fonction scrambleest utilisée pour le désembrouillage. Cependant, l'ordre de la liste des cycles est inversé.

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

Exemples

La même graine (37) est utilisée pour l'embrouillage et le désembrouillage.

Cela produit l'image brouillée de la montagne. L'image ci-dessous montre que la variable scrambledMount peut être remplacée par l'image réelle de la scène de montagne.

scrambledMount=scramble[mountain, 37, True]

mount1


Maintenant nous courons l'inverse; scrambledMount est entré et l'image originale est récupérée.

 scramble[scrambledMount, 37, True]

mount2


Même chose pour les cercles:

cercles1


 scramble[scrambledCircles, 37, True]

cercles2


Je ne vois pas comment une image pourrait être un tableau en 3 dimensions.
Edc65

1
@ edc65, Lignes x Colonnes x Couleurs. L'image de montagne est de 422 lignes de 800 colonnes de 3 couleurs. Si le tableau est aplati, il génère 1012800 éléments de données sous forme de tableau unidimensionnel, c’est-à-dire sous forme de liste.
DavidC

@ edc65 J'ajouterais que Permute a été utilisé pour réorganiser les couleurs en tant que triples rgb. Je ne l’aplatissais plus parce que je n’étais pas intéressé à modifier la liste des couleurs d’un pixel. Si vous considérez l'info rgb, {r, g, b} comme un élément, nous parlons alors d'un tableau 2D. Vu sous cet angle, il est tout à fait logique de poser la question (Comment une image peut-elle être un tableau en 3 dimensions?) Que vous avez soulevée. En fait, il est peut-être plus normal de considérer une image comme un tableau 2D, sans tenir compte du fait que les éléments rgb ajoutent une autre dimension.
DavidC

10

Python

J'aime ce casse-tête, il semblait intéressant et je suis arrivé avec une fonction d'emballage et de mouvement à appliquer sur l'image.

Enveloppé

Je lis la photo comme un texte (de gauche à droite, de haut en bas) et l'écris comme une coquille d'escargot.

Cette fonction est cyclique: il y a un dans N, f ^ (n) (x) = x par exemple, pour une image de 4 * 2, f (f (f (x))) = x

Mouvement

Je prends un nombre aléatoire et déplace chaque colonne et ligne de celle-ci

Code

# Opening and creating pictures
img = Image.open("/home/faquarl/Bureau/unnamed.png")
PM1 = img.load()
(w,h) = img.size
img2 = Image.new( 'RGBA', (w,h), "black") 
PM2 = img2.load()
img3 = Image.new( 'RGBA', (w,h), "black") 
PM3 = img3.load()

# Rotation
k = 0
_i=w-1
_j=h-1
_currentColMin = 0
_currentColMax = w-1
_currentLigMin = 0
_currentLigMax = h-1
_etat = 0
for i in range(w):
    for j in range(h):
        PM2[_i,_j]=PM1[i,j]
        if _etat==0:
            if _currentColMax == _currentColMin:
                _j -= 1
                _etat = 2
            else:
                _etat = 1
                _i -= 1
        elif _etat==1:
            _i -= 1
            if _j == _currentLigMax and _i == _currentColMin:
                _etat = 2
        elif _etat==2:
            _j -= 1
            _currentLigMax -= 1
            if _j == _currentLigMin and _i == _currentColMin:
                _etat = 5
            else:
                _etat = 3
        elif _etat==3:
            _j -= 1
            if _j == _currentLigMin and _i == _currentColMin:
                _etat = 4
        elif _etat==4:
            _i += 1
            _currentColMin += 1
            if _j == _currentLigMin and _i == _currentColMax:
                _etat = 7
            else:
                _etat = 5
        elif _etat==5:
            _i += 1
            if _j == _currentLigMin and _i == _currentColMax:
                _etat = 6
        elif _etat==6:
            _j += 1
            _currentLigMin += 1
            if _j == _currentLigMax and _i == _currentColMax:
                _etat = 1
            else:
                _etat = 7
        elif _etat==7:
            _j += 1
            if _j == _currentLigMax and _i == _currentColMax:
                _etat = 8
        elif _etat==8:
            _i -= 1
            _currentColMax -= 1
            if _j == _currentLigMax and _i == _currentColMin:
                _etat = 3
            else:
                _etat = 1
        k += 1
        if k == w * h:
            i = w
            j = h
# Movement
if w>h:z=w
else:z=h
rand.seed(z)
a=rand.randint(0,h)
for i in range(w):
  for j in range(h):
  if i%2==0:
    PM3[(i+a)%w,(j+a)%h]=PM2[i,j]
  else:
    PM3[(i-a)%w,(j-a)%h]=PM2[i,j]
# Rotate Again

Des photos

Première rotation: entrez la description de l'image ici

alors permutation: entrez la description de l'image ici

Et avec le dernier tour: entrez la description de l'image ici

Quant à l'autre exemple: entrez la description de l'image ici


2
Comment restaurez-vous l'image d'origine?
Trichoplax

Si c'est juste une rotation, je peux le faire un certain temps (cela dépend de la taille). Cependant, si j’avais les permutations, je ne suis pas sûr que ce soit cyclique, j’ai donc une deuxième fonction qui ne change que ce qui est PM2 [_i, _j] = PM1 [i, j] est devenu PM2 [i, j] = PM1 [ _i, _j] et PM3 [(i + a)% w, (j + a)% h] = PM2 [i, j] est devenu PM3 [(ia)% w, (ja)% h] = PM2 [i, j]. Je cherche un moyen de le faire sans changer ces deux lignes
Faquarl

8

VB.NET (+ bonus)

Ceci utilise l'idée de flawr, grâce à lui, mais utilise différents algorithmes d'échange et de vérification. Le programme code et décode de la même manière.

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

Images de sortie:


8

Après avoir rappelé qu'il est sur le point d'échanger des pixels et non de les modifier, voici ma solution:

Brouillé: entrez la description de l'image ici

Restauré: entrez la description de l'image ici

Ceci est fait en randomisant l'ordre des pixels, mais pour pouvoir le restaurer, la randomisation est fixée. Pour ce faire, utilisez un pseudo-aléatoire avec une valeur de départ fixe et générez une liste d’index décrivant les pixels à échanger. Comme l'échange sera le même, la même liste restaurera l'image d'origine.

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

Notez que l'utilisation de cet algorithme sur un format de compression avec perte ne produira pas le même résultat, car le format d'image modifiera les données. Cela devrait fonctionner correctement avec tout codec sans perte tel que PNG.


8

Mathematica

Nous définissons une fonction d'assistance het la fonction d'embrouillage scramblecomme suit :

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

Après avoir chargé une image, vous pouvez appeler scramble[img, k]kest n'importe quel entier pour brouiller l'image. Appeler à nouveau avec -kdécryptera. (Si kest 0, alors aucun changement n'est effectué.) Il kconvient généralement de choisir quelque chose comme 100ce qui donne une jolie image brouillée:

Exemple de sortie 1

Exemple de sortie 2


7

Matlab: Brouillage de lignes et de colonnes basé sur des invariances de somme de lignes / colonnes

Cela semblait être un casse-tête amusant, alors j'ai réfléchi à la question et proposé la fonction suivante. Il est basé sur l'invariance des sommes de valeur de pixel de ligne et de colonne lors d'un décalage circulaire: il décale chaque ligne, puis chaque colonne, de la somme totale des valeurs de pixel de la ligne / colonne (en supposant un uint8 pour un nombre entier dans la variable de décalage ) Ceci peut ensuite être inversé en décalant chaque colonne puis chaque ligne de leur valeur de somme dans la direction opposée.

Ce n'est pas aussi joli que les autres, mais j'aime bien le fait qu'il soit non aléatoire et entièrement spécifié par l'image - pas de paramètres de choix.

Je l'avais initialement conçu pour décaler chaque canal de couleur séparément, mais j'ai ensuite remarqué la spécification voulant que seuls les pixels complets soient déplacés.

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

Première image test Secont test image


6

Java

Ce programme échange aléatoirement des pixels (crée un mappage pixel à pixel), mais au lieu d’une fonction aléatoire, il utilise Math.sin () (entier x). C'est totalement réversible. Avec des images de test, il crée des motifs.

Paramètres: nombre entier (nombre de passes, nombre négatif à inverser, 0 ne fait rien), mage en entrée et image en sortie (peuvent être identiques). Le fichier de sortie doit être au format qui utilise la compression sans perte.

1 passe: entrez la description de l'image ici entrez la description de l'image ici

100 passes (il faut quelques minutes pour le faire): entrez la description de l'image ici entrez la description de l'image ici

Code:

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

public class Test{

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

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

6

Python 2.7 avec PIL

Un peu tard pour la fête, mais j'ai pensé que ce serait amusant de convertir les images en plaids (et vice versa). Tout d'abord, nous décalons les colonnes de 4 fois le nombre de colonnes (colonnes égales vers le bas, colonnes impaires vers le haut). Ensuite, nous décalons les lignes de gauche ou de droite de 4 fois le nombre de lignes (colonnes paires à gauche, colonnes impaires à droite).

Le résultat est assez Tartanish.

Pour inverser, nous les faisons simplement dans l’ordre inverse et décalons de la même manière.

Code

from PIL import Image

def slideColumn (pix, tpix, x, offset, height):
  for y in range(height):
    tpix[x,(offset+y)%height] = pix[x,y]

def slideRow (pix, tpix, y, offset, width):
  for x in range(width):
    tpix[(offset+x)%width,y] = pix[x,y]

def copyPixels (source, destination, width, height):
  for x in range(width):
    for y in range(height):
      destination[x,y]=source[x,y]

def shuffleHorizontal (img, tmpimg, factor, encoding):
  xsize,ysize = img.size
  pix = img.load()
  tpix = tmpimg.load()
  for y in range(ysize):
    offset = y*factor
    if y%2==0:
      offset = xsize-offset
    offset = (xsize + offset) % xsize
    if encoding:
      slideRow(pix,tpix,y,offset,xsize)
    else:
      slideRow(pix,tpix,y,-offset,xsize)
  copyPixels(tpix,pix,xsize,ysize)

def shuffleVertical (img, tmpimg, factor, encoding):
  xsize,ysize = img.size
  pix = img.load()
  tpix = tmpimg.load()
  for x in range(xsize):
    offset = x*factor
    if x%2==0:
      offset = ysize-offset
    offset = (ysize + offset) % ysize
    if encoding:
      slideColumn(pix,tpix,x,offset,ysize)
    else:
      slideColumn(pix,tpix,x,-offset,ysize)
  copyPixels(tpix,pix,xsize,ysize)


def plaidify (img):
  tmpimg = Image.new("RGB",img.size)
  shuffleVertical(img,tmpimg,4,True)
  shuffleHorizontal(img,tmpimg,4,True)

def deplaidify (img):
  tmpimg = Image.new("RGB",img.size)
  shuffleHorizontal(img,tmpimg,4,False)
  shuffleVertical(img,tmpimg,4,False)

Résultats

Le plaid de l'image 1:

les plaidifiés 1.jpg

La forme de plaid image 2:

le plaidifié 2.png


2
Très agréable! Est-il possible d'obtenir les diagonales selon un angle de 45 °?
Todd Lehman

2
Il est possible de changer les lignes de décalage en: offset = x*xsize/ysize et offset = y*ysize/xsize Mais, cela ne cache pas non plus vraiment l'image, malheureusement.
Jrrl

5

Python (+ bonus) - permutation des pixels

Dans cette méthode, chaque pixel sera placé sur une autre position, avec la contrainte que l'autre pixel soit placé sur la première position. Mathématiquement, il s’agit d’une permutation de longueur de cycle 2. En tant que telle, la méthode est elle-même inverse.

Rétrospectivement, il ressemble beaucoup à mfvonh, mais cette soumission est en Python et j'ai dû construire cette permutation moi-même.

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

Première image de test: Première image test Deuxième image de test: Deuxième image test


5

Python 2.7 + PIL, Inspiration des casse-tête coulissants

Juste eu une autre idée. Fondamentalement, cette méthode divise une image en blocs de taille égale, puis mélange leur ordre. Étant donné que la nouvelle commande est basée sur une graine fixe, il est possible de revenir complètement au processus en utilisant la même graine. De plus, avec le paramètre supplémentaire appelé granularité, il est possible d'obtenir des résultats différents et méconnaissables.

Résultats:

Original

original

Granularité 16

16

Granularité 13

13

Granularité 10

dix

Granularité 3

3

Granularité 2

2

Granularité 1

entrez la description de l'image ici

Original

original

Granularité 16

16

Granularité 13

13

Granularité 10

dix

Granularité 3

3

Granularité 2

2

Granularité 1

1

Code:

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

from PIL import Image
import random

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

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

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

    nblocks=grid_width_dim*grid_height_dim          #number of blocks

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

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

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

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

    return new_image



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

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

        counter+=1

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

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

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

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

    nblocks=grid_width_dim*grid_height_dim          #number of blocks

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

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

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

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

    return new_image

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

    width=im.size[0]

    grid_width_dim=width/block_width                        #dimension of the grid

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

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

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


if __name__ == '__main__':

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

    im=Image.open(filename)

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

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

5

47

94 lignes. 47 pour l'encodage, 47 pour le décodage.

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

entrez la description de l'image ici entrez la description de l'image ici

(converti en jpg)

entrez la description de l'image ici

(original réduit)

entrez la description de l'image ici


3
Une partie du motif original est visible à travers ces lignes. Cependant, cela ressemble à dessiner avec le doigt sur une fenêtre embuée.
Somnium

2
... + 184426 octets pour codegolf-35005_ref.rb?
Edc65

5

Matlab avec une pincée de théorie des groupes (+ bonus)

Dans cette approche, nous supposons que nous avons un nombre pair de pixels totaux. (Si ce n'est pas le cas, nous ignorons simplement un pixel). Nous devons donc choisir la moitié des pixels à permuter avec l'autre moitié. Pour cela, nous indexons tous les pixels 0jusqu'à 2N-1et considérons ces indices comme un groupe cyclique.

Parmi les nombres premiers, nous recherchons un nombre pqui n’est ni trop petit ni trop grand, et qui correspond à 2Nl’ordre de notre groupe. Cela signifie g générer notre groupe ou {k*g mod 2N | k=0,1,...,2N-1} = {0,1,...,2N-1}.

Nous choisissons donc les premiers Nmultiples d' gun ensemble et tous les autres indices comme l'autre ensemble, et échangeons simplement l'ensemble de pixels correspondant.

Si vous pchoisissez la bonne manière, le premier ensemble est réparti uniformément sur l’ensemble de l’image.

Les deux cas de test:

Légèrement hors sujet mais intéressant:

Pendant les tests, j'ai remarqué que si vous sauvegardez le fichier dans un fichier jpg (compressé avec perte) et remplacez-le par un fichier png compressé sans perte, vous verrez assez rapidement les artefacts de la compression et affichera les résultats de deux réarrangements consécutifs. :

Comme vous pouvez le constater, la compression jpg donne un résultat presque noir et blanc!

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

%read image
src = imread(inputname);

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

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

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

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

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

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

4

JavaScript (+ bonus) - répéteur de permutation de division de pixel

La fonction prend un élément d'image et

  1. Divise les pixels par 8.
  2. Effectue un échange réversible de groupes de pixels.
  3. Echange récursif si le groupe de pixels> = 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()
}

Les montagnes Cercles


4

Python 2.7 + PIL, brouilleur de colonne / ligne

Cette méthode brouille simplement les lignes et les colonnes de l'image. Il est possible de brouiller une seule des dimensions ou les deux. En outre, l'ordre de la nouvelle ligne / colonne brouillée est basé sur un mot de passe. En outre, une autre possibilité consiste à brouiller l'ensemble du tableau d'images sans tenir compte des dimensions.

Résultats:

Brouiller l'image entière:

entrez la description de l'image ici

entrez la description de l'image ici

Brouiller les colonnes:

entrez la description de l'image ici

entrez la description de l'image ici

Brouiller les lignes:

entrez la description de l'image ici

entrez la description de l'image ici

Brouillage des colonnes et des lignes:

entrez la description de l'image ici

entrez la description de l'image ici

J'ai aussi essayé d'appliquer plusieurs passages à l'image, mais les résultats finaux ne différaient pas beaucoup, seulement la difficulté à la déchiffrer.

Code:

from PIL import Image
import random,copy

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

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

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

    im.putdata(newpixels)

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

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

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

    im.putdata(newpixels)

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

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

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

    im.putdata(newpixels)

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

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

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

    im.putdata(newpixels)

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

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

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

    im.putdata(newpixels)

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

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

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

    im.putdata(newpixels)


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

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

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

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

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

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

3

Winforms C #

Image1: entrez la description de l'image ici

Image 2: entrez la description de l'image ici

Code source:

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

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

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

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

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

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

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

        dst.Save(trg);
    }


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

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

1

Python 3.6 + pypng

Riffle / Master Shuffle

#!/usr/bin/env python3.6

import argparse
import itertools

import png

def read_image(filename):
    img = png.Reader(filename)
    w, h, data, meta = img.asRGB8()
    return w, h, list(itertools.chain.from_iterable(
        [
            (row[i], row[i+1], row[i+2])
            for i in range(0, len(row), 3)
        ]
        for row in data
    ))

def riffle(img, n=2):
    l = len(img)
    base_size = l // n
    big_groups = l % n
    base_indices = [0]
    for i in range(1, n):
        base_indices.append(base_indices[-1] + base_size + int(i <= big_groups))
    result = []
    for i in range(0, base_size):
        for b in base_indices:
            result.append(img[b + i])
    for i in range(big_groups):
        result.append(img[base_indices[i] + base_size])
    return result

def master(img, n=2):
    parts = [[] for _ in range(n)]
    for i, pixel in enumerate(img):
        parts[i % n].append(pixel)
    return list(itertools.chain.from_iterable(parts))

def main():
    parser = argparse.ArgumentParser()

    parser.add_argument('infile')
    parser.add_argument('outfile')
    parser.add_argument('-r', '--reverse', action='store_true')
    parser.add_argument('-i', '--iterations', type=int, default=1)
    parser.add_argument('-n', '--groupsize', type=int, default=2)
    parser.add_argument('-c', '--complex', nargs='+', type=int)

    args = parser.parse_args()

    w, h, img = read_image(args.infile)

    if args.complex:
        if any(-1 <= n <= 1 for n in args.complex):
            parser.error("Complex keys must use group sizes of at least 2")
        if args.reverse:
            args.complex = [
                -n for n in reversed(args.complex)
            ]
        for n in args.complex:
            if n > 1:
                img = riffle(img, n)
            elif n < -1:
                img = master(img, -n)
    elif args.reverse:
        for _ in range(args.iterations):
            img = master(img, args.groupsize)
    else:
        for _ in range(args.iterations):
            img = riffle(img, args.groupsize)

    writer = png.Writer(w, h)
    with open(args.outfile, 'wb') as f:
        writer.write_array(f, list(itertools.chain.from_iterable(img)))


if __name__ == '__main__':
    main()

Mon algorithme applique le mélange aléatoire dans un sens et le mélange principal dans l'autre (puisque les deux sont inverses), plusieurs itérations chacune, mais chacune est généralisée pour se scinder en un nombre quelconque de sous-groupes au lieu de deux. L’effet est que vous pouvez créer une clé de permutation multi-itération car l’image ne sera pas restaurée sans connaître la séquence exacte des remaniements et du mélange principal. Une séquence peut être spécifiée avec une série d'entiers, avec des nombres positifs représentant des riffles et des nombres négatifs représentant des maîtres.

J'ai mélangé le paysage avec la clé [3, -5, 2, 13, -7]:

Paysage 3 -5 2 13 -7

Il est intéressant de noter que certaines choses intéressantes se produisent depuis [3, -5], où il reste des artefacts de l’image originale:

Paysage 3 -5

Voici le motif abstrait mélangé avec la clé [2, 3, 5, 7, -11, 13, -17]:

Cercles 2 3 5 7 -11 13 -17

Si un seul paramètre est incorrect dans la clé, le démêlage ne restaure pas l'image:

Bad Unshuffle

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.