将rgb值还原成图片(python)

本文介绍了一道2016年SWPU-CTF竞赛中的Misc类题目解决方案。通过对.pcapng文件的分析,使用Wireshark工具提取了一个名为flag.zip的文件,并通过编写Python脚本将提取出的RGB值转换为图像,最终找到了隐藏的线索。
    这是最近2016-SWPU-CTF的一道misc题目,比较简单,不过也比较有意思,所以还是写一写思路吧,供有兴趣的小伙伴学习。

    首先,文件下载下来是一个.pcapng文件,用wireshark打开。看一下有什么传输文件,文件-导出对象-http。翻阅之后可以发现一个flag.zip文件,保存下来。

1.png2.png
打开之后发现是一堆“255, 255, 255”的东西,猜想应该就是rgb值,试一下还原图片,写了个脚本。

 #-*- coding:utf-8 -*-
from PIL import Image
import re
x = 887 
y = 111 
image = Image.new("RGB",(x,y))
f = open('ce.txt') 
for i in range(0,x):
    for j in range(0,y):
        l = f.readline()
        r = l.split(", ")#注意逗号后面有个空格(文本中逗号后面有空格,所以需要分离)
        image.putpixel((i,j),(int(r[0]),int(r[1]),int(r[2])))
im.save('image1.jpg')
#读取行数可以直接用editplus打开查看,分解在线分解(http://factordb.com/)
#x,y的可能取值
# 32819*3
# 2661*27
# 887*111
<think> 我们之前已经讨论过RGB888转RGB565的转换方法。现在,用户要求将一张RGB888格式的图片转换成RGB565数据。这里,我们需要读取图片,转换每个像素,并输出RGB565数据(通常是一个字节数组,每个像素用2个字节表示)。 步骤: 1. 使用PIL或OpenCV读取图片,确保图片RGB888格式(每个像素由3个字节表示)。 2. 将图片转换为一个二维数组(高度×宽度×3)。 3. 遍历每个像素,将每个像素的RGB888转换为一个16位的RGB565。 4. 将每个16位的RGB565拆分为两个字节(高字节和低字节)并按顺序存入字节数组,或者直接存储为16位整数数组(根据需求)。 注意:RGB565的存储方式有两种:大端序(Big-Endian)和小端序(Little-Endian)。通常,在嵌入式系统中,数据存储的顺序需要根据目标平台来确定。常见的做法是高位字节在前(大端序),但有些系统可能要求低位字节在前(小端序)。这里我们默认使用大端序(即高字节在前,低字节在后),但也可以根据用户需求调整。 下面提供两种方法: 方法一:使用PIL库(Pillow) 方法二:使用OpenCV库 我们假设用户需要将转换后的数据保存为二进制文件(.bin)或者直接获取字节数组。 ### 实现代码 #### 方法1:使用Pillow(推荐) ```python from PIL import Image import numpy as np def convert_rgb888_to_rgb565(r, g, b): """将单个RGB888像素转换为RGB565(16位整数)""" # 确保在0-255范围内 r = min(255, max(0, r)) g = min(255, max(0, g)) b = min(255, max(0, b)) # 取高位并移位组合 return ((r & 0xF8) << 8) | ((g & 0xFC) << 3) | (b >> 3) def convert_image_to_rgb565_data(image_path, output_path=None, endian='big'): """ 将RGB888像转换为RGB565数据(字节数组) :param image_path: 输入像路径 :param output_path: 输出二进制文件路径(可选) :param endian: 字节序,'big'或'little' :return: RGB565数据的字节数组 """ # 打开像并转换为RGB img = Image.open(image_path).convert('RGB') img_array = np.array(img) # 获取像尺寸 height, width, _ = img_array.shape # 创建一个数组用于存储每个像素的RGB565(16位整数) rgb565_array = np.zeros((height, width), dtype=np.uint16) # 遍历每个像素并转换 for h in range(height): for w in range(width): r, g, b = img_array[h, w] rgb565_array[h, w] = convert_rgb888_to_rgb565(r, g, b) # 将数组展平为一维,然后转换为字节数组 # 注意:这里我们按行展平 flat_array = rgb565_array.ravel() if endian == 'big': # 转换为大端序的字节数组(每个16位整数,高字节在前) data = bytearray() for value in flat_array: # 使用大端序:高位字节在前,低位字节在后 data.append((value >> 8) & 0xFF) # 高字节 data.append(value & 0xFF) # 低字节 else: # little-endian data = bytearray() for value in flat_array: # 小端序:低位字节在前,高位字节在后 data.append(value & 0xFF) # 低字节 data.append((value >> 8) & 0xFF) # 高字节 # 如果指定了输出路径,则将字节数组写入文件 if output_path: with open(output_path, 'wb') as f: f.write(data) return data # 示例用法 # 将图片转换为RGB565字节数组(大端序) rgb565_data = convert_image_to_rgb565_data('input.jpg', 'output.bin') ``` #### 方法2:使用OpenCV(效率更高) OpenCV可以让我们避免使用循环,从而大幅提升处理速度。 ```python import cv2 import numpy as np def convert_image_cv_to_rgb565(image_path, output_path=None, endian='big'): """ 使用OpenCV将RGB888像转换为RGB565数据(字节数组) :param image_path: 输入像路径 :param output_path: 输出二进制文件路径(可选) :param endian: 字节序,'big'或'little' :return: RGB565数据的字节数组 """ # 使用OpenCV读取像(注意OpenCV默认是BGR顺序) img_bgr = cv2.imread(image_path) # 将BGR转换为RGB img_rgb = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2RGB) # 提取通道 r = img_rgb[:, :, 0] g = img_rgb[:, :, 1] b = img_rgb[:, :, 2] # 使用位运算直接计算整个数组RGB565 # 注意:这里使用整数类型的位运算,所以需要确保数据类型 r5 = (r.astype(np.uint16) >> 3) & 0x1F # 取高5位 g6 = (g.astype(np.uint16) >> 2) & 0x3F # 取高6位 b5 = (b.astype(np.uint16) >> 3) & 0x1F # 取高5位 # 组合:RGB565 (16位) # R: 15~11位,G: 10~5位,B: 4~0位 rgb565 = (r5 << 11) | (g6 << 5) | b5 # 将二维数组转换为一维(按行) flat_array = rgb565.ravel() # 转换为字节数组 if endian == 'big': # 大端序:每个16位整数,高字节在前 # 使用numpy的ndarray.tobytes()方法,但注意numpy的默认字节序是本地字节序(通常小端序) # 因此,我们需要指定dtype为大端序('>u2'表示大端序无符号16位整数) data = flat_array.astype('>u2').tobytes() else: # little-endian data = flat_array.astype('<u2').tobytes() # 如果指定了输出路径,则写入文件 if output_path: with open(output_path, 'wb') as f: f.write(data) return data # 示例用法 # 将图片转换为RGB565字节数组(大端序) rgb565_data = convert_image_cv_to_rgb565('input.jpg', 'output.bin', endian='big') ``` ### 说明: 1. **字节序**:我们提供了两种字节序选项。在嵌入式系统中,通常需要根据目标硬件选择字节序。默认使用大端序('big'),但有些系统(如ARM)可能使用小端序('little')。 2. **效率**:OpenCV方法使用向量化操作,比循环方法快很多,特别是对于大像。 3. **颜色通道顺序**:OpenCV读取的像默认是BGR顺序,因此需要转换为RGB。 4. **输出**:输出的字节数组可以直接写入文件(二进制格式),供嵌入式系统使用。 ### 验证转换是否正确 为了验证转换是否正确,我们可以将转换后的RGB565数据再转换回RGB888,然后显示图片(会有一定的精度损失)。这里提供一个简单的验证函数(使用Pillow显示还原后的图片): ```python def verify_rgb565_conversion(rgb565_data, width, height, endian='big'): """ 将RGB565字节数组转换回RGB888像并显示(用于验证) :param rgb565_data: 字节数组 :param width: 像宽度 :param height: 像高度 :param endian: 字节序 """ # 将字节数组转换为16位整数数组 if endian == 'big': # 大端序:每两个字节为一个16位整数(高字节在前) array_16bit = np.frombuffer(rgb565_data, dtype='>u2') else: array_16bit = np.frombuffer(rgb565_data, dtype='<u2') # 重塑为二维数组 img_array_16bit = array_16bit.reshape((height, width)) # 创建RGB888的数组 rgb888 = np.zeros((height, width, 3), dtype=np.uint8) # 将RGB565转换为RGB888 # 提取RGB分量 r5 = (img_array_16bit >> 11) & 0x1F g6 = (img_array_16bit >> 5) & 0x3F b5 = img_array_16bit & 0x1F # 将5/6位扩展到8位(左移并复制高位到低位) # R: 5位->8位: 左移3位,然后或上右移2位(复制高位) r8 = (r5 << 3) | (r5 >> 2) g8 = (g6 << 2) | (g6 >> 4) b8 = (b5 << 3) | (b5 >> 2) rgb888[:, :, 0] = r8 rgb888[:, :, 1] = g8 rgb888[:, :, 2] = b8 # 使用Pillow显示像 img = Image.fromarray(rgb888, 'RGB') img.show() # 示例验证(假设原尺寸已知) # 假设原尺寸为width x height # verify_rgb565_conversion(rgb565_data, width, height, endian='big') ``` 注意:还原后的像会有色彩失真,这是因为转换过程中损失了低位信息。 ### 相关问题 1. 如何将RGB565字节数组重新转换为RGB888像? 2. 在转换过程中,如何避免颜色失真? 3. 如果目标设备是小端序,应该如何调整代码? 4. 除了保存为二进制文件,还有哪些方式可以处理转换后的RGB565数据? [^1]: 参考自用户提供的引用[1]:在项目工程中将图片转为RGB565或在电脑中转换 [^2]: 参考自用户提供的引用[2]:RGB888转换为RGB565原理 </think> ### RGB888图片RGB565数据的Python实现 #### 核心算法原理 RGB888到RGB565的转换本质上是颜色空间压缩: - **RGB888**:每通道8位($2^8=256$种),总24位/像素 - **RGB565**:红/蓝通道5位($2^5=32$种),绿通道6位($2^6=64$种),总16位/像素 转换公式: $$ \text{RGB565} = (R \gg 3) \ll 11 + (G \gg 2) \ll 5 + (B \gg 3) $$ 等价位运算: $$ \text{RGB565} = (R \& \text{0xF8}) \ll 8 + (G \& \text{0xFC}) \ll 3 + (B \gg 3) $$ #### 完整转换代码(PIL实现) ```python from PIL import Image import numpy as np def rgb888_to_rgb565_image(image_path, output_bin): """ 将RGB888像转换为RGB565二进制数据 :param image_path: 输入图片路径 :param output_bin: 输出二进制文件路径 """ # 读取像并转换为RGB888数组 img = Image.open(image_path).convert('RGB') rgb_array = np.array(img) # 提取颜色通道(高度×宽度×通道) r = rgb_array[:, :, 0].astype(np.uint16) # 红色通道 g = rgb_array[:, :, 1].astype(np.uint16) # 绿色通道 b = rgb_array[:, :, 2].astype(np.uint16) # 蓝色通道 # RGB888转RGB565(向量化运算) rgb565 = ((r & 0xF8) << 8) | ((g & 0xFC) << 3) | (b >> 3) # 转换为字节序列(大端序) bytes_high = (rgb565 >> 8).astype(np.uint8) bytes_low = (rgb565 & 0xFF).astype(np.uint8) # 合并高低字节并保存为二进制 with open(output_bin, 'wb') as f: for hi, lo in zip(bytes_high.flatten(), bytes_low.flatten()): f.write(bytes([hi, lo])) # 每个像素2字节 # 使用示例 rgb888_to_rgb565_image('input.jpg', 'output.bin') ``` #### OpenCV高效实现(适合大处理) ```python import cv2 import numpy as np def rgb888_to_rgb565_cv(image_path, output_bin): """使用OpenCV加速转换(适合大尺寸像)""" img = cv2.imread(image_path) rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) # OpenCV默认为BGR顺序 # 矢量运算转换 r, g, b = rgb[:, :, 0], rgb[:, :, 1], rgb[:, :, 2] rgb565 = ((r.astype(np.uint16) & 0xF8) << 8) | \ ((g.astype(np.uint16) & 0xFC) << 3) | \ (b.astype(np.uint16) >> 3) # 直接写入二进制(大端序) with open(output_bin, 'wb') as f: f.write(rgb565.astype('>u2').tobytes()) # '>u2'表示大端16位无符号整数 ``` #### 验证转换正确性 ```python def verify_conversion(original_path, rgb565_bin): """验证RGB565数据与原始像一致性""" # 读取原始像 orig_img = np.array(Image.open(original_path).convert('RGB')) # 读取RGB565二进制 with open(rgb565_bin, 'rb') as f: data = np.frombuffer(f.read(), dtype='>u2') # 大端16位读取 # 重塑为像尺寸 h, w, _ = orig_img.shape rgb565 = data.reshape((h, w)) # 逆向转换(RGB565转RGB888) r = ((rgb565 >> 11) & 0x1F) << 3 g = ((rgb565 >> 5) & 0x3F) << 2 b = (rgb565 & 0x1F) << 3 reconstructed = np.dstack((r, g, b)).astype(np.uint8) # 计算误差(应小于5%) diff = np.mean(np.abs(orig_img.astype(int) - reconstructed)) print(f"平均像素误差: {diff:.2f} (域0-255)") return diff < 12.8 # 5%误差阈 ``` #### 关键说明 1. **字节序处理**:嵌入式系统通常使用**大端序**(高位在前),代码中`>u2`确保正确格式[^4] 2. **颜色精度**:转换后平均色彩误差约8/255≈3.1%,在可接受范围 3. **内存优化**:RGB565比RGB888节省**33%内存**(16位 vs 24位/像素) 4. **通道顺序**:OpenCV默认使用BGR顺序,需显式转换[^3] > **应用场景**:此转换特别适用于嵌入式LCD显示[^1]、物联网设备像传输[^4]等内存受限场景。 ### 相关问题 1. 如何将RGB565二进制数据重新转换为JPG图片? 2. RGB565与RGB888互转时怎样最小化色彩失真? 3. 在嵌入式系统中如何优化RGB565数据的显示性能? 4. 除了RGB565,还有哪些适合嵌入式系统的颜色压缩格式? 5. 如何处理带透明度(Alpha通道)的图片格式转换? [^1]: LCD显示通常使用RGB565格式以节省内存 [^2]: RGB888转RGB565本质是颜色精度压缩 [^3]: OpenCV默认使用BGR通道顺序 [^4]: 二进制数据传输需注意字节序问题
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值