인식 할 수 없도록 이미지의 픽셀을 재정렬 한 다음 다시 가져옵니다.


86

인식 할 수 없도록 이미지의 픽셀을 재 배열 할 수있는 프로그램을 만드십시오. 그러나 프로그램은 원본 이미지로 다시 변환 할 수 있어야합니다.

인코딩 및 디코딩을 위해 두 가지 기능을 작성할 수 있지만 반복적으로 적용한 기능 중 하나는 원래 이미지 (math- f(x) = 1 - x)를 제공하는 것이 보너스입니다.

또한 출력에서 ​​일부 패턴을 생성하면 보너스도 제공됩니다.

언어가 지원하는 경우 이미지가 1D / 2D 배열 또는 이미지 객체로 표시 될 수 있습니다. 픽셀 순서 만 변경할 수 있습니다!

인식하기 어려운 이미지를 생성하는 승자 코드로 선택하는 것이 합리적이지만 정확하게 측정하는 방법을 모른다면 상상할 수있는 모든 방법을 속일 수 있습니다. 따라서이 질문을 인기 콘테스트로 선택했습니다. 사용자가 가장 적합한 답변을 선택하도록하십시오!

테스트 이미지 1 (800 x 422px) : 테스트 이미지 2 (800 x 480px) : 코드 출력 이미지를 제공하십시오.


이 질문은 "출력이 이미지 인 이미지에 대한 암호화 알고리즘을 작성하십시오"라는 매우 긴 방법입니다.
David Richerby

3
@DavidRicherby… 동일한 픽셀 / 색상을 사용합니다. "일반 이미지"에 5 개의 검은 픽셀- "암호 이미지"에 5 개의 검은 픽셀.
Daniel Beck

2
@ user2992539 좋습니다.이 경우 타이 브레이커로 사용됨을 명시 적으로 지정할 수 있습니다. 그렇지 않다면 보너스라고 말하는 것은 그다지 의미가 없습니다.
Martin Ender

3
이 질문으로 인해 Arnold의 고양이지도를 생각하게되었습니다 . 나는 그것이 목적에 적합하다고 생각하지 않지만 같은 방식으로 흥미 롭습니다.지도를 충분히 반복하면 원래 이미지로 돌아갑니다.
trichoplax

4
이제 Stack Exchange 네트워크의 다른 곳 : 모든 장소의 Security.SE
Doorknob

답변:


58

Python 2.7 (PIL 포함)-의사 난수 없음

이미지를 2 x 2 블록으로 나누고 (나머지는 무시하고) 각 블록을 180도 회전 한 다음 3 x 3 블록, 4 등으로 매개 변수 BLKSZ까지 동일하게 수행합니다. 그런 다음 BLKSZ-1, BLKSZ-2에 대해 3, 2로 다시 똑같이 수행합니다. 스크램블 기능은 스크램블 기능입니다.

코드 :

from PIL import Image
import math

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

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


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

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

print("Done!")

블록 크기에 따라 계산에서 원본 이미지와 유사성을 근절 할 수 있습니다 (BLKSZ = 50). 여기에 이미지 설명을 입력하십시오 여기에 이미지 설명을 입력하십시오

또는 계산 효율을 높이십시오 (BLKSZ = 10). 여기에 이미지 설명을 입력하십시오 여기에 이미지 설명을 입력하십시오


6
BLKSZ가 이미지 크기의 약 절반 인 경우 최상의 결과를 얻을 수 있습니다. 어쨌든, 나는 알고리즘을 좋아하고 작은 BLKSZ의 경우 현대 미술처럼 보입니다! 시원한!
Somnium

11
나는 항상 파이썬을 찬성했다.
qwr

2에서 50까지의 모든 값을 스크램블링하는 대신 소수만 사용해야합니까?
Neil

@Neil 아마도 그것은 더 무작위적이고 덜 예술적으로 보일 것입니다.
Somnium

BLKSZ = 10풍경은 정말 멋지다!
wchargin

52

C #, Winform

편집 좌표 배열을 채우는 방법을 변경하면 다른 패턴을 가질 수 있습니다-아래 참조

이런 종류의 패턴을 좋아합니까?

경치

요약

보너스:

비명 스크램블 소리

모든 픽셀을 절반으로 정확히 한 번만 무작위로 스왑합니다. 스크램블 해제 (보너스)에 대해 동일한 절차를 반복하십시오.

암호

Scramble.cs

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

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

        public struct Coord
        {
            public int x, y;
        }

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

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

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

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

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

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

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

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

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

            }
        }
    }

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

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

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

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

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

        private uint* pixelData = null;

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

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

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

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

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

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

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

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

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

Scramble.designer.cs

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

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

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

        }


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

Program.cs

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

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

컴파일하려면 프로젝트 속성에서 '안전하지 않은 코드'를 확인하십시오.

복잡한 패턴

스크램블

작업 함수의 첫 부분을 Application.DoEvents까지 변경하십시오.

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

        Graphics gr = Graphics.FromImage(outb);

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

        gr.Dispose();

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

        int p2 = numpix/2;

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

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

1
흥미로운) 비슷한 접근법으로 출력에서보다 복잡한 패턴을 만들 수 있는지 궁금합니다.
Somnium

1
좋은 생각-홀수가 많을 때 중간 줄은 어떻게 되나요?
flawr

1
@flawr 분할은 픽셀 당입니다. 홀수의 픽셀이 있으면 마지막 픽셀은 그대로 유지됩니다. 홀수 행이있는 경우 가운데 행의 왼쪽 절반은 '상단'이고 오른쪽 절반은 '하단'입니다.
edc65

1
@ user2992539 바둑판까지 더 세분화 할 수 있다고 생각합니다. 세분이 많을수록 이미지를 더 잘 인식 할 수 있습니다.
edc65

7
"스크램블"버전처럼!)
Somnium

43

C, 임의 흐림, 쉽게 가역

파티에 늦었다. 여기 내 항목이 있습니다!

이 방법은 스크램블링 블러를 수행합니다. 나는 그것을 scramblur 라고 부른다 . 매우 간단합니다. 루프에서 임의의 픽셀을 선택한 다음 토 로이드 캔버스 모델에서 임의로 선택한 주변 픽셀로 교체합니다. "가까운 픽셀"의 의미 (1은 항상 인접한 픽셀을 선택 함을 의미), 반복 횟수 및 임의의 난수 시드를 정의하는 최대 거리를 지정합니다. 최대 거리가 클수록 반복 횟수가 클수록 결과가 흐려집니다.

음수의 반복을 지정하여 되돌릴 수 있습니다 (이것은 단순히 명령 행 인터페이스 편의이며 실제로 음의 반복과 같은 것은 없습니다). 내부적으로 사용자 지정 64 비트 LCPRNG (선형 유사 의사 난수 생성기)를 사용하고 값 블록을 사전 생성합니다. 이 테이블은 스크램블링 또는 스크램블링 해제를 위해 각각 블록을 순방향 또는 역방향으로 반복 할 수 있습니다.

데모

처음 두 이미지의 경우 아래로 스크롤하면 더 높은 최대 오프셋을 사용하여 각 이미지가 흐려집니다. 맨 위는 원래 이미지 (예 : 0 픽셀 오프셋)에 이어 1, 2, 4, 8, 16, 32, 64 아래의 모든 이미지에 대한 반복 횟수는 10⁶ = 1,000,000입니다.

