---
# System prepended metadata

title: '資料隱藏 Homework #3'
tags: [' Python', Data Hiding]

---

---
tags: Data Hiding, Python
disqus: HackMD
---

資料隱藏 Homework #3
===

```python=
import os
import cv2
import numpy as np
import copy
import pywt
from PIL import Image
from scipy.fftpack import dct
from scipy.fftpack import idct
```

```python=
base_path = '/content/drive/MyDrive/H.成大上課講義/資料隱藏技術/作業/作業三/'
file_path = base_path + 'images/'
coverImage = file_path + 'Lenna256.png'
watermarkimgA = file_path + 'watermarkA.jpg'
watermarkimgB = file_path + 'watermarkB.jpg'
```

LsbWatermark
---
```python=
class LSBSteg:
    def __init__(self, im):
        self.image = im
        self.height, self.width, self.nbchannels = im.shape
        self.size = self.width * self.height

        self.maskONEValues = [1, 2, 4, 8, 16, 32, 64, 128]
        # Mask used to put one ex:1->00000001, 2->00000010 .. associated with OR bitwise
        self.maskONE = self.maskONEValues.pop(0)  # Will be used to do bitwise operations

        self.maskZEROValues = [254, 253, 251, 247, 239, 223, 191, 127]
        # Mak used to put zero ex:254->11111110, 253->11111101 .. associated with AND bitwise
        self.maskZERO = self.maskZEROValues.pop(0)

        self.curwidth = 0  # Current width position
        self.curheight = 0  # Current height position
        self.curchan = 0  # Current channel position

    def put_binary_value(self, bits):  # Put the bits in the image
        for c in bits:
            val = list(self.image[self.curheight, self.curwidth])  # Get the pixel value as a list
            if int(c) == 1:
                val[self.curchan] = int(val[self.curchan]) | self.maskONE  # OR with maskONE
            else:
                val[self.curchan] = int(val[self.curchan]) & self.maskZERO  # AND with maskZERO

            self.image[self.curheight, self.curwidth] = tuple(val)
            self.next_slot()  # Move "cursor" to the next space

    def next_slot(self):  # Move to the next slot were information can be taken or put
        if self.curchan == self.nbchannels - 1:  # Next Space is the following channel
            self.curchan = 0
            if self.curwidth == self.width - 1:  # Or the first channel of the next pixel of the same line
                self.curwidth = 0
                if self.curheight == self.height - 1:  # Or the first channel of the first pixel of the next line
                    self.curheight = 0
                    if self.maskONE == 128:  # Mask 1000000, so the last mask
                        raise SteganographyException("No available slot remaining (image filled)")
                    else:  # Or instead of using the first bit start using the second and so on..
                        self.maskONE = self.maskONEValues.pop(0)
                        self.maskZERO = self.maskZEROValues.pop(0)
                else:
                    self.curheight += 1
            else:
                self.curwidth += 1
        else:
            self.curchan += 1

    def read_bit(self):  # Read a single bit int the image
        val = self.image[self.curheight, self.curwidth][self.curchan]
        val = int(val) & self.maskONE
        self.next_slot()
        if val > 0:
            return "1"
        else:
            return "0"

    def read_byte(self):
        return self.read_bits(8)

    def read_bits(self, nb):  # Read the given number of bits
        bits = ""
        for i in range(nb):
            bits += self.read_bit()
        return bits

    def byteValue(self, val):
        return self.binary_value(val, 8)

    def binary_value(self, val, bitsize):  # Return the binary value of an int as a byte
        binval = bin(val)[2:]
        if len(binval) > bitsize:
            raise SteganographyException("binary value larger than the expected size")
        while len(binval) < bitsize:
            binval = "0" + binval
        return binval

    def encode_text(self, txt):
        l = len(txt)
        binl = self.binary_value(l, 16)  # Length coded on 2 bytes so the text size can be up to 65536 bytes long
        self.put_binary_value(binl)  # Put text length coded on 4 bytes
        for char in txt:  # And put all the chars
            c = ord(char)
            self.put_binary_value(self.byteValue(c))
        return self.image

    def decode_text(self):
        ls = self.read_bits(16)  # Read the text size in bytes
        l = int(ls, 2)
        i = 0
        unhideTxt = ""
        while i < l:  # Read all bytes of the text
            tmp = self.read_byte()  # So one byte
            i += 1
            unhideTxt += chr(int(tmp, 2))  # Every chars concatenated to str
        return unhideTxt

    def encode_image(self, imtohide):
        height = imtohide.shape[0]
        width = imtohide.shape[1]
        channels = imtohide.shape[2]
        if self.width * self.height * self.nbchannels < width * height * channels:
            raise SteganographyException("Carrier image not big enough to hold all the datas to steganography")
        binw = self.binary_value(width, 16)  # Width coded on to byte so width up to 65536
        binh = self.binary_value(height, 16)
        self.put_binary_value(binw)  # Put width
        self.put_binary_value(binh)  # Put height
        for h in range(height):  # Iterate the hole image to put every pixel values
            for w in range(width):
                for chan in range(channels):
                    val = imtohide[h, w][chan]
                    self.put_binary_value(self.byteValue(int(val)))
        return self.image

    def decode_image(self):
        width = int(self.read_bits(16), 2)  # Read 16bits and convert it in int
        height = int(self.read_bits(16), 2)
        channels = self.nbchannels
        unhideimg = np.zeros((width, height, 3), np.uint8)  # Create an image in which we will put all the pixels read
        for h in range(height):
            for w in range(width):
                for chan in range(channels):
                    val = list(unhideimg[w, h])
                    val[chan] = int(self.read_byte(), 2)  # Read the value
                    unhideimg[w, h] = tuple(val)
        return unhideimg

    def encode_binary(self, data):
        l = len(data)
        if self.width * self.height * self.nbchannels < l + 64:
            raise SteganographyException("Carrier image not big enough to hold all the datas to steganography")
        self.put_binary_value(self.binary_value(l, 64))
        for byte in data:
            byte = byte if isinstance(byte, int) else ord(byte)  # Compat py2/py3
            self.put_binary_value(self.byteValue(byte))
        return self.image

    def decode_binary(self):
        l = int(self.read_bits(64), 2)
        output = b""
        for i in range(l):
            output += bytearray([int(self.read_byte(), 2)])
        return output
```

