opencv-python实现图片隐藏文件

因为最近接触到了大佬们做的图片文件夹、图片化打包以及各种无影坦克,觉得非常有趣,所以用python做了一个简单的小玩具。因为本身没有多少编程经验,所以希望看到这篇文章的大佬们不吝赐教。

1、功能:将文件通过更改像素值的方式写入准备好的图片(表图),达到隐藏文件的目的。

2、原理:通过opencv将表图分解为B、G、R三个通道,仅更改每个像素三个通道的最后一位(大于255则减10)实现单个像素存储数字0~999,以此实现数据的存储。

           存储结构: 指令ID + 指令内容 + 指令ID + 指令内容+......

                              为方便读取存储内容,指令ID均使用大于255的数字。

3、代码:

       (1)首先是开头的库:

# -*- coding: UTF-8 -*-
import binascii
import cv2
import numpy as np
import math as m
import os

   (2)写几个函数为了后续编写方便:

   拆分和组合长整数的函数:

def split_int(s):
    s = str(s)
    n = len(s)
    if n % 2 == 1:
        s = '0' + s
        n = len(s)
    sl = np.zeros(int(n / 2))
    i = 0
    while i < (n / 2):
        sl[i] = s[2 * i:2 * (i + 1)]
        i = i + 1
    return sl


def get_split_int(sl):
    n = len(sl)
    i = 0
    s = ''
    while i < n:
        if len(str(int(sl[i]))) % 2 == 1:
            s1 = '0' + str(int(sl[i]))
        else:
            s1 = str(int(sl[i]))
        s = s + s1
        i = i + 1
    s = int(s)
    return s

编码和解码的函数:

def encode_bd(b):
    b = binascii.hexlify(b)
    b = str(b)
    b = b[2:4]
    d = int(b, 16)
    return d


def encode_db(d):
    d = str(hex(int(str(d), 10)))
    d = d[2:4]
    if len(d) == 1:
        d = '0' + d
    d = bytes(d, encoding='utf8')
    b = binascii.unhexlify(d)
    return b

通过更改一个像素的b,g,r值写入数据:

def w_bgr(b, g, r, data):
    data_r = data % 10
    data_g = (data % 100 - data_r) / 10
    data_b = (data - data_r - data_g) / 100
    b = b - b % 10 + data_b
    if b > 255:
        b -= 10
    g = g - g % 10 + data_g
    if g > 255:
        g -= 10
    r = r - r % 10 + data_r
    if r > 255:
        r -= 10
    return b, g, r

通过表示表图像素位置的整数i写入数据:

def set_bgr(b, g, r, bgr_i, s_y, data):
    x = int(m.floor(bgr_i / s_y))
    y = int(bgr_i % s_y)
    b[x, y], g[x, y], r[x, y] = w_bgr(b[x, y], g[x, y], r[x, y], data)

通过一个像素的b,g,r值读取十进制数据:

def get_d(b, g, r):
    data_r = r % 10
    data_g = g % 10
    data_b = b % 10
    data = data_r + 10 * data_g + 100 * data_b
    return data

通过表示表图像素位置的整数i读取十进制数据:

def get_d_i(b, g, r, bgr_i, s_y):
    x = int(m.floor(bgr_i / s_y))
    y = int(bgr_i % s_y)
    data = get_d(b[x, y], g[x, y], r[x, y])
    return data

通过表示表图像素位置的整数i读取十进制数据并编码为byte:

def get_byte(b, g, r, bgr_i, s_y):
    x = int(m.floor(bgr_i / s_y))
    y = int(bgr_i % s_y)
    strb = get_d(b[x, y], g[x, y], r[x, y])
    strb = encode_db(int(strb))
    return strb

判断文件是否存在并改名

def name_exists(name):
    j = 0
    name_new = name
    while os.path.exists(name_new):
        name_new = '(' + str(j) + ')' + name
        j = j + 1
    return name_new

(3)写一个IMG类方便对表图进行操作:

class IMG:
    def __init__(self, path):
        self.img = cv2.imread(path)
        self.size = os.path.getsize(path)
        (self.b, self.g, self.r) = cv2.split(self.img)

    def set_d(self, i, data):
        s_y = self.img.shape[1]
        set_bgr(self.b, self.g, self.r, i, s_y, data)
        i = i + 1
        return i

    def set_b(self, i, data):
        data = encode_bd(data)
        s_y = self.img.shape[1]
        set_bgr(self.b, self.g, self.r, i, s_y, data)
        i = i + 1
        return i

    def set_ds(self, i, ds):
        s_y = self.img.shape[1]
        le = len(ds)
        j = 0
        while j < le:
            set_bgr(self.b, self.g, self.r, i, s_y, ds[j])
            i = i + 1
            j = j + 1
        return i

    def set_s(self, i, s):
        s = s.encode('utf-8')
        le = len(s)
        j = 0
        while j < le:
            k = s[j:(j + 1)]
            k = binascii.hexlify(k)
            k = int(k, 16)
            i = self.set_d(i, k)
            j = j + 1
        return i

    def update(self):
        self.img = cv2.merge([self.b, self.g, self.r])

    def save(self, name):
        print('生成文件:' + name + '.png', end=' ')
        name = name + '.png'
        new_name = name_exists(name)
        if new_name != name:
            print('文件已存在,更名为' + new_name)
        else:
            print('')
        cv2.imwrite(new_name, self.img)

    def get_d(self, i):
        s_y = self.img.shape[1]
        data = get_d_i(self.b, self.g, self.r, i, s_y)
        i = i + 1
        return data, i

    def get_ds(self, i, le):
        s_y = self.img.shape[1]
        ds = np.zeros(le)
        j = 0
        while j < le:
            ds[j] = get_d_i(self.b, self.g, self.r, i, s_y)
            i = i + 1
            j = j + 1
        return ds, i

    def get_b(self, i):
        s_y = self.img.shape[1]
        b = get_byte(self.b, self.g, self.r, i, s_y)
        i = i + 1
        return b, i

(4)写一个FILE类方便对文件进行操作:

class FILE:
    def __init__(self):
        self.name = 'tank'
        self.size = 0

(5)指令:

为了修改方便定义几个变量当宏定义使

cmd_start = 256
cmd_filesize = 257
cmd_name = 258
cmd_file = 259
cmd_end = 260

写入指令的函数:

def w_cmd(cmd, i, img, data):
    if cmd == cmd_start:
        print('写入开始指令...', end=' ')
        i = img.set_d(i, cmd_start)
        i = img.set_d(i, data)
        print('完成,校验数:' + str(data))
        return i
    elif cmd == cmd_filesize:
        print('写入文件大小...', end=' ')
        d = data
        i = img.set_d(i, cmd_filesize)
        data = split_int(data)
        i = img.set_ds(i, data)
        print('完成,文件大小:' + str(d))
        return i
    elif cmd == cmd_name:
        print('写入文件名称...', end=' ')
        i = img.set_d(i, cmd_name)
        name = os.path.split(data)
        name = name[1]
        i = img.set_s(i, name)
        print('完成,文件名称:' + str(name))
        return i
    elif cmd == cmd_file:
        print('写入文件...', end=' ')
        i = img.set_d(i, cmd_file)
        i0 = i
        with open(data, "rb") as f:
            while True:
                strb = f.read(1)
                if strb == b"":
                    print('完成')
                    return i
                i = img.set_b(i, strb)
                print('\r', '写入文件...已写入'+str(i-i0), end='', flush=True)
    elif cmd == cmd_end:
        print('写入结束指令...', end=' ')
        i = img.set_d(i, cmd_end)
        i = img.set_d(i, data)
        print('完成')
        return i
    else:
        print('指令错误,未识别的指令ID' + str(cmd))
        return i

因为指令均使用大于255的数字,所以写一个能从当前位向后读取至大于255的数字并返回列表,读取某些指令时用得到:

def auto_cmd(i, img):
    cmd_list = []
    data, i = img.get_d(i)
    while data < 255:
        cmd_list.append(data)
        data, i = img.get_d(i)
    i = i - 1
    return cmd_list, i

读取指令的函数:

def r_cmd(i, img, file):
    cmd, i = img.get_d(i)
    if cmd == cmd_start:
        print('读取开始指令...', end=' ')
        data, i = img.get_d(i)
        if data != 0:
            i = -1
            print('校验数:' + str(data) + ',错误')
        else:
            print('完成,校验数:' + str(data))
        return i
    elif cmd == cmd_filesize:
        print('读取文件大小...', end=' ')
        size, i = auto_cmd(i, img)
        s = get_split_int(size)
        file.size = s
        print('完成,文件大小:' + str(s))
        return i
    elif cmd == cmd_name:
        print('读取文件名...', end=' ')
        data, i = auto_cmd(i, img)
        j = 0
        s1 = b''
        while j < len(data):
            s = encode_db(int(data[j]))
            s1 = s1 + s
            j = j + 1
        s1 = s1.decode('utf-8')
        file.name = s1
        print('完成,文件名:' + str(s1), end=' ')
        new_name = name_exists(str(s1))
        if new_name != str(s1):
            print('文件已存在,更名为'+new_name)
            file.name = new_name
        else:
            print('')
        return i
    elif cmd == cmd_file:
        print('读取文件,正在写入...', end=' ')
        with open(file.name, "wb") as f:
            j = 0
            while j < file.size:
                print('\r', '读取文件,正在写入...已写入' + str(j), end='', flush=True)
                data, i = img.get_b(i)
                f.write(data)
                j = j + 1
            print('完成')
            return i
    elif cmd == cmd_end:
        print('读取结束指令...', end=' ')
        i = -1
        print('完成,结束读取')
        return i
    else:
        i = -1
        print('指令错误,未识别的指令ID:' + str(cmd))
        return i

(6)再往之后写起来就方便了,以写入和读取一个文件为例:

写入一个文件并隐藏在表图中生成一个新图片的函数:

def new_png(path, img, name='tank'):
    img = IMG(img)
    i = 0
    i = w_cmd(cmd_start, i, img, 0)
    i = w_cmd(cmd_filesize, i, img, os.path.getsize(path))
    i = w_cmd(cmd_name, i, img, path)
    i = w_cmd(cmd_file, i, img, path)
    i = w_cmd(cmd_end, i, img, 0)
    img.update()
    img.save(name)
    return i

读取文件中的指令并执行:

def read(img):
    img = IMG(img)
    file = FILE()
    i = 0
    while i > -1:
        i = r_cmd(i, img, file)

使用方法及代码:

写入时调用的函数:

new_png("0.5.zip", 'ph.jpg')

注释:new_png(文件路径,表图路径)

读取时调用的函数:

read('tank.png')

注释: read(生成图)

4、演示:

文件与表图:

写入:

 

读取:

读取后:

图片比较,上方为原图:

 

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
你可以使用OpenCV和Turtle库来画图片的轮廓。首先,你需要使用OpenCV加载图像并进行边缘检测,然后使用Turtle库来绘制检测到的边缘。 下面是一个示例代码,展示了如何使用OpenCV和Turtle来画图片的轮廓: ```python import cv2 import turtle # 加载图像并进行边缘检测 image = cv2.imread('image.jpg') gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) edges = cv2.Canny(gray, 50, 150) # 创建Turtle画布 screen = turtle.Screen() screen.setup(image.shape[1], image.shape[0]) screen.setworldcoordinates(0, 0, image.shape[1], image.shape[0]) # 创建Turtle画笔 pen = turtle.Turtle() pen.speed(0) pen.hideturtle() # 绘制边缘 for y in range(edges.shape[0]): for x in range(edges.shape[1]): if edges[y, x] > 0: pen.penup() pen.goto(x, edges.shape[0] - y) pen.pendown() pen.forward(1) # 关闭窗口 turtle.done() cv2.destroyAllWindows() ``` 在上述代码,你需要将'image.jpg'替换为你想要处理的图像文件路径。该代码会加载图像,将其转化为灰度图像,然后使用Canny边缘检测算法检测边缘。接下来,我们创建一个Turtle画布,并设置其大小与图像一致。然后,我们创建一个Turtle画笔,设置绘制速度并隐藏画笔。最后,我们遍历边缘图像的每个像素,如果像素值大于0,则将画笔移动到该位置并绘制1个像素的线段。最后,我们关闭Turtle窗口并销毁OpenCV的窗口。 希望这个示例能帮到你!如有任何问题,请随时提问。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值