두 번째 두 이미지의 경우 각 이미지는 최대 오프셋 256에서 0까지 점진적으로 낮은 오프셋 (예 : 가장 흐릿한 것부터 가장 흐릿한 것)을 사용하여 흐려 집니다 .

경치 요약

다음 두 이미지의 경우 여기 에서 여기 까지 진행률을 전체 크기로 볼 수 있습니다 .

나쁜 속보 심슨

암호

오늘 아침에 깨어 난 상태에서 약 한 시간 안에 이것을 해킹했으며 문서가 거의 없습니다. 며칠 후에 다시 방문하여 사람들이 요청하면 나중에 더 많은 문서를 추가 할 수 있습니다.

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

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

typedef uint8_t uint8;
typedef uint64_t uint64;

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

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

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

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

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

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

static uint64 lcg64_table[lcg64_table_length];

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

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

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

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

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

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

  char *line = NULL;
  size_t linecap = 0;

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

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

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

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

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

success:
  free(line);
  return image;

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

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

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

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

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

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

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

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

  lcg64_init(lcg64_seed);

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

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

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

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

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

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

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

  image_scramble(image, max_delta, iterations, lcg64_seed);

  image_write(image, stdout);

  image_discard(image);

  return 0;
}

4
이 답변을 지나서 스크롤하면 굉장해 보입니다
Thomas

1
이 답변은 정말 키가 큽니다. 여분의 이미지 (즉, 두 개의 테스트 이미지를 제외한 모든 이미지가 완전히 희미 함)를 오프 사이트 갤러리로 옮길 수 있다고 생각하십니까?
Tim S.

@TimS. — 끝났다! 작은 섬네일로 축소했습니다.
Todd Lehman

42

파이썬 3.4

  • 보너스 1 : 자기 역수 : 반복하면 원본 이미지가 복원됩니다.
  • 선택적 키 이미지 : 동일한 키 이미지를 다시 사용해야 만 원본 이미지를 복원 할 수 있습니다.
  • 보너스 2 : 출력 패턴 생성 : 키 이미지는 스크램블 된 픽셀로 근사됩니다.

보너스 2가 달성되면 추가 키 이미지를 사용하여 보너스 1이 손실되지 않습니다. 프로그램은 동일한 키 이미지로 다시 실행되는 경우 여전히 자기 역수입니다.

표준 사용법

테스트 이미지 1 :

스크램블 테스트 이미지 1

테스트 이미지 2 :

스크램블 테스트 이미지 2

인수로 단일 이미지 파일을 사용하여 프로그램을 실행하면 전체 이미지에서 픽셀이 고르게 스크램블 된 이미지 파일이 저장됩니다. 스크램블링 된 출력으로 다시 실행하면 스크램블링이 다시 적용된 이미지 파일이 다시 저장됩니다.이 스크램블링 프로세스는 자신의 역이므로 원본을 복원합니다.

스크램블링 프로세스는 모든 픽셀의 목록이 2 사이클로 분할되어 모든 픽셀이 하나의 다른 픽셀과 교체되기 때문에 자체 역 수행입니다. 두 번째로 실행하면 모든 픽셀이 처음 교환 된 픽셀과 교체되어 모든 것이 시작된 방식으로 돌아갑니다. 홀수의 픽셀이 있으면 움직이지 않는 픽셀이 있습니다.

2주기를 제안한 최초 의 mfvonh의 답변 덕분 입니다.

주요 이미지와 함께 사용

테스트 이미지 2를 주요 이미지로 사용하여 스크램블링 테스트 이미지 1

테스트 2가있는 스크램블 테스트 1

테스트 이미지 1을 키 이미지로하여 스크램블링 테스트 이미지 2

테스트 1 스크램블 테스트 2

두 번째 이미지 파일 인수 (키 이미지)로 프로그램을 실행하면 키 이미지를 기준으로 원본 이미지가 영역으로 분할됩니다. 이들 영역 각각은 개별적으로 2 사이클로 분할되어, 모든 스크램블링이 영역 내에서 발생하고 픽셀이 한 영역에서 다른 영역으로 이동하지 않는다. 이렇게하면 각 영역에 픽셀이 분산되므로 영역이 균일 한 얼룩덜룩 한 색이되지만 각 영역마다 평균 색상이 약간 다릅니다. 이것은 잘못된 색상으로 주요 이미지의 대략적인 근사치를 제공합니다.

다시 실행하면 각 영역에서 동일한 픽셀 쌍이 교체되므로 각 영역이 원래 상태로 복원되고 이미지가 전체적으로 다시 나타납니다.

이미지를 영역으로 나누는 것을 제안한 최초 의 edc65의 답변 덕분 입니다. 나는 임의의 영역을 사용하기 위해 이것을 확장하고 싶었지만 영역 1의 모든 것을 영역 2의 모든 것으로 바꾸는 접근법은 영역의 크기가 동일해야 함을 의미했습니다. 내 해결책은 영역을 서로 격리하고 각 영역을 섞습니다. 영역은 더 이상 비슷한 크기 일 필요가 없으므로 임의의 모양 영역을 적용하는 것이 더 간단 해집니다.

암호

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


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


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


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


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


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


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


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

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


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


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


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

JPEG 이미지 굽기

.jpg 파일은 매우 빠르게 처리되지만 너무 뜨겁게 실행됩니다. 원본을 복원 할 때 잔상이 남습니다.

jpg 화상

그러나 심각하게 손실 된 형식은 일부 픽셀 색상이 약간 변경되어 출력 자체가 유효하지 않게됩니다. 주요 이미지가 사용되고 픽셀 셔플 링이 영역으로 제한되면 모든 왜곡이 발생한 영역 내에 유지 된 다음 이미지가 복원 될 때 해당 영역에 고르게 퍼집니다. 영역 간 평균 왜곡의 차이는 영역간에 눈에 띄는 차이를 남기므로 스크램블링 프로세스에 사용 된 영역은 여전히 ​​복원 된 이미지에서 볼 수 있습니다.

스크램블링하기 전에 .png (또는 무손실 형식)로 변환하면 스크램블되지 않은 이미지가 번지거나 왜곡되지 않은 원본과 동일해야합니다.

화상이없는 png

작은 세부 사항

  • 영역에 최소 256 픽셀 크기가 부과됩니다. 이미지가 너무 작은 영역으로 분할되도록 허용 한 경우 스크램블링 후에도 원본 이미지가 여전히 부분적으로 보입니다.
  • 홀수의 픽셀을 가진 영역이 두 개 이상인 경우 두 번째 영역의 한 픽셀이 첫 번째 픽셀로 다시 할당됩니다. 이것은 홀수의 픽셀을 가진 하나의 영역 만이 존재할 수 있고, 따라서 하나의 픽셀 만이 스크램블되지 않은 상태로 남게됨을 의미한다.
  • 영역 수를 제한하는 세 번째 선택적 인수가 있습니다. 예를 들어 이것을 2로 설정하면 2 개의 톤 스크램블 이미지가 나타납니다. 관련된 이미지에 따라 나아지거나 나빠질 수 있습니다. 여기에 숫자를 지정하면 같은 숫자를 다시 사용해서 만 이미지를 복원 할 수 있습니다.
  • 원본 이미지의 고유 한 색상 수는 영역 수를 제한합니다. 원본 이미지가 두 톤이면 주요 이미지 나 세 번째 인수에 관계없이 최대 2 개의 영역 만있을 수 있습니다.