DctWatermark
---
```python=
class DCTSteg:
    def __init__(self):
        self.path = os.path.join("images")
        self.image = ""
        self.watermark = ""
        pass

    def resizeImage(self, filename, size):
        img = Image.open(os.path.join(self.path, filename))
        img = img.resize((size, size), Image.ANTIALIAS)
        img.save(os.path.join(self.path, filename))

    def resizeImage2(self, filename, width, height):
        img = Image.open(os.path.join(self.path, filename))
        img = img.resize((width, height), Image.ANTIALIAS)
        img.save(os.path.join(self.path, filename))

    def convertImage(self, imageName, size, imName):
        img = Image.open(imageName)
        img = img.resize((size, size), Image.ANTIALIAS)
        # Convert RGB image to gray scale
        img = img.convert('L')
        # Storing the gray scale image
        img.save(os.path.join(self.path, 'processedInputImage/' + imName))
        imageArray = np.array(img.getdata(), dtype=np.float).reshape((size, size))
        return imageArray

    def embedWatermark(self, watermarkArray, originalImage):
        watermarkFlat = watermarkArray.ravel()
        ind = 0
        for x in range(0, len(originalImage), 8):
            for y in range(0, len(originalImage), 8):
                if ind < len(watermarkFlat):
                    subdct = originalImage[x:x+8, y:y+8]
                    subdct[5][5] = watermarkFlat[ind]
                    originalImage[x:x+8, y:y+8] = subdct
                    ind+= 1
        return originalImage

    def applyDCT(self, imageArray):
        size = len(imageArray[0])
        allSubdct = np.empty((size, size))
        for i in range(0, size, 8):
            for j in range(0, size, 8):
                subpixels = imageArray[i:i+8, j:j+8]
                subdct = dct(dct(subpixels.T, norm="ortho").T, norm="ortho")
                allSubdct[i:i+8, j:j+8] = subdct
        return allSubdct

    def inverseDCT(self, allSubdct):
        size = len(allSubdct[0])
        allSubidct = np.empty((size, size))
        for i in range(0, size, 8):
            for j in range(0, size, 8):
                subidct = idct(idct(allSubdct[i:i+8, j:j+8].T, norm="ortho").T, norm="ortho")
                allSubidct[i:i+8, j:j+8] = subidct

        return allSubidct

    def getWatermark(self, dctWatermarkedCoeff, watermarkSize):
        subwatermarks = []
        for x in range(0, len(dctWatermarkedCoeff), 8):
            for y in range(0, len(dctWatermarkedCoeff), 8):
                coeffSlice = dctWatermarkedCoeff[x:x+8, y:y+8]
                subwatermarks.append(coeffSlice[5][5])

        watermark = np.array(subwatermarks).reshape(watermarkSize, watermarkSize)
        return watermark

    def recoverWatermark(self, imageArray, newpath, model='haar', level=1):
        coeffsWatermarkedImage=list(pywt.wavedec2(data=imageArray, wavelet=model, level=level))
        dctWatermarkedCoeff = self.applyDCT(coeffsWatermarkedImage[0])
        watermarkArray = self.getWatermark(dctWatermarkedCoeff, 128)
        watermarkArray = np.uint8(watermarkArray)
        # Save result
        img = Image.fromarray(watermarkArray)
        img.save(os.path.join(self.path, newpath))

    def printImage(self, imageArray, name):
        imageArrayCopy = imageArray.clip(0, 255)
        imageArrayCopy = imageArrayCopy.astype("uint8")
        img = Image.fromarray(imageArrayCopy)
        img.save(os.path.join(self.path, 'result/' + name))

    def watermarkImage(self, img1, img2):
        self.image = img1
        self.watermark = img2

        model = 'haar'
        level = 1
        imageArray = self.convertImage(self.image, 2048, "1.jpg")
        watermarkArray = self.convertImage(self.watermark, 128, "2.png")
        coeffsImage = list(pywt.wavedec2(data=imageArray, wavelet=model, level=level))
        # coeffs_image = process_coefficients(image_array, model, level=level)
        dctArray = self.applyDCT(coeffsImage[0])
        dctArray = self.embedWatermark(watermarkArray, dctArray)
        coeffsImage[0] = self.inverseDCT(dctArray)
        # Watermark reconstruction
        imageArrayH = pywt.waverec2(coeffsImage, model)
        self.printImage(imageArrayH, 'watermarkedImage.jpg')
        # recover images
        # self.recoverWatermark(imageArray=imageArrayH, newpath='result/recoveredWatermark.jpg', model=model, level=level)

    def decodeWatermark(self, img, newpath):
        model = 'haar'
        level = 1
        imageArray = self.convertImage(img, 2048, "3.jpg")
        self.recoverWatermark(imageArray=imageArray, newpath=newpath, model=model, level=level)
```

