!pip install qiskit ipywidgets pylatexenc python-resize-image qiskit[visualization]

from qiskit import IBMQ, QuantumCircuit, ClassicalRegister, QuantumRegister
from qiskit import execute, QuantumRegister
from qiskit.qasm import pi
from qiskit.tools.visualization import plot_histogram, circuit_drawer, plot_state_city
from qiskit import execute, Aer, BasicAer, transpile
import numpy as np
import matplotlib.pyplot as plt
from PIL import Image
from resizeimage import resizeimage
import random
import frqi
import qiskit.quantum_info as qi
import math

# Quantum Fourier Transform

def qft_rotations(circuit, n):
    if n == 0: # Exit function if circuit is empty
        return circuit
    n -= 1 # Indexes start from 0
    circuit.h(n) # Apply the H-gate to the most significant qubit
    for qubit in range(n):
        # For each less significant qubit, we need to do a
        # smaller-angled controlled rotation:
        circuit.cp(pi/2**(n-qubit), qubit, n)

def swap_registers(circuit, n):
    for qubit in range(n//2):
        circuit.swap(qubit, n-qubit-1)
    return circuit

def qft(circuit, n):
    """QFT on the first n qubits in circuit"""
    qft_rotations(circuit, n)
    swap_registers(circuit, n)
    return circuit

# Inverse Quantum Fourier Transform

def qft_dagger(qc, n):
    """n-qubit QFTdagger the first n qubits in circ"""
    # Don't forget the Swaps!
    for qubit in range(n//2):
        qc.swap(qubit, n-qubit-1)
    for j in range(n):
        for m in range(j):
            qc.cp(-math.pi/float(2**(j-m)), m, j)
        qc.h(j)

anc = QuantumRegister(1, "anc")
img = QuantumRegister(11, "img")
anc2 = QuantumRegister(1, "anc2")
c = ClassicalRegister(12)

qc = QuantumCircuit(anc, img, anc2, c)

imageNames = ["1.jpg", "2.jpg", "3.jpg"]
imageNum1 = 0
imageNum2 = 2

image1 = Image.open(imageNames[imageNum1]).convert('LA')
image2 = Image.open(imageNames[imageNum2]).convert('LA')

def image_normalization(image):
    image = resizeimage.resize_cover(image, [32, 32])
    w, h = 32, 32
    image = np.array([[image.getpixel((x,y))[0] for x in range(w)] for y in range(h)])

    # 2-dimensional data convert to 1-dimensional array
    image = image.flatten()
    # change type
    image = image.astype('float64')
    # Normalization(0~pi/2)
    image /= 255.0
    generated_image = np.arcsin(image)

    return generated_image

image1 = image_normalization(image1)
image2 = image_normalization(image2)


# apply hadamard gates
for i in range(1, len(img)):
    qc.h(img[i])

# encode ref image
for i in range(len(image1)):
        if image1[i] != 0:
                frqi.c10ry(qc, 2 * image1[i], format(i, '010b'), img[0], anc2[0], [img[j] for j in range(1,len(img))])





# TRANSFORMATION 1: Quantum Fourier Transform
qft(qc, len(img))

# DECRYPTION FOR 1: Inverse Quantum Fourier Transform
qft_dagger(qc, len(img))

# TRANSFORMATION 2: Rotation in Frequency Space

qft(qc, len(img))

# random.random() * 2 * math.pi
angle = 0.1
for i in range(len(img)):
  qc.rx(angle, i)

qft_dagger(qc, len(img))

# TRANSFORMATION 3 : Randomized Angle on Each Qubit Transform (like an encryption algorithm)
angleArr = []
for i in range(len(img)):
  angle = random.random() * 2 * math.pi
  angleArr.append(angle)
  qc.rx(angle, i)

# DECRYPTION FOR 3: Randomized Angle on Each Qubit Transform (like an encryption algorithm)

for i in range(len(img)):
  qc.rx(-angleArr[i], i)



# TRANSFORMATION 4 : Uniform Random Rotation on all Qbits
angle = random.random() * 2 * math.pi
for i in range(len(img)):
  qc.rx(angle, i)

qc.measure(anc, c[0])
qc.measure(img, c[1:12])
print(qc.depth())
numOfShots = 8192
result = execute(qc, Aer.get_backend('qasm_simulator'), shots=numOfShots, backend_options={"fusion_enable":True}).result()
#circuit_drawer(qc).show()
#plot_histogram(result.get_counts(qc))

print(result.get_counts(qc))

# generated image
genimg = np.array([])

#### decode
for i in range(len(image1)):
        try:
                genimg = np.append(genimg,[np.sqrt(result.get_counts(qc)[format(i, '010b')+'10']/numOfShots)])
        except KeyError:
                genimg = np.append(genimg,[0.0])

# inverse nomalization
genimg *= 32.0 * 255.0

# convert type
genimg = genimg.astype('int')

# back to 2-dimensional data
genimg = genimg.reshape((32,32))

plt.imshow(genimg, cmap='gray', vmin=0, vmax=255)
plt.savefig('gen_'+str(imageNum1)+'.png')
plt.show()