2
+1 박수! 나는 이것에 대해 모호하게 생각했지만 구현하기가 너무 어렵다는 것을 알았습니다.
edc65

1
훌륭합니다. 출품작을 제출했지만 주요 이미지 기능으로 인해 귀하의 작품이 더 좋습니다.
Todd Lehman

이 두 이미지가 서로 키가 어떻게 생겼는지 궁금 것 lardlad.com/assets/wallpaper/simpsons1920.jpgblogs.nd.edu/oblation/files/2013/09/BreakingBad.jpg (720x450로 축소 또는 의미가있는 것은 물론 JPEG 화상을 피하기 위해 PNG로 사전 변환 된 것).
Todd Lehman

2
@ToddLehman 내 알고리즘은 자신의 역수 여야 할 필요성에 의해 제한됩니다. 하나의 이미지를 섞어 다른 이미지와 닮은 흥미로운 방법을 찾고 싶다면 Mona Lisa 팔레트 에서 American Gothic을 살펴보십시오 . 이러한 프로그램 중 일부는 언급 한 이미지로 놀라운 일을합니다.
trichoplax

2
주요 이미지 기능은이 머리와 어깨를 나머지 부분 위에 배치합니다.
잭 에이 들리

33

다음은 변경에 대한 비 랜덤 변환입니다.

  1. 왼쪽에 모든 짝수 열을, 오른쪽에 모든 홀수 열을 넣습니다.
  2. 반복 nx 횟수
  3. ny시간에 대해서도 동일하게 수행

변환은 거의 자기 역수이므로 변환을 총 size_x횟수 (x 방향)로 반복 하면 원래 이미지가 반환됩니다. 정확한 수학을 찾지 못했지만 정수 배수의 배수를 사용 int(log_2(size_x))하면 가장 작은 유령 이미지로 가장 잘 섞입니다.

뒤섞인 산 여기에 이미지 설명을 입력하십시오

from numpy import *
from pylab import imread, imsave

def imshuffle(im, nx=0, ny=0):
    for i in range(nx):
        im = concatenate((im[:,0::2], im[:,1::2]), axis=1)
    for i in range(ny):
        im = concatenate((im[0::2,:], im[1::2,:]), axis=0)
    return im

im1 = imread('circles.png')
im2 = imread('mountain.jpg')

imsave('s_circles.png', imshuffle(im1, 7,7))
imsave('s_mountain.jpg', imshuffle(im2, 8,9))

이것이 첫 번째 단계 20 반복의 모습입니다 (nx = ny, 다른 해상도의 영향에 주목) 여기에 이미지 설명을 입력하십시오


7
정말 멋진 알고리즘입니다. 그리고 Lena Söderberg 사진을 사용하면 완전히 보너스를 받아야합니다. :)
Todd Lehman

Lena 항상

24

매스 매 티카

이것은 매우 간단합니다. 5 * nPixels임의의 좌표 쌍을 선택 하고 그 두 픽셀을 교체합니다 (그림이 완전히 가려 짐). 그것을 해독하기 위해 나는 반대로도 마찬가지입니다. 물론 두 단계에서 동일한 좌표 쌍을 얻으려면 PRNG를 시드해야합니다.

