一、需求描述与图片提取
1、在多个分包数据中,提取一张图片有多难?
(1)、将分包组成一个完整包
(2)、基于完整包来截取图片数据)
(3)、如果是RGB格式的图片,确认传输的数据是否是3通道,不是的话得增加其他通道的数据,否则图片会模糊
(4)、保存RGB图片时,需要知道原始图片的尺寸
(5)、有时候图片的数据会超过图片的尺寸,导致无法通过数组的方式写入,因此需要截取图片数据为该尺寸的数据
(6)、截取数据时,截取后半部分的数据才不会使得图片错位
(7)、实践过程中除了遇到以上问题,还有保存图片时,采用了各种方式,如直接写入文件(open函数)、通过Image.frombuffer直接写入(PIL)等等,最终发现Image.fromarray的RGB写入方式才好使,而且需要通过numpy来处理数据。
2、图片数据:
数据太多,无法上传
3、源代码:
import io
import re
import binascii
import socket
from PyQt5.QtGui import QImage
from common.base_socket import BaseSocket
from common.cases_data import test1_data
from PIL import Image
import numpy as np
class SaveImageFromResponse(object):
with open("./666.txt", "r") as f:
res_data = f.read().replace("\n", "") # 去掉所有回车符号
# 返回一张图片的数据列表
def res_place_list(self, res_data = res_data):
# res_data为假设的响应数据
# 一张图片的数据2:存在其他请求
all_req_list = [m.start() for m in re.finditer("aa55abcd000000", res_data)]
return all_req_list
# 返回所有完整的分包列表
def res_pack_list(self, res_data = res_data):
all_req_list = self.res_place_list()
pack_list = []
for index, req in enumerate(all_req_list):
# 获取包的长度
len_str = res_data[req + 14:req + 18]
len_act = len_str[2:4] + len_str[0:2]
len_num = int(len_act, 16) # 包的长度
pack_list.append(res_data[req:req + len_num * 2 + 26 + 4])
return pack_list
# 返回一个完整包
def one_full_pack(self, head=0, tail=None):
pack_list = self.res_pack_list()
# 掐头去尾组装一个完整的数据包
one_pack = ""
one_pack_list = pack_list[head:tail]
for index, pack in enumerate(one_pack_list):
one_pack += pack[26:-4]
# return one_pack.replace("\n", "") # 已经在原始数据中处理
return one_pack
# 获取图片数据
def one_image_pack(self, json_data):
one_pack = self.one_full_pack() # 完整包的区域是列表的4,-4位置
return one_pack.replace(json_data, "")
# 获取json数据
def one_json_pack(self):
one_pack = self.one_full_pack()
return bytes().fromhex(one_pack).decode(errors="ignore")
# 生成图片
def save_image(self, json_data):
image_data = self.one_image_pack(json_data)
if "\n" in image_data:
print("注意,image_data中存在回车键!")
# 将单通道数据复制成3通道
image_data_list = list(image_data)
image_data = ""
for i,value in enumerate(image_data_list):
if (i+1)%2 == 0:
image_data += (image_data_list[i-1]+value)*3
# # 方式1:得到一张模糊的图片
# new666 = Image.frombuffer("RGB", (1280,800), image_data.encode(), 'raw', "RGB", 0, 1)
# new666.save("new666.png", "PNG")
# # 方式2:得到一张模糊的图片
# new666 = Image.frombuffer("L", (1280, 800), image_data.encode(), 'raw', "L", 0, 1)
# print(new666.getchannel("L"))
# new666=Image.merge("RGB", (new666.getchannel("L"), new666.getchannel("L"), new666.getchannel("L")))
# new666.save("new666.png", "PNG")
# # 方式3:此路不通
# byte_stream = io.BytesIO(bytearray.fromhex(image_data)) # 请求数据转化字节流
# roiImg = Image.open(byte_stream) # Image打开二进制流Byte字节流数据
# imgByteArr = io.BytesIO() # 创建一个空的Bytes对象
# roiImg.save(imgByteArr, format='PNG') # PNG就是图片格式
# imgByteArr = imgByteArr.getvalue() # 保存的二进制流
# with open("./new666.png", "ab") as f:
# f.write(imgByteArr)
# # 方式4:此路不通
# # new666 = QImage.fromData(image_data.encode())
# new666 = QImage.fromData(bytearray.fromhex(image_data))
# print(new666.size().width())
# print(new666.size().height())
# new666.save("./new666.png", "PNG", 100)
# # 方式5:此路不通
# arr = np.frombuffer(bytes().fromhex(image_data), dtype=np.uint8)
# new666 = Image.fromarray(arr.reshape((1280,800, 3),order='A'))
# new666.save("new666.png", "PNG")
# # 方式6:得到一张模糊的图片
# new666 = Image.frombuffer("RGB", (1280, 800), image_data.encode(), 'raw', "RGB", 0, 1)
# new666.convert("RGB")
# new666.save("new666.png", "PNG")
# print(new666.format, new666.size, new666.mode)
# # 方式7:随即生成一张图片
# images = np.random.randint(0, 256, size=[800, 1280, 3], dtype=np.uint8) # size=[h,w,channel]
# print(images)
# new666 = Image.fromarray(images)
# new666.save("new666.png", "PNG")
# print(new666.format, new666.size, new666.mode) # None (1280, 800) RGB
# 方式8:
# print(np.random.randint(1, 2, size=[800, 1280, 3], dtype=np.uint8))
arr = np.frombuffer(bytes().fromhex(image_data), dtype=np.uint8)
print("len(arr)", len(arr)-3072000)
arr_data = arr[len(arr)-3072000:] # 截取前面部分会导致图片错位
new666 = Image.fromarray(arr_data.reshape((800, 1280, 3),order='A'))
new666.save("new666.png", "PNG")
# 图片转16进制字符串
def image_to_str16(self, image):
with open(image, 'rb') as f:
content = f.read()
return binascii.hexlify(content).decode()
# 16进制字符串转图片
def str16_to_image(self, str_16:str):
with open("./666_or_copy.png","ab") as f:
# pic = binascii.a2b_hex(str_16.encode()) # 方法1
pic = bytearray.fromhex(str_16) # 方法2
f.write(pic)
if __name__ == '__main__':
print(SaveImageFromResponse().res_place_list())
# print(SaveImageFromResponse().res_pack_list()[0:5])
# print(SaveImageFromResponse().one_full_pack()[0:5200])
json_data = '''aa55abcd0000001405140300007b22416c676f54696d65223a35342c22486173496d616765223a22596573222c22486173526573756c74223a22596573222c22496d6167654e616d65223a223230323130333235303834333536323933222c224e47526573756c74223a22222c22526573756c74223a5b7b22416e676c65223a2d302e363832333637353033363433303335392c22436f6465223a22444d313233343536373831323334353637383930222c22486569676874223a3235392e333532393936383236313731392c224c656674546f7058223a3435342e35383335353731323839303632352c224c656674546f7059223a3237392e33383033373130393337352c2253756363657373223a224f4b222c225769647468223a3235372e383639393634353939363039347d5d2c2253756363657373223a224f4b222c225461736b54696d65223a3230352c225479706573223a367d'''
print(SaveImageFromResponse().one_image_pack(json_data)[-1000:-1])
if "262925222526242723252427252424252429262722262426282728262625262825272" in SaveImageFromResponse().one_image_pack(json_data):
print("很好!")
SaveImageFromResponse().save_image(json_data)
# str_16 = SaveImageFromResponse().image_to_str16("./666_or.png")
# print(str_16[0:1000])
# if json_data in str_16:
# print("图片数据有误!")
# 图片转16进制再转图片
str_16=SaveImageFromResponse().image_to_str16("./666_or.png")
# print(str_16)
SaveImageFromResponse().str16_to_image(str_16)
pass