圖片處理方法
---
```python=
class ImgUtility:
    def __init__(self):
        pass
        
    def greenCover(self, xmin, ymin, xmax, ymax):
        img = self
        cv2.rectangle(img, (xmin, ymin), (xmax, ymax), (104, 255, 88), 16*8, 8*8)
        return img

    def colorCover(self, x, y):
        img = self
        height = img.shape[0]
        width = img.shape[1]
        for h in range(height):
            for w in range(width):
                if w >= x and h >= y:
                    img[w, h] = 0
        return img

    def colorCover2(self, xmin, ymin, xmax, ymax):
        img = self
        height = img.shape[0]
        width = img.shape[1]
        for h in range(height):
            for w in range(width):
                if (w >= xmin) and (h >= ymin) and (w <= xmin + xmax) and (h <= ymin + ymax):
                    img[w, h] = 0
        return img
```

主程式開始
---
* 建立資料夾

```python=
if not os.path.isdir('images'):
  os.mkdir('images')
if not os.path.isdir('images/result'):
  os.mkdir('images/result')
if not os.path.isdir('images/processedInputImage'):
  os.mkdir('images/processedInputImage')
```

* coverImage空間域藏入watermark
```python=
LsbWatermark = LSBSteg(cv2.imread(coverImage))
new_imgC = LsbWatermark.encode_image(cv2.imread(watermarkimgA))  # 嵌入空間域浮水印
cv2.imwrite('images/result/new_image_C.png', new_imgC)  # 儲存C圖(已嵌入空間域浮水印)
LsbWatermark = LSBSteg(cv2.imread('images/result/new_image_C.png'))  # 取得空間域浮水印
decodeLsbWatermark = LsbWatermark.decode_image()  # 第一張浮水印
img90 = np.rot90(cv2.flip(decodeLsbWatermark, 0), -1)  # 轉正浮水印
cv2.imwrite('images/result/decodeLsbWatermark.png', img90)  # 儲存空間域浮水印
```

