From 554565f358d919ac266f6d605a53e88e0c56f088 Mon Sep 17 00:00:00 2001 From: p65192740 Date: Wed, 29 Nov 2023 19:49:50 +0800 Subject: [PATCH] ADD file via upload --- main.py | 302 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 302 insertions(+) create mode 100644 main.py diff --git a/main.py b/main.py new file mode 100644 index 0000000..0556469 --- /dev/null +++ b/main.py @@ -0,0 +1,302 @@ +import sys, os +from ui import Ui_Form +from PyQt5 import QtWidgets, QtCore, QtGui, QtSvg +from PIL import Image, ImageQt + +class UiMain(QtWidgets.QMainWindow, Ui_Form): + + def __init__(self, parent = None): + super(UiMain, self).__init__(parent) + self.setupUi(self) + self.initSlot() + self.show() + + def initSlot(self): + self.pushButtonOpenCover.clicked.connect(self.EtOpenCover) + self.pushButtonClearCover.clicked.connect(self.EtCleanCover) + self.pushButtonSaveCover.clicked.connect(self.EtSaveCover) + + self.pushButtonOpenSecret.clicked.connect(self.EtOpenSecret) + self.pushButtonClearSecret.clicked.connect(self.EtCleanSecret) + self.pushButtonSaveSecret.clicked.connect(self.EtSaveSecret) + + self.pushButtonOpenContainer.clicked.connect(self.EtOpenContainer) + self.pushButtonClearContainer.clicked.connect(self.EtCleanContainer) + self.pushButtonSaveContainer.clicked.connect(self.EtSaveContainer) + + self.pushButtonEncode.clicked.connect(self.EtEncode) + self.pushButtonDecode.clicked.connect(self.EtDecode) + + def EtCleanCover(self): + self.labelCover.setPixmap(QtGui.QPixmap("")) + + def EtCleanSecret(self): + self.labelSecret.setPixmap(QtGui.QPixmap("")) + + def EtCleanContainer(self): + self.labelContainer.setPixmap(QtGui.QPixmap("")) + + def EtOpenCover(self): + path, fileType = QtWidgets.QFileDialog.getOpenFileName(self, "选取文件", os.getcwd(), "所有文件 (*)") + if len(path) == 0: return + self.PCover, self.QCover = self.loadImage(path, self.labelCover) + + def EtOpenSecret(self): + path, fileType = QtWidgets.QFileDialog.getOpenFileName(self, "选取文件", os.getcwd(), "所有文件 (*)") + if len(path) == 0: return + self.PSecret, self.QSecret = self.loadImage(path, self.labelSecret) + + def EtOpenContainer(self): + path, fileType = QtWidgets.QFileDialog.getOpenFileName(self, "选取文件", os.getcwd(), "所有文件 (*)") + if len(path) == 0: return + self.PContainer, self.QContainer = self.loadImage(path, self.labelContainer) + + def EtSaveCover(self): + dirpath = QtWidgets.QFileDialog.getSaveFileName(self, '选择保存路径', os.getcwd(), "所有文件 (*)") + print(dirpath[0]) + self.PCover.save(dirpath[0]) + + def EtSaveSecret(self): + dirpath = QtWidgets.QFileDialog.getSaveFileName(self, '选择保存路径', os.getcwd(), "所有文件 (*)") + print(dirpath[0]) + self.PSecret.save(dirpath[0]) + + def EtSaveContainer(self): + dirpath = QtWidgets.QFileDialog.getSaveFileName(self, '选择保存路径', os.getcwd(), "所有文件 (*)") + print(dirpath[0]) + self.PContainer.save(dirpath[0]) + + def EtEncode(self): +# self.PContainer = self.PCover + self.PContainer = encode(self.PCover, self.PSecret) + self.setLabelImage(ImageQt.toqimage(self.PCover), self.labelContainer) + + def EtDecode(self): + self.EtCleanCover() + self.PSecret = decode(self.PContainer) +# self.setLabelImage(ImageQt.toqimage(self.PCover), self.labelCover) + self.setLabelImage(ImageQt.toqimage(self.PSecret), self.labelSecret) + + def loadImage(self, path: str, label: QtWidgets.QLabel): + p = Image.open(path) + q = ImageQt.toqimage(p) + self.setLabelImage(q, label) + return p,q + + def setLabelImage(self, qimage: QtGui.QImage, label: QtWidgets.QLabel): + rect1 = label.rect() + rect2 = qimage.rect() + xscale = rect1.width() / rect2.width() + yscale = rect1.height() / rect2.height() + scale = min(xscale, yscale) + trans = QtGui.QTransform() + trans.scale(scale, scale) + q = qimage.transformed(trans) + label.setPixmap(QtGui.QPixmap.fromImage(q)) + +def add_leading_zeros(binary_number, expected_length): + """ + Adds leading zeros to a binary number so that the number of characters + in the binary string matches the specified expected length and the value + of the binary sring remains unchanged. + + Args: + binary_number: A string representation of a number in base 2 + expected_length: Expected length of the binary number string + + Returns: + A binary string of a length specified by the argument with the + same numerical value as the binary number from the first argument. + """ + length = len(binary_number) + return (expected_length - length) * '0' + binary_number + +def rgb_to_binary(r, g, b): + """ + Converts decimal numbers representing RGB values of a pixel into + binary numbers of the same values. + + Args: + r: Decimal representation of the red channel value + g: Decimal representation of the green channel value + b: Decimal representation of the blue channel value + + Returns: + Binary representations of the red, green, and blue channel values + """ + return add_leading_zeros(bin(r)[2:], 8), add_leading_zeros(bin(g)[2:], 8), add_leading_zeros(bin(b)[2:], 8) + +def get_binary_pixel_values(img, width, height): + """ + Retrieves a string of concatenated binary representations of RGB channel values of all pixels in an image. + + Args: + img: An RGB image + width: Width of the image + height: Height of the image + + Returns: + A string with concatenated binary numbers representing the RGB channel values of all pixels in the image + where each binary number representing one channel value is 8 bits long, padded with leading zeros + when necessary. Therefore, each pixel in the image is represented by 24 bit long binary sequence. + """ + hidden_image_pixels = '' + for col in range(width): + for row in range(height): + pixel = img[col, row] + r = pixel[0] + g = pixel[1] + b = pixel[2] + r_binary, g_binary, b_binary = rgb_to_binary(r, g, b) + hidden_image_pixels += r_binary + g_binary + b_binary + return hidden_image_pixels + +def change_binary_values(img_visible, hidden_image_pixels, width_visible, height_visible, width_hidden, height_hidden): + """ + Replaces the 4 least significant bits of a subset of pixels in an image with bits representing a sequence of binary + values of RGB channels of all pixels of the image to be concealed. + + The first pixel in the top left corner is used to store the width and height of the image to be hidden, which is + necessary for recovery of the hidden image. + + Args: + img_visible: An RGB image to be used for hiding another image + hidden_image_pixels: Binary string representing all pixel values of the image to be hidden + width_visible: Width of the image to be used for hiding another image + height_visible: Height of the image to be used for hiding another image + width_hidden: Width of the image to be hidden + height_hidden: Height of the image to be hidden + + Returns: + An RGB image which is a copy of img_visible where the 4 least significant bits of a subset of pixels + are replaced with bits representing the hidden image. + """ + idx = 0 + for col in range(width_visible): + for row in range(height_visible): + if row == 0 and col == 0: + width_hidden_binary = add_leading_zeros(bin(width_hidden)[2:], 12) + height_hidden_binary = add_leading_zeros(bin(height_hidden)[2:], 12) + w_h_binary = width_hidden_binary + height_hidden_binary + img_visible[col, row] = (int(w_h_binary[0:8], 2), int(w_h_binary[8:16], 2), int(w_h_binary[16:24], 2)) + continue + r, g, b = img_visible[col, row] + r_binary, g_binary, b_binary = rgb_to_binary(r, g, b) + r_binary = r_binary[0:4] + hidden_image_pixels[idx:idx+4] + g_binary = g_binary[0:4] + hidden_image_pixels[idx+4:idx+8] + b_binary = b_binary[0:4] + hidden_image_pixels[idx+8:idx+12] + idx += 12 + img_visible[col, row] = (int(r_binary, 2), int(g_binary, 2), int(b_binary, 2)) + if idx >= len(hidden_image_pixels): + return img_visible + # can never be reached, but let's return the image anyway + return img_visible + +def encode(img_visible, img_hidden): + """ + Loads the image to be hidden and the image used for hiding and conceals the pixel information from one image + in the other one. + + Args: + img_visible: An RGB image used for hiding another image + img_hidden: An RGB image to be concealed + + Returns: + An RGB image which is supposed to be not very different visually from img_visible, but contains all the information + necessary to recover an identical copy of the image we want to hide. + """ + encoded_image = img_visible.load() + img_hidden_copy = img_hidden.load() + width_visible, height_visible = img_visible.size + width_hidden, height_hidden = img_hidden.size + hidden_image_pixels = get_binary_pixel_values(img_hidden_copy, width_hidden, height_hidden) + encoded_image = change_binary_values(encoded_image, hidden_image_pixels, width_visible, height_visible, width_hidden, height_hidden) + return img_visible + + +def extract_hidden_pixels(image, width_visible, height_visible, pixel_count): + """ + Extracts a sequence of bits representing a sequence of binary values of + all pixels of the hidden image. + The information representing a hidden image is stored in the 4 least significant + bits of a subset of pixels of the visible image. + + Args: + image: An RGB image to recover a hidden image from + width_visible: Width of the visible image + height_visible: Height of the visible image + pixel_count: Number of pixels in the hidden image + + Returns: + A binary string representing pixel values of the hidden image + """ + hidden_image_pixels = '' + idx = 0 + for col in range(width_visible): + for row in range(height_visible): + if row == 0 and col == 0: + continue + r, g, b = image[col, row] + r_binary, g_binary, b_binary = rgb_to_binary(r, g, b) + hidden_image_pixels += r_binary[4:8] + g_binary[4:8] + b_binary[4:8] + if idx >= pixel_count * 2: + return hidden_image_pixels + return hidden_image_pixels + +def reconstruct_image(image_pixels, width, height): + """ + Recontructs the hidden image using the extracted string of pixel binary values. + + Args: + image_pixels: A string of binary values of all pixels of the image to be recovered + width: Width of the image to be recovered + height: Height of the image to be recovered + + Returns: + The recovered image + """ + image = Image.new("RGB", (width, height)) + image_copy = image.load() + idx = 0 + for col in range(width): + for row in range(height): + r_binary = image_pixels[idx:idx+8] + g_binary = image_pixels[idx+8:idx+16] + b_binary = image_pixels[idx+16:idx+24] + if len(r_binary) == 0: r_binary = '0' + if len(g_binary) == 0: g_binary = '0' + if len(b_binary) == 0: b_binary = '0' +# print(r_binary,g_binary,b_binary) + image_copy[col, row] = (int(r_binary, 2), int(g_binary, 2), int(b_binary, 2)) + idx += 24 + return image + +def decode(image): + """ + Loads the image to recover a hidden image from, retrieves the information about the + size of the hidden image stored in the top left pixel of the visible image, + extracts the hidden binary pixel values from the image and reconstructs the hidden + image. + + Args: + image: An RGB image to recover a hidden image from + + Returns: + A recovered image, which was hidden in the binary representation of the visible image + """ + image_copy = image.load() + width_visible, height_visible = image.size + r, g, b = image_copy[0, 0] + r_binary, g_binary, b_binary = rgb_to_binary(r, g, b) + w_h_binary = r_binary + g_binary + b_binary + width_hidden = int(w_h_binary[0:12], 2) + height_hidden = int(w_h_binary[12:24], 2) + pixel_count = width_hidden * height_hidden + hidden_image_pixels = extract_hidden_pixels(image_copy, width_visible, height_visible, pixel_count) + decoded_image = reconstruct_image(hidden_image_pixels, width_hidden, height_hidden) + return decoded_image + +if __name__ == '__main__': + app = QtWidgets.QApplication(sys.argv) + ui = UiMain() + sys.exit(app.exec_()) \ No newline at end of file