scramble[image_] := Module[
   {data, h, w, temp},
   data = ImageData@image;
   {h, w} = Most@Dimensions@data;
   SeedRandom[42];
   (
      temp = data[[#[[1]], #[[2]]]];
      data[[#[[1]], #[[2]]]] = data[[#2[[1]], #2[[2]]]];
      data[[#2[[1]], #2[[2]]]] = temp;
      ) & @@@
    Partition[
     Transpose@{RandomInteger[h - 1, 10*h*w] + 1, 
       RandomInteger[w - 1, 10*h*w] + 1}, 2];
   Image@data
   ];
unscramble[image_] := Module[
   {data, h, w, temp},
   data = ImageData@image;
   {h, w} = Most@Dimensions@data;
   SeedRandom[42];
   (
      temp = data[[#[[1]], #[[2]]]];
      data[[#[[1]], #[[2]]]] = data[[#2[[1]], #2[[2]]]];
      data[[#2[[1]], #2[[2]]]] = temp;
      ) & @@@
    Reverse@
     Partition[
      Transpose@{RandomInteger[h - 1, 10*h*w] + 1, 
        RandomInteger[w - 1, 10*h*w] + 1}, 2];
   Image@data
   ];

두 기능의 유일한 차이점은 Reverse@입니다 unscramble. 두 함수 모두 실제 이미지 객체를 사용합니다. 다음과 같이 사용할 수 있습니다.

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

outin동일하다. 다음 scr과 같은 모습입니다 :

여기에 이미지 설명을 입력하십시오 여기에 이미지 설명을 입력하십시오


4
큰! 문제는 Mathematica가 PRNG 알고리즘을 변경하려고 생각하면 오래된 인코딩 된 이미지를 디코딩하지 않기 때문에 PRNG를 직접 만드는 것이 더 안전하다는 것입니다!
Somnium

1
좋은. Permute 및 FindPermutation으로 동일한 결과를 얻을 수 있어야합니다.
DavidC

잘 모르겠습니다. 사이클 목록으로 원하는 정확한 순열을 입력 할 수 있습니다.
DavidC

@DavidCarraher 흠, 흥미 롭습니다. 그래도 사용에 대한 원래 순열을 기억할 필요는 FindPermutation없습니까?
마틴 엔더

아니면 어떤 것을 {c, a, b}[[{2, 3, 1}]]사용할 수 있습니까?
Somnium

22

C # (+ 대칭 알고리즘에 대한 보너스)

이것은 발견에 의해 작동 x되도록 x^2 == 1 mod (number of pixels in image)한 다음 각 픽셀의 인덱스를 곱하여 x새 위치를 찾기 위해. 이를 통해 동일한 알고리즘을 사용하여 이미지를 스크램블 및 스크램블 할 수 있습니다.

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

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

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

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

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

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

스크램블 된 첫 번째 테스트 이미지

두 번째 테스트 이미지, 스크램블


1
영리한 하나) 항상 그 합동 방정식에 대한 해결책이 있습니까?
Somnium

1
@ user2992539 항상 간단한 솔루션, 1(원본 이미지) 및 modulo-1(반전 / 역상 이미지)가 있습니다. 대부분의 숫자에는 사소한 해결책이 있지만 몇 가지 예외가 있습니다 . (의 소인수 분해와 관련됨 modulo)
Tim S.

내가 이해하는 것처럼 사소한 솔루션은 입력과 비슷한 이미지로 이어집니다.
Somnium

맞음 : 1원본 이미지를 -1출력합니다. 예 : imgur.com/EiE6VW2
Tim S.

19

C #, 자기 역수, 임의성 없음

원래 이미지의 크기가 2의 제곱 인 경우, 각 행과 열은 비트 패턴이 반전 된 행과 열과 교환됩니다 (예 : 너비 256의 이미지의 경우 행 0xB4는 행 0x2D와 교환 됨). 다른 크기의 이미지는 2의 거듭 제곱이있는 직사각형으로 분할됩니다.

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

        static void Scramble(string fileName)
        {
            using (var origImage = new System.Drawing.Bitmap(fileName))
            using (var tmpImage = new System.Drawing.Bitmap(origImage))
            {
                {
                    int x = origImage.Width;
                    while (x > 0) {
                       int xbit = x & -x;
                        do {
                            x--;
                            var xalt = BitReverse(x, xbit);
                            for (int y = 0; y < origImage.Height; y++)
                                tmpImage.SetPixel(xalt, y, origImage.GetPixel(x, y));
                        } while ((x & (xbit - 1)) != 0);
                    }
                }
                {
                    int y = origImage.Height;
                    while (y > 0) {
                        int ybit = y & -y;
                        do {
                            y--;
                            var yalt = BitReverse(y, ybit);
                            for (int x = 0; x < origImage.Width; x++)
                                origImage.SetPixel(x, yalt, tmpImage.GetPixel(x, y));
                        } while ((y & (ybit - 1)) != 0);
                    } 
                }
                origImage.Save(System.IO.Path.GetFileNameWithoutExtension(fileName) + "-scrambled.png");
            }
        }

        static int BitReverse(int n, int bit)
        {
            if (bit < 4)
                return n;
            int r = n & ~(bit - 1);
            int tmp = 1;
            while (bit > 1) {
                bit >>= 1;
                if ((n & bit) != 0)
                    r |= tmp;
                tmp <<= 1;
            }
            return r;
        }
    }
}

첫 번째 이미지 :

첫 번째 이미지를 스크램블

두 번째 이미지 :

스크램블 된 두 번째 이미지


2
나는 이것에 대한 "plaid"출력을 좋아한다.
브라이언 로저스

14

씨#

스크램블링 및 스크램블링 해제 방법과 동일합니다. 개선에 대한 제안에 감사드립니다.

using System;
using System.Drawing;
using System.Linq;

public class Program
{
    public static Bitmap Scramble(Bitmap bmp)
    {
        var res = new Bitmap(bmp);
        var r = new Random(1);

        // Making lists of even and odd numbers and shuffling them
        // They contain numbers between 0 and picture.Width (or picture.Height)
        var rX = Enumerable.Range(0, bmp.Width / 2).Select(x => x * 2).OrderBy(x => r.Next()).ToList();
        var rrX = rX.Select(x => x + 1).OrderBy(x => r.Next()).ToList();
        var rY = Enumerable.Range(0, bmp.Height / 2).Select(x => x * 2).OrderBy(x => r.Next()).ToList();
        var rrY = rY.Select(x => x + 1).OrderBy(x => r.Next()).ToList();

        for (int y = 0; y < bmp.Height; y++)
        {
            for (int x = 0; x < rX.Count; x++)
            {
                // Swapping pixels in a row using lists rX and rrX
                res.SetPixel(rrX[x], y, bmp.GetPixel(rX[x], y));
                res.SetPixel(rX[x], y, bmp.GetPixel(rrX[x], y));
            }
        }
        for (int x = 0; x < bmp.Width; x++)
        {
            for (int y = 0; y < rY.Count; y++)
            {
                // Swapping pixels in a column using sets rY and rrY
                var px = res.GetPixel(x, rrY[y]);
                res.SetPixel(x, rrY[y], res.GetPixel(x, rY[y]));
                res.SetPixel(x, rY[y], px);
            }
        }

        return res;
    }
}

환각 격자 무늬의 결과를 출력합니다 첫 번째 두번째 것


줄무늬 패턴이있는 것이
좋습니다

1
2 개의 이미지를 교환 해 주시겠습니까? 문제에서 산 이미지가 첫 번째입니다.
AL

1
알고리즘에 대한 간단한 설명을 포함시킬 수 있습니까?
trichoplax

14

파이썬 2 (자체 역, 무작위성이 없으며 상황에 따라 다름)

이것은 "가장 눈에 띄지 않는"상을받지는 않지만 "흥미로운"점수를받을 수 있습니다. :-)

나는 픽셀의 스크램블링이 실제로 이미지 자체에 의존하는 상황에 맞는 것을 만들고 싶었습니다.

아이디어는 매우 간단합니다. 픽셀의 색상에서 파생 된 임의의 값에 따라 모든 픽셀을 정렬 한 다음 해당 목록의 첫 번째 픽셀의 위치를 ​​마지막으로, 두 번째는 두 번째에서 마지막으로 등으로 바꿉니다.

불행히도이 간단한 접근 방식에서는 동일한 색상의 픽셀에 문제가 있으므로 여전히 자체 역으로 만들려면 프로그램이 조금 더 복잡해졌습니다 ...

from PIL import Image

img = Image.open('1.png', 'r')
pixels = img.load()
size_x, size_y = img.size

def f(colour):
    r,g,b = colour[:3]
    return (abs(r-128)+abs(g-128)+abs(b-128))//128

pixel_list = [(x,y,f(pixels[x,y])) for x in xrange(size_x) for y in xrange(size_y)]
pixel_list.sort(key=lambda x: x[2])
print "sorted"

colours = {}
for p in pixel_list:
    if p[2] in colours:
        colours[p[2]] += 1
    else:
        colours[p[2]] = 1
print "counted"

for n in set(colours.itervalues()):
    pixel_group = [p for p in pixel_list if colours[p[2]]==n]
    N = len(temp_list)
    for p1, p2 in zip(pixel_group[:N//2], pixel_group[-1:-N//2:-1]):
        pixels[p1[0],p1[1]], pixels[p2[0],p2[1]] = pixels[p2[0],p2[1]], pixels[p1[0],p1[1]]
print "swapped"

img.save('1scrambled.png')
print "saved"

결과는 다음과 같습니다. (abs (r-128) + abs (g-128) + abs (b-128)) // 128 (abs (r-128) + abs (g-128) + abs (b-128)) // 128

해시 함수를 변경하여 매우 다른 결과를 얻을 수 있습니다 f.

  • r-g-b:

    RGB

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

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

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

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

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

    (r + g + b) // 600

  • 0:

    0


3
와! 이거 대단해 !!! 잘 했어!
Todd Lehman

1
그것은 지금까지 가장 흥미로운 것입니다. 잘 했어!
bebe

12

매스 매 티카 (+ 보너스)

이렇게하면 색상 채널이 축소되고 이미지가 하나의 긴 데이터 목록으로 스크램블됩니다. 결과는 원본과 동일한 색상 분포를 갖지 않기 때문에 인식하기 어려운 스크램블 버전입니다 (데이터도 스크램블되었으므로). 이것은 두 번째 스크램블 이미지에서 가장 분명하지만, 자세히 보면 첫 번째에서도 동일한 효과가 나타납니다. 함수는 자체 역수입니다.

채널별로 스크램블되므로 유효하지 않을 수도 있다는 의견이있었습니다. 꼭 그래야한다고 생각하지만 큰 문제는 아닙니다. 필요한 유일한 변화는 변화하는 것입니다 (대신 채널당의) 전체 픽셀 출격 Flatten @ xFlatten[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]]

설명

f2 차원 배열을 취하는 함수 를 정의합니다 x. 이 함수는 이미지 크기의 곱을 임의의 시드로 사용한 다음 배열을 1 차원 목록 f(로컬로 음영 처리)으로 평면화합니다 . 그런 다음 형태의 목록 작성 의 길이를 2- (따라서, 예를 들어, 세그먼트로 분할하여, 임의로, 그리스트를 바꾸어 넣 치수 모두 홀수 인 경우 (최근 번호를 삭제)하고 순열로 의 값을 교환함으로써 방금 생성 된 각 하위 목록에 표시된 위치는 마지막으로 순열 된 목록을 원래 크기로 다시 모양을 바꾸며 이미지 크기를 축소하는 것 외에도 채널별로 스크램블됩니다.{1, 2, ... n}nf{{1, 2}, {3, 4}, ...}fxFlatten명령은 또한 각 픽셀의 채널 데이터를 축소합니다. 사이클에는 각각 2 개의 픽셀 만 포함되므로이 함수는 그 반대입니다.

용법

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

f @ img1 // Image

여기에 이미지 설명을 입력하십시오

f @ f @ img1 // Image

여기에 이미지 설명을 입력하십시오

f @ img2 // Image

여기에 이미지 설명을 입력하십시오

f @ f @ img2 // Image

여기에 이미지 설명을 입력하십시오

사용하고 Flatten[x, 1]있습니다.

g@x_ := With[{r = SeedRandom[Times @@ Dimensions @ x], f = Flatten[x, 1]}, 
  ArrayReshape[
   Permute[f, Cycles@Partition[RandomSample@Range@Length@f, 2]], 
   Dimensions@x]]

g@img2 // Image

여기에 이미지 설명을 입력하십시오


1
내 생각에 이것은 픽셀 스케일보다 작은 크기로 교체되기 때문에 기준에 맞지 않는 것입니다.
trichoplax

1
나는 그것이 올바른 대답이라고 생각하지 않지만 실제로 그것을 좋아합니다. 어쨌든 +1 어쨌든 +1 ...
trichoplax

1
@githubphagocyte 업데이트보기 :)
mfvonh

대단하다-다시 +1에 도달했지만 물론 두 번 할 수는 없습니다.
trichoplax

1
@githubphagocyte 아 맞다, 나는 짧은 구문이 기괴 할 수 있다는 것을 잊었다. 예. f @ f @ img1 // Imageis (전체 구문)Image[f[f[img1]]]
mfvonh

10

Matlab (+ 보너스)

기본적으로 두 픽셀의 위치를 ​​무작위로 전환하고 전환 된 각 픽셀에 태그를 지정하여 다시 전환되지 않도록합니다. 매번 난수 생성기를 재설정하기 때문에 동일한 스크립트를 '암호 해독'에 다시 사용할 수 있습니다. 이것은 거의 모든 픽셀이 전환 될 때까지 수행됩니다 (따라서 stepizes가 2보다 큽니다)

편집 : Martin Büttner가 비슷한 접근법을 사용하는 것을 보았습니다. 아이디어를 복사하지 않으려 고했습니다. 응답이 없을 때 코드를 작성하기 시작했기 때문에 죄송합니다. 나는 여전히 내 버전에 다른 아이디어를 사용한다고 생각합니다 =) (그리고 두 개의 임의 좌표가 선택되는 비트를 보면 알고리즘이 훨씬 비효율적입니다 ^^)

이미지

1 2

암호

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

완전히 이해하지 못합니다. 암호화 된 이미지에 두 번째로 적용된 코드가 암호를 해독합니까?
Somnium

2
정확히 : 정확히 두 픽셀이 바뀔 때마다 전체 프로세스 중에 다시 스왑되지 않습니다. 난수 생성기 재설정으로 인해 '임의'숫자가 정확히 같은 시간이기 때문에 픽셀 쌍이 다시 스왑됩니다. (RNG는 다음 번호를 생성하기 위해 항상 이전에 생성 된 번호에 의존합니다.이 사실을 분명히 바랍니다.)
flawr

1
하, 그것은 이었다 실제로 내 최초의 생각,하지만 난 일하러 가야했기 때문에 그때는 반드시 각 픽셀은 정확히 한 번만 교체되어 있는지 확인을 방해 할 수 없습니다. : D +1!
Martin Ender

3
@ user2992539 matlab을위한 훌륭한 오픈 소스 대안 인 Octave 를 확인하십시오 . matlab 코드의 99 %를 옥타브에서 직접 실행할 수 있습니다.
flawr

2
나는 당신이 당신의 사진을 정말로 열심히 찌르면 입력에서 일부 구조를 볼 수 있다고 생각합니다 (모든 픽셀을 움직이지 않기 때문에). 선택 알고리즘을 O (∞) 대신 O (1)에서 실행하도록 변경하면 문제를 해결할 수 있습니다. ;)
Martin Ender

10

수학-순열을 사용하여 스크램블하고 그 역을 스크램블합니다.

jpg 이미지는 {r,g,b}픽셀 색상 의 3 차원 배열입니다 . (3 차원은 행, 열 및 색상별로 픽셀 집합을 구성합니다). {r,g,b}트리플 의리스트로 편 평화 한 다음 "알려진"사이클리스트에 따라 순열 된 후, 최종 차원의 배열로 재 조립할 수 있습니다. 결과는 스크램블 된 이미지입니다.

디 스크램블링은 스크램블 된 이미지를 가져 와서 사이클 목록의 반대 방향으로 처리합니다. 예, 원본 이미지를 출력합니다.

따라서 단일 기능 (현재의 경우 scramble)은 이미지에서 픽셀을 스크램블링하고 스크램블링하지 않는 기능을합니다.

시드 번호와 함께 이미지가 입력됩니다 (임의의 숫자 생성기가 스크램블링 및 스크램블링을 위해 동일한 상태에 있음). 역의 파라미터가 False이면, 함수는 스크램블됩니다. True 인 경우 기능이 해독됩니다.


스크램블

픽셀이 평평 해지고 임의의 사이클 목록이 생성됩니다. Permute는주기를 사용하여 병합 된 목록에서 픽셀 위치를 전환합니다.

제대로 챙기다

동일한 기능 scramble이 스크램블 해제에 사용됩니다. 그러나 사이클리스트의 순서는 반대로됩니다.

scramble[img_,s_,reverse_:False,imgSize_:300]:=
  Module[{i=ImageData[img],input,r},input=Flatten[i,1];SeedRandom[s];
  r=RandomSample@Range[Length[input]];Image[ArrayReshape[Permute[input,
  Cycles[{Evaluate@If[reverse,Reverse@r,r]}]],Dimensions[i]],ImageSize->imgSize]]

스크램블링 및 스크램블링을 위해 동일한 시드 (37)가 사용됩니다.

이것은 산의 스크램블 이미지를 생성합니다. 아래 그림은 변수 scrambledMount가 산 장면의 실제 이미지로 대체 될 수 있음을 보여줍니다.

scrambledMount=scramble[mountain, 37, True]

mount1


이제 우리는 역을 실행합니다. scrambledMount가 입력되고 원본 사진이 회복됩니다.

 scramble[scrambledMount, 37, True]

mount2


서클도 마찬가지입니다.

서클 1


 scramble[scrambledCircles, 37, True]

서클 2


이미지가 어떻게 3 차원 배열이 될 수 있는지 알 수 없습니다.
edc65

1
@ edc65, 행 x 열 x 색상. 산 이미지는 422 행 x 800 열 3 색입니다. 배열이 평면화되면 1 차원 배열, 즉 목록으로 1012800 개의 데이터가 생성됩니다.
DavidC

@ edc65 Permute를 사용하여 색상을 RGB 트리플로 다시 정렬하는 데 추가해야합니다. 픽셀의 색상 목록에서 변경하는 데 관심이 없기 때문에 더 이상 평면화하지 않았습니다. rgb 정보 {r, g, b}를 요소로 생각하면 2D 배열에 대해 이야기하고 있습니다. 이런 식으로 보면 제기 한 질문 (이미지가 어떻게 3 차원 배열 일 수 있습니까?)을 제기하는 것이 합리적입니다. 실제로 RGB 요소가 다른 차원을 추가한다는 사실을 무시하고 이미지를 2D 배열로 간주하는 것이 더 일반적 일 수 있습니다.
DavidC

10

파이썬

나는이 퍼즐을 좋아한다. 그는 흥미로워 보였고 나는 이미지에 적용하기 위해 랩핑 및 이동 기능을 제공했다.

포장

그림을 텍스트 (왼쪽에서 오른쪽, 위아래로)로 읽고 달팽이 껍질로 씁니다.

이 함수는 주기적입니다. 예를 들어, 4 * 2의 사진의 경우, f ^ (n) (x) = x가 있습니다. f (f (f (x))) = x

운동

난수를 가져 와서 각 열과 리그 네를 옮깁니다.

암호

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

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

영화

첫 회전 : 여기에 이미지 설명을 입력하십시오

다음 순열 : 여기에 이미지 설명을 입력하십시오

그리고 마지막 회전으로 : 여기에 이미지 설명을 입력하십시오

다른 예는 다음과 같습니다. 여기에 이미지 설명을 입력하십시오


2
원본 이미지를 어떻게 복원합니까?
trichoplax

회전이라면 크기에 따라 일정 시간 동안 할 수 있습니다. 그러나 순열이 순환 형인지 확실하지 않으므로 PM2 [_i, _j] = PM1 [i, j]가 PM2 [i, j] = PM1 [ _i, _j] 및 PM3 [(i + a) % w, (j + a) % h] = PM2 [i, j]는 PM3 [(ia) % w, (ja) % h] = PM2 [i, 제이]. 나는이 두 줄을
바꾸지

8

VB.NET (+ 보너스)

덕분에 flawr의 아이디어를 사용하지만 다른 스와핑 및 검사 알고리즘을 사용합니다. 프로그램은 동일한 방식으로 인코딩 및 디코딩합니다.

Imports System

Module Module1

    Sub swap(ByVal b As Drawing.Bitmap, ByVal i As Integer, ByVal j As Integer)
        Dim c1 As Drawing.Color = b.GetPixel(i Mod b.Width, i \ b.Width)
        Dim c2 As Drawing.Color = b.GetPixel(j Mod b.Width, j \ b.Width)
        b.SetPixel(i Mod b.Width, i \ b.Width, c2)
        b.SetPixel(j Mod b.Width, j \ b.Width, c1)
    End Sub

    Sub Main(ByVal args() As String)
        For Each a In args
            Dim f As New IO.FileStream(a, IO.FileMode.Open)
            Dim b As New Drawing.Bitmap(f)
            f.Close()
            Dim sz As Integer = b.Width * b.Height - 1
            Dim w(sz) As Boolean
            Dim r As New Random(666)
            Dim u As Integer, j As Integer = 0
            Do While j < sz
                Do
                    u = r.Next(0, sz)
                Loop While w(u)
                ' swap
                swap(b, j, u)
                w(j) = True
                w(u) = True
                Do
                    j += 1
                Loop While j < sz AndAlso w(j)
            Loop
            b.Save(IO.Path.ChangeExtension(a, "png"), Drawing.Imaging.ImageFormat.Png)
            Console.WriteLine("Done!")
        Next
    End Sub

End Module

출력 이미지 :


8

이것이 픽셀을 교체하고 변경하지 않는다는 것을 상기시킨 후에 여기에 대한 해결책이 있습니다.

스크램블 : 여기에 이미지 설명을 입력하십시오

복원 : 여기에 이미지 설명을 입력하십시오

이것은 픽셀 순서를 무작위 화함으로써 이루어 지지만,이를 복원 할 수 있도록 무작위 화는 고정되어있다. 이것은 고정 된 시드와 함께 의사 난수를 사용하여 수행되며 교체 할 픽셀을 설명하는 인덱스 목록을 생성합니다. 스왑이 동일하므로 동일한 목록이 원본 이미지를 복원합니다.

public class ImageScramble {

  public static void main(String[] args) throws IOException {
    if (args.length < 2) {
      System.err.println("Usage: ImageScramble <fileInput> <fileOutput>");
    } else {
      // load image
      final String extension = args[0].substring(args[0].lastIndexOf('.') + 1);
      final BufferedImage image = ImageIO.read(new File(args[0]));
      final int[] pixels = image.getRGB(0, 0, image.getWidth(), image.getHeight(), null, 0, image.getWidth());

      // create randomized swap list
      final ArrayList<Integer> indexes = IntStream.iterate(0, i -> i + 1).limit(pixels.length).collect(ArrayList::new, ArrayList::add, ArrayList::addAll);
      Collections.shuffle(indexes, new Random(1337));

      // swap all pixels at index n with pixel at index n+1
      int tmp;
      for (int i = 0; i < indexes.size(); i += 2) {
        tmp = pixels[indexes.get(i)];
        pixels[indexes.get(i)] = pixels[indexes.get(i + 1)];
        pixels[indexes.get(i + 1)] = tmp;
      }

      // write image to disk
      final BufferedImage imageScrambled = new BufferedImage(image.getWidth(), image.getHeight(), image.getType());
      imageScrambled.setRGB(0, 0, imageScrambled.getWidth(), imageScrambled.getHeight(), pixels, 0, imageScrambled.getWidth());
      ImageIO.write(imageScrambled, extension, new File(args[1]));
    }
  }
}

손실 압축 형식에서이 알고리즘을 사용하면 이미지 형식이 데이터를 변경하므로 동일한 결과를 생성하지 않습니다. 이것은 PNG와 같은 무손실 코덱에서 잘 작동합니다.


8

매스 매 티카

헬퍼 함수 h와 스크램블링 함수 scramble는 다음 과 같이 정의합니다 .

h[l_List, n_Integer, k_Integer: 1] := 
  With[{ m = Partition[l, n, n, 1, 0] }, 
    Flatten[
      Riffle[
        RotateLeft[ m[[ ;; , {1} ]] , k ],
        m[[ ;; , 2;; ]]
      ], 1
    ] [[ ;; Length[l] ]]
  ];

scramble[img_Image, k_Integer] :=
  Module[{ list , cNum = 5 },
    Which[
      k > 0,    list = Prime@Range[cNum],
      k < 0,    list = Reverse@Prime@Range[cNum],
      True,     list = {}
    ];
    Image[
      Transpose[
        Fold[ h[ #1, #2, k ] &, #, list ] & /@
        Transpose[
          Fold[ h[#1, #2, k] &, #, list ] & /@ ImageData[img]
        ]
      ]
    ]
  ];

이미지를로드 한 후 정수가있는 scramble[img, k]곳 을 호출 k하여 이미지를 스크램블 할 수 있습니다 . 로 다시 전화하면 문제 -k가 해결됩니다. ( kis 0인 경우 변경되지 않습니다.) 일반적으로 k다음과 같이 선택해야합니다 100.

출력 예 1

출력 예 2


7

Matlab : 행 / 열 합 불변에 기반한 행 및 열 스크램블링

이것은 재미있는 퍼즐처럼 보였으므로 그것에 대해 생각하고 다음 기능을 생각해 냈습니다. 순환 이동 중 행 및 열 픽셀 값 합계의 불변에 기반합니다. 각 행을 이동 한 다음 각 열을 행 / 열의 픽셀 값의 총 합으로 시프트합니다 (shift-variable의 정수에 대해 uint8을 가정) ). 그런 다음 각 열을 이동 한 다음 반대 방향으로 합계 값을 기준으로 행을 바꾸면이를 되돌릴 수 있습니다.

그것은 다른 것만 큼 예쁘지 않지만, 나는 그것이 무작위가 아니며 이미지에 의해 완전히 지정되어 있기 때문에 선택 매개 변수가 없습니다.

원래 각 색상 채널을 개별적으로 이동하도록 설계했지만 전체 픽셀 만 이동하도록 사양을 확인했습니다.

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

첫 번째 테스트 이미지 두 번째 테스트 이미지


6

자바

이 프로그램은 무작위로 픽셀을 교체하고 (픽셀 대 픽셀 매핑을 생성), 임의의 함수 대신 Math.sin () (정수 x)을 사용합니다. 완전히 뒤집을 수 있습니다. 테스트 이미지를 사용하면 몇 가지 패턴이 생성됩니다.

매개 변수 : 정수 (패스 수, 역수-0, 아무것도하지 않음), 입력 마법사 및 출력 이미지 (동일 할 수 있음). 출력 파일은 무손실 압축을 사용하는 형식이어야합니다.

1 패스 : 여기에 이미지 설명을 입력하십시오 여기에 이미지 설명을 입력하십시오

100 패스 (몇 분 소요) : 여기에 이미지 설명을 입력하십시오 여기에 이미지 설명을 입력하십시오

암호:

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

public class Test{

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

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

6

PIL이 포함 된 Python 2.7

파티에 조금 늦었지만 이미지를 격자 무늬로 변환하는 것이 재미있을 것이라고 생각했습니다. 먼저 열 수를 열 수의 4 배만큼 늘리거나 줄입니다 (짝수 열도 홀수 열). 그런 다음 행 수를 행 수의 4 배 (왼쪽 열, 홀수 열 오른쪽)만큼 왼쪽 또는 오른쪽으로 이동합니다.

결과는 상당히 Tartanish입니다.

반대로, 우리는 이것을 반대 순서로 수행하고 반대 양만큼 이동합니다.

암호

from PIL import Image

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

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

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

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

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


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

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

결과

이미지 1의 격자 무늬 :

격자 무늬 1.jpg

격자 무늬 이미지 2 :

격자 무늬 2.png


2
아주 좋아요! 대각선을 45 ° 각도로 얻을 수 있습니까?
Todd Lehman

2
오프셋 선을 다음 offset = x*xsize/ysize 과 같이 변경하면 가능합니다. offset = y*ysize/xsize 그러나 불행히도 실제로 이미지를 숨기지는 않습니다.
jrrl

5

파이썬 (+ 보너스)-픽셀 순열

이 방법에서는 모든 픽셀이 다른 위치에 배치되고 다른 픽셀이 첫 번째 위치에 배치되어야한다는 제약이 따릅니다. 수학적으로 사이클 길이 2의 순열입니다. 따라서 방법은 자체 역수입니다.

돌이켜 보면 mfvonh와 매우 비슷하지만이 제출물은 Python으로되어 있으며이 순열을 직접 만들어야했습니다.

def scramble(I):
    result = np.zeros_like(I)
    size = I.shape[0:2]
    nb_pixels = size[0]*size[1]
    #Build permutation
    np.random.seed(0)
    random_indices = np.random.permutation( range(nb_pixels) )
    random_indices1 = random_indices[0:int(nb_pixels/2)]
    random_indices2 = random_indices[-1:-1-int(nb_pixels/2):-1]
    for c in range(3):
        Ic = I[:,:,c].flatten()
        Ic[ random_indices2 ] = Ic[random_indices1]
        Ic[ random_indices1 ] = I[:,:,c].flatten()[random_indices2]
        result[:,:,c] = Ic.reshape(size)
    return result

첫 번째 테스트 이미지 : 첫 번째 테스트 이미지 두 번째 테스트 이미지 : 두 번째 테스트 이미지


5

Python 2.7 + PIL, 슬라이딩 퍼즐의 영감

또 다른 아이디어가있었습니다. 기본적으로이 방법은 이미지를 동일한 크기의 블록으로 나누고 순서를 섞습니다. 새로운 주문은 고정 된 시드를 기반으로하기 때문에 동일한 시드를 사용하여 프로세스를 완전히 되돌릴 수 있습니다. 또한 세분성이라는 추가 매개 변수를 사용하면 인식 할 수없는 다른 결과를 얻을 수 있습니다.

결과 :

기발한

기발한

세분성 16

16

세분성 13

13

세분성 10

10

세분성 3

삼

세분성 2

2

세분성 1

여기에 이미지 설명을 입력하십시오

기발한

기발한

세분성 16

16

세분성 13

13

세분성 10

10

세분성 3

삼

세분성 2

2

세분성 1

1

암호:

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

from PIL import Image
import random

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

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

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

    nblocks=grid_width_dim*grid_height_dim          #number of blocks

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

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

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

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

    return new_image



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

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

        counter+=1

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

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

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

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

    nblocks=grid_width_dim*grid_height_dim          #number of blocks

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

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

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

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

    return new_image

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

    width=im.size[0]

    grid_width_dim=width/block_width                        #dimension of the grid

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

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

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


if __name__ == '__main__':

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

    im=Image.open(filename)

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

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

5

47

94 줄 인코딩 47, 디코딩 47

require 'chunky_png'
require_relative 'codegolf-35005_ref.rb'


REF = {:png => ref, :w => 1280, :h => 720}
REF[:pix] = REF[:png].to_rgb_stream.unpack('C*').each_slice(3).to_a
SEVENTH_PRIME = 4*7 - 4-7 - (4&7)
FORTY_SEVEN   = 4*7 + 4+7 + (4&7) + (4^7) + 7/4
THRESHOLD     = FORTY_SEVEN * SEVENTH_PRIME


class RNG
    @@m = 2**32
    @@r = 0.5*(Math.sqrt(5.0) - 1.0)
    def initialize(n=0)
        @cur = FORTY_SEVEN + n
    end
    def hash(seed)
        (@@m*((seed*@@r)%1)).floor
    end
    def _next(max)
        hash(@cur+=1) % max
    end
    def _prev(max)
        hash(@cur-=1) % max
    end        
    def advance(n)
        @cur += n
    end
    def state
        @cur
    end
    alias_method :rand, :_next
end


def load_png(file, resample_w = nil, resample_h = nil)
    png  = ChunkyPNG::Image.from_file(file)
    w    = resample_w || png.width
    h    = resample_h || png.height
    png.resample_nearest_neighbor!(w,h) if resample_w || resample_h
    pix  = png.to_rgb_stream.unpack('C*').each_slice(3).to_a
    return {:png => png, :w => w, :h => h, :pix => pix}
end


def make_png(img)
    rgb_stream = img[:pix].flatten.pack('C*')
    img[:png] = ChunkyPNG::Canvas.from_rgb_stream(img[:w],img[:h],rgb_stream)
    return img
end


def difference(pix_a,pix_b)
    (pix_a[0]+pix_a[1]+pix_a[2]-pix_b[0]-pix_b[1]-pix_b[2]).abs
end


def code(img, img_ref, mode)
    img_in  = load_png(img)
    pix_in  = img_in[:pix]
    pix_ref = img_ref[:pix]
    s = img_in[:w] * img_in[:h] 
    rng = RNG.new(mode==:enc ? 0 : FORTY_SEVEN*s+1)
    rand = mode == :enc ? rng.method(:_next) : rng.method(:_prev)
    s.times do
        FORTY_SEVEN.times do
            j = rand.call(s)
            i = rng.state % s
            diff_val = difference(pix_ref[i],pix_ref[j])
            if diff_val > THRESHOLD
               pix_in[i], pix_in[j] = pix_in[j], pix_in[i]
            end
        end
    end
    make_png(img_in)
end


case ARGV.shift
when 'enc'
    org, cod = ARGV
    encoded_image = code(org,REF,:enc)
    encoded_image[:png].save(cod)
when 'dec'
    org, cod = ARGV
    decoded_image = code(cod,REF,:dec)
    decoded_image[:png].save(org)
else
    puts '<original> <coded>'
    puts 'specify either <enc> or <dec>'
    puts "ruby #{$0} enc codegolf-35005_inp.png codegolf-35005_enc.png"
end

codegolf-35005_ref.rb

여기에 이미지 설명을 입력하십시오 여기에 이미지 설명을 입력하십시오

(jpg로 변환)

여기에 이미지 설명을 입력하십시오

(원래 축소)

여기에 이미지 설명을 입력하십시오


3
원래 패턴 중 일부는 해당 선을 통해 볼 수 있습니다. 그러나 안개가 자욱한 창에 손가락으로 그리면 비슷합니다.
Somnium

2
... + codegolf-35005_ref.rb의 경우 184426 바이트?
edc65

5

그룹 이론의 꼬집음과 함께 Matlab (+ 보너스)

이 방법에서는 총 픽셀 수가 짝수라고 가정합니다. (그렇지 않으면 하나의 픽셀 만 무시합니다) 따라서 다른 절반과 교체하려면 절반의 픽셀을 선택해야합니다. 에서이를 위해, 우리는 인덱스 모든 픽셀 0까지 2N-1하고 이러한 지표 고려 순환 그룹.

소수 중에서 우리 p는 너무 작지 않고 너무 크지 않은, 그리고 2N우리 그룹의 순서 와 같은 숫자 를 찾습니다 . 이것은 그룹을 g 생성 한다는 의미 {k*g mod 2N | k=0,1,...,2N-1} = {0,1,...,2N-1}입니다.

따라서 첫 번째 N배수 g를 하나의 세트로 선택하고 나머지 모든 인덱스를 다른 세트로 선택하고 해당 픽셀 세트를 바꾸십시오.

경우 p적당한 방법을 선택하고, 첫번째 세트 균일 화상 전체에 걸쳐 분포한다.

두 가지 테스트 사례 :

주제를 약간 벗어 났지만 흥미 로움 :

테스트하는 동안, 손실없이 압축 된 PNG 대신 (손실 압축) jpg에 저장하고 변환을 앞뒤로 적용하면 압축의 인공물을 매우 빨리 볼 수 있습니다. 이것은 두 개의 연속 재배 열의 결과를 보여줍니다 :

보다시피 jpg 압축은 결과를 거의 흑백으로 보이게합니다!

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

%read image
src = imread(inputname);

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

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

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

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

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

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

4

JavaScript (+ 보너스)-픽셀 분할 스왑 리피터

이 함수는 이미지 요소를 가져와

  1. 픽셀을 8로 나눕니다.
  2. 픽셀 그룹의 가역적 스왑을 수행합니다.
  3. 픽셀 그룹> = 8 인 경우 스와핑을 반복합니다.
function E(el){
    var V=document.createElement('canvas')
    var W=V.width=el.width,H=V.height=el.height,C=V.getContext('2d')
    C.drawImage(el,0,0)
    var id=C.getImageData(0,0,W,H),D=id.data,L=D.length,i=L/4,A=[]
    for(;--i;)A[i]=i
    function S(A){
        var L=A.length,x=L>>3,y,t,i=0,s=[]
        if(L<8)return A
        for(;i<L;i+=x)s[i/x]=S(A.slice(i,i+x))
        for(i=4;--i;)y=[6,4,7,5,1,3,0,2][i],t=s[i],s[i]=s[y],s[y]=t
        s=[].concat.apply([],s)
        return s
    }
    var N=C.createImageData(W,H),d=N.data,A=S(A)
    for(var i=0;i<L;i++)d[i]=D[(A[i>>2]*4)+(i%4)]
    C.putImageData(N,0,0)
    el.src=C.canvas.toDataURL()
}

산 서클


4

Python 2.7 + PIL, 열 / 행 스크램블러

이 방법은 단순히 이미지의 행과 열을 스크램블합니다. 치수 중 하나만 또는 둘 다를 스크램블하는 것이 가능합니다. 또한 새로운 스크램블 행 / 열의 순서는 암호를 기반으로합니다. 또한, 다른 가능성은 치수를 고려하지 않고 전체 이미지 어레이를 스크램블링하는 것이다.

결과 :

전체 이미지 스크램블 :

여기에 이미지 설명을 입력하십시오

여기에 이미지 설명을 입력하십시오

열 스크램블링 :

여기에 이미지 설명을 입력하십시오

여기에 이미지 설명을 입력하십시오

행 스크램블링 :

여기에 이미지 설명을 입력하십시오

여기에 이미지 설명을 입력하십시오

열과 행을 모두 스크램블 :

여기에 이미지 설명을 입력하십시오

여기에 이미지 설명을 입력하십시오

또한 이미지에 여러 번 실행을 시도했지만 최종 결과는 크게 다르지 않고 해독하기가 어렵습니다.

암호:

from PIL import Image
import random,copy

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

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

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

    im.putdata(newpixels)

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

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

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

    im.putdata(newpixels)

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

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

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

    im.putdata(newpixels)

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

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

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

    im.putdata(newpixels)

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

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

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

    im.putdata(newpixels)

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

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

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

    im.putdata(newpixels)


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

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

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

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

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

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

3

C # Winforms

이미지 1 : 여기에 이미지 설명을 입력하십시오

이미지 2 : 여기에 이미지 설명을 입력하십시오

소스 코드:

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

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

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

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

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

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

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

        dst.Save(trg);
    }


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

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

1

파이썬 3.6 + pypng

리플 / 마스터 셔플

#!/usr/bin/env python3.6

import argparse
import itertools

import png

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

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

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

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

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

    args = parser.parse_args()

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

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

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


if __name__ == '__main__':
    main()

내 알고리즘은 한 방향으로 리플 셔플을 적용하고 다른 방향으로 마스터 셔플을 적용합니다 (두 개가 서로 역이기 때문에). 그 결과 정확한 리플 및 마스터 셔플 시퀀스를 몰라도 이미지가 복원되지 않으므로 다중 반복 순열 키를 만들 수 있습니다. 시퀀스는 일련의 정수로 지정할 수 있으며, 양수는 리플을 나타내고 음수는 마스터를 나타냅니다.

[3, -5, 2, 13, -7] 키를 사용하여 풍경을 섞었습니다.

조경 3-5 2 13-7

흥미롭게도 원본 이미지의 일부 아티팩트가 남아있는 [3, -5]에서 흥미로운 일이 발생합니다.

풍경 3-5

다음은 키 [2, 3, 5, 7, -11, 13, -17]로 섞인 추상 패턴입니다.

서클 2 3 5 7 -11 13-17

키에서 하나의 매개 변수 만 잘못 입력하면 셔플 해제가 이미지를 복원하지 않습니다.

배드 언플 러플

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.