* coverImage頻率域藏入watermark
```python=
"""第一題嵌入浮水印得到D圖, image/result/watermarkedImage.jpg"""
DctWatermark = DCTSteg()
DctWatermark.watermarkImage('images/result/new_image_C.png', watermarkimgB)  # 嵌入頻率域浮水印
```

```python=
"""第二題取出浮水印, image/result/Watermark_decode.jpg"""
DctWatermark.decodeWatermark(os.path.join('images/result/watermarkedImage.jpg')
                              , newpath='result/Watermark_decode.jpg')
imgD = cv2.imread(os.path.join('images/result/watermarkedImage.jpg'))
watermark_decode = cv2.imread(os.path.join('images/result/Watermark_decode.jpg'))
watermark_decode97x195 = cv2.resize(watermark_decode, (97, 195), interpolation=cv2.INTER_CUBIC)
cv2.imwrite(os.path.join('images/result/watermarkedImage97x195.jpg'), watermark_decode97x195)
```

```python=
"""第三題將D圖之右下角的四分之一裁掉，然後取出剩下部分內部之 watermark"""
tmp = copy.copy(imgD)
cropimg = ImgUtility.colorCover(tmp, 128*8, 128*8)
cv2.imwrite(os.path.join('images/result/cropImage.jpg'), cropimg)
DctWatermark.decodeWatermark(os.path.join('images/result/cropImage.jpg')
                              , newpath='result/cropImage_decode.jpg')
DctWatermark.resizeImage2('result/cropImage_decode.jpg', 97, 195)
```

```python=
"""第四題將中間32*32，64*64，128*128正方形分別裁掉，然後分別取出剩下部分內部之watermark。"""
tmp = copy.copy(imgD)
cropimg32x32 = ImgUtility.colorCover2(tmp, 112*8, 112*8, 32*8, 32*8)
cv2.imwrite(os.path.join('images/result/cropImage32x32.jpg'), cropimg32x32)
DctWatermark.decodeWatermark(os.path.join('images/result/cropImage32x32.jpg')
                              , newpath='result/cropImage32x32_decode.jpg')
DctWatermark.resizeImage2('result/cropImage32x32_decode.jpg', 97, 195)
tmp = copy.copy(imgD)
cropimg64x64 = ImgUtility.colorCover2(tmp, 96*8, 96*8, 64*8, 64*8)
cv2.imwrite(os.path.join('images/result/cropImage64x64.jpg'), cropimg64x64)
DctWatermark.decodeWatermark(os.path.join('images/result/cropImage64x64.jpg')
                              , newpath='result/cropImage64x64_decode.jpg')
DctWatermark.resizeImage2('result/cropImage64x64_decode.jpg', 97, 195)
tmp = copy.copy(imgD)
cropimg128x128 = ImgUtility.colorCover2(tmp, 64*8, 64*8, 128*8, 128*8)
cv2.imwrite(os.path.join('images/result/cropImage128x128.jpg'), cropimg128x128)
DctWatermark.decodeWatermark(os.path.join('images/result/cropImage128x128.jpg')
                              , newpath='result/cropImage128x128_decode.jpg')
DctWatermark.resizeImage2('result/cropImage128x128_decode.jpg', 97, 195)
```

```python=
"""第五題將 D 圖截去如下圖中之兩圈綠色部分(兩圈實際位置請自行決定，但須說明 你做的選擇)，
綠色部分的寬度均為 16 pixels，然後取出剩下部分內部之 watermark。"""
tmp = copy.copy(imgD)
img256x256 = cv2.resize(tmp, (256, 256))
cv2.imwrite('images/result/watermarkedImage256x256.jpg', img256x256)
green1 = ImgUtility.greenCover(imgD, 10*8, 10*8, 220*8, 220*8)
green2 = ImgUtility.greenCover(green1, 90*8, 90*8, 140*8, 140*8)
tmp2 = copy.copy(green2)
green256x256 = cv2.resize(green2, (256, 256))
cv2.imwrite(os.path.join('images/result/greenCoverImage256x256.jpg'), green256x256)
cv2.imwrite(os.path.join('images/result/greenCoverImage.jpg'), tmp2)
DctWatermark.decodeWatermark(os.path.join('images/result/greenCoverImage.jpg')
                              , newpath='result/greenCoverImageWatermark_decode.jpg')
DctWatermark.resizeImage2('result/greenCoverImageWatermark_decode.jpg', 97, 195)
```