一幅从相机获取的3072 * 2048像素的图片原始像素格式为bayer-rb将其分别保存为png、bmp、jpg以及raw直接存储元素的原始bayer-rb字节数据格式的本地文件然后使用Qt转换为QImage并显示统计用时。使用QImage()直接读取png格式并生成QImage对象import sys import cv2 import numpy as np from PySide6.QtCore import QElapsedTimer from PySide6.QtGui import QPixmap, QImage from PySide6.QtWidgets import QApplication, QWidget, QVBoxLayout, QLabel class MyWidget(QWidget): def __init__(self): super().__init__() self.init_ui() def init_ui(self): layout QVBoxLayout() self.label QLabel() layout.addWidget(self.label) self.setLayout(layout) if __name__ __main__: app QApplication(sys.argv) widget MyWidget() widget.show() # 计时器 timer QElapsedTimer() timer.start() q_image QImage(out.png) print(f转换QImage耗时{timer.elapsed()} ms) timer.restart() pixmap QPixmap.fromImage(q_image) print(f转换QPixmap耗时{timer.elapsed()} ms) timer.restart() widget.label.setPixmap(pixmap) print(f设置QPixmap耗时{timer.elapsed()} ms) # 宽[3072], 高[2048] sys.exit(app.exec())用时转换QImage耗时136 ms 转换QPixmap耗时0 ms 设置QPixmap耗时0 ms使用QImage()直接读取bmp格式并生成QImage对象import sys import cv2 import numpy as np from PySide6.QtCore import QElapsedTimer from PySide6.QtGui import QPixmap, QImage from PySide6.QtWidgets import QApplication, QWidget, QVBoxLayout, QLabel class MyWidget(QWidget): def __init__(self): super().__init__() self.init_ui() def init_ui(self): layout QVBoxLayout() self.label QLabel() layout.addWidget(self.label) self.setLayout(layout) if __name__ __main__: app QApplication(sys.argv) widget MyWidget() widget.show() # 计时器 timer QElapsedTimer() timer.start() q_image QImage(out.bmp) print(f转换QImage耗时{timer.elapsed()} ms) timer.restart() pixmap QPixmap.fromImage(q_image) print(f转换QPixmap耗时{timer.elapsed()} ms) timer.restart() widget.label.setPixmap(pixmap) print(f设置QPixmap耗时{timer.elapsed()} ms) # 宽[3072], 高[2048] sys.exit(app.exec())用时转换QImage耗时56 ms 转换QPixmap耗时0 ms 设置QPixmap耗时0 msjpg格式import sys import cv2 import numpy as np from PySide6.QtCore import QElapsedTimer from PySide6.QtGui import QPixmap, QImage from PySide6.QtWidgets import QApplication, QWidget, QVBoxLayout, QLabel class MyWidget(QWidget): def __init__(self): super().__init__() self.init_ui() def init_ui(self): layout QVBoxLayout() self.label QLabel() layout.addWidget(self.label) self.setLayout(layout) if __name__ __main__: app QApplication(sys.argv) widget MyWidget() widget.show() # 计时器 timer QElapsedTimer() timer.start() q_image QImage(out.jpg) print(f转换QImage耗时{timer.elapsed()} ms) timer.restart() pixmap QPixmap.fromImage(q_image) print(f转换QPixmap耗时{timer.elapsed()} ms) timer.restart() widget.label.setPixmap(pixmap) print(f设置QPixmap耗时{timer.elapsed()} ms) # 宽[3072], 高[2048] sys.exit(app.exec())用时转换QImage耗时91 ms 转换QPixmap耗时0 ms 设置QPixmap耗时0 msbayer格式字节流由于Qt不直接支持bayer格式使用numpy进行转换import sys import cv2 import numpy as np from PySide6.QtCore import QElapsedTimer from PySide6.QtGui import QPixmap, QImage from PySide6.QtWidgets import QApplication, QWidget, QVBoxLayout, QLabel class MyWidget(QWidget): def __init__(self): super().__init__() self.init_ui() def init_ui(self): layout QVBoxLayout() self.label QLabel() layout.addWidget(self.label) self.setLayout(layout) def bayer_bytes_to_qimage(bayer_bytes, width, height): bayer_array np.frombuffer(bayer_bytes, dtypenp.uint8).reshape((height, width)) # 将bayer格式字节流转换为numpy数组 rgb_array cv2.cvtColor(bayer_array, cv2.COLOR_BAYER_RG2BGR) # 将bayer格式数组转换为RGB格式数组(如果raw是使用opencv创建的有可能cv2.COLOR_BAYER_RG2BGR格式转换 q_image QImage(rgb_array.data, rgb_array.shape[1], rgb_array.shape[0], w * 3, QImage.Format.Format_RGB888) return q_image if __name__ __main__: app QApplication(sys.argv) widget MyWidget() widget.show() # 计时器 timer QElapsedTimer() timer.start() # 模拟字节流读取文件为字节实际场景可能是网络返回、数据库读取 with open(out.raw, rb) as f: bayer_bytes f.read() print(f读取raw文件时间: {timer.elapsed()} ms) h 2048 w 3072 timer.restart() q_image bayer_bytes_to_qimage(bayer_bytes, w, h) print(f转换QImage耗时{timer.elapsed()} ms) timer.restart() pixmap QPixmap.fromImage(q_image) print(f转换QPixmap耗时{timer.elapsed()} ms) timer.restart() widget.label.setPixmap(pixmap) print(f设置QPixmap耗时{timer.elapsed()} ms) # 宽[3072], 高[2048] sys.exit(app.exec())用时读取raw文件时间: 2 ms cv2转换耗时4 ms 转换QPixmap耗时7 ms用时对比读取格式读文件用时(ms)转QImage耗时(ms)转换QPixmap耗时(ms)总耗时(ms)png--1360136bmp--56056jpg--91091raw24713使用原始图像字节流并使用numpy转换效率远远比Qt直接读取图像文件高其中的原因是numpy支持不创建副本的内存视图引用和矩阵转换。验证直接读取bmp图像文件的像素字节并用numpy转换为数组后转为QImageimport sys import cv2 import numpy as np from PySide6.QtCore import QElapsedTimer from PySide6.QtGui import QPixmap, QImage from PySide6.QtWidgets import QApplication, QWidget, QVBoxLayout, QLabel class MyWidget(QWidget): def __init__(self): super().__init__() self.init_ui() def init_ui(self): layout QVBoxLayout() self.label QLabel() layout.addWidget(self.label) self.setLayout(layout) if __name__ __main__: app QApplication(sys.argv) widget MyWidget() widget.show() # 计时器 timer QElapsedTimer() timer.start() h 2048 w 3072 with open(out.bmp, rb) as f: f.seek(54) image_bytes f.read() # 去除bmp头部信息 print(f读取bmp文件时间: {timer.elapsed()} ms) timer.restart() rgb_array np.frombuffer(image_bytes, dtypenp.uint8).reshape((h, w, 3)) # 将字节流转换为numpy数组 rgb_arrayy np.flipud(rgb_array) # 垂直翻转bmp图像像素是从左下角开始存储的 q_image QImage(rgb_array.data, w, h, w * 3, QImage.Format.Format_RGB888) # 将numpy数组转换为QImage print(f转换QImage耗时{timer.elapsed()} ms) timer.restart() pixmap QPixmap.fromImage(q_image) print(f转换QPixmap耗时{timer.elapsed()} ms) timer.restart() widget.label.setPixmap(pixmap) print(f设置QPixmap耗时{timer.elapsed()} ms) # 宽[3072], 高[2048] sys.exit(app.exec())总耗时17ms读取bmp文件时间: 9 ms 转换QImage耗时0 ms 转换QPixmap耗时8 ms 设置QPixmap耗时0 ms然后也使用Qt从图像文件的原始字节流生成QPixmap进行比对import sys import cv2 import numpy as np from PySide6.QtCore import QElapsedTimer from PySide6.QtGui import QPixmap, QImage from PySide6.QtWidgets import QApplication, QWidget, QVBoxLayout, QLabel class MyWidget(QWidget): def __init__(self): super().__init__() self.init_ui() def init_ui(self): layout QVBoxLayout() self.label QLabel() layout.addWidget(self.label) self.setLayout(layout) if __name__ __main__: app QApplication(sys.argv) widget MyWidget() widget.show() # 计时器 timer QElapsedTimer() timer.start() with open(out.bmp, rb) as f: image_bytes f.read() print(f读取bmp文件时间: {timer.elapsed()} ms) timer.restart() pixmap QPixmap() pixmap.loadFromData(image_bytes) print(f转换QPixmap耗时{timer.elapsed()} ms) timer.restart() widget.label.setPixmap(pixmap) print(f设置QPixmap耗时{timer.elapsed()} ms) # 宽[3072], 高[2048] sys.exit(app.exec())总耗时45ms读取bmp文件时间: 9 ms 转换QPixmap耗时36 ms 设置QPixmap耗时0 ms当然也可以分两步先用原始字节流生成QImage再转换QPixmap结果差不多:import sys from PySide6.QtCore import QElapsedTimer from PySide6.QtGui import QPixmap, QImage from PySide6.QtWidgets import QApplication, QWidget, QVBoxLayout, QLabel class MyWidget(QWidget): def __init__(self): super().__init__() self.init_ui() def init_ui(self): layout QVBoxLayout() self.label QLabel() layout.addWidget(self.label) self.setLayout(layout) if __name__ __main__: app QApplication(sys.argv) widget MyWidget() widget.show() # 计时器 timer QElapsedTimer() timer.start() h 2048 w 3072 with open(out.bmp, rb) as f: image_bytes f.read() print(f读取bmp文件时间: {timer.elapsed()} ms) timer.restart() q_image QImage() q_image.loadFromData(image_bytes) print(f转换QImage耗时{timer.elapsed()} ms) timer.restart() pixmap QPixmap.fromImage(q_image) print(f转换QPixmap耗时{timer.elapsed()} ms) timer.restart() widget.label.setPixmap(pixmap) print(f设置QPixmap耗时{timer.elapsed()} ms) # 宽[3072], 高[2048] sys.exit(app.exec())总耗时54ms读取bmp文件时间: 11 ms 转换QImage耗时43 ms 转换QPixmap耗时0 ms 设置QPixmap耗时0 ms结论无论是读取本地文件或者使用字节流使用numpy把像素字节转换成数组再生成QImage的效率远远高于Qt本身的QImage生成功能实测4倍以上。附将相机原始bayer字节流保存为.raw文件的方法def save_bayer_to_raw( pixel_data: bytes, width: int, height: int, pixel_format: str, # BayerRG8, BayerRG10, BayerRG10P, BayerRG12, BayerRG12P output_path: str output.raw ): 海康相机Bayer裸数据 自定义二进制头 → 保存为 .raw 文件 自定义头格式固定128字节方便后续读取解析 # -------------------------- # 1. 构造自定义文件头128字节 # -------------------------- header_size 128 header_bytes bytearray(header_size) # 偏移0宽度 (4字节 int) w_bytes width.to_bytes(4, byteorderlittle) header_bytes[:4] w_bytes # 偏移4高度 (4字节 int) h_bytes height.to_bytes(4, byteorderlittle) header_bytes[4:8 ] h_bytes # 偏移8位深 (4字节 int) bit_depth { BayerRG8: 8, BayerRG10: 10, BayerRG10P: 10, BayerRG12: 12, BayerRG12P: 12 }[pixel_format] bit_depth_bytes bit_depth.to_bytes(4, byteorderlittle) # 位深 header_bytes[8:12] bit_depth_bytes # 偏移12像素格式字符串最多32字节 fmt_bytes pixel_format.encode(utf-8) # 像素格式 fmt_bytes fmt_bytes.ljust(32, b\x00) # 不足补0 header_bytes[12:44] fmt_bytes # 偏移64数据总长度4字节 unsigned long long data_len len(pixel_data) l_bytes data_len.to_bytes(4, byteorderlittle, signedFalse) header_bytes[64:68] l_bytes # -------------------------- # 2. 拼接头 裸像素数据 # -------------------------- raw_file_bytes b.join([header_bytes, pixel_data]) # -------------------------- # 3. 二进制写入文件 # -------------------------- with open(output_path, wb) as f: f.write(raw_file_bytes) print(f✅ 保存成功{output_path}) print(f 尺寸{width}x{height}) print(f 格式{pixel_format}) print(f 头长度{header_size} 像素数据长度{data_len})以及从含宽高信息的.raw文件读取bayer字节流和转换成RGB数组的方法import cv2 import numpy as np def read_bayer_raw(raw_file_path): with open(raw_file_path, rb) as f: # 从本地文件读取bayer-rg格式字节流(这个字节流也可以是从相机获取的) head_bytes f.read(128) # 读取文件头 # 解析头部数据 w int.from_bytes(head_bytes[:4], byteorderlittle) h int.from_bytes(head_bytes[4:8], byteorderlittle) bit_depth int.from_bytes(head_bytes[8:12], byteorderlittle) pixel_format head_bytes[12:44].decode(utf-8).strip(\x00) data_len int.from_bytes(head_bytes[64:68], byteorderlittle) pixel_data f.read(data_len) return w, h, bit_depth, pixel_format, data_len, pixel_data def bayer_to_rgb8(bayer_data: bytes, width: int, height: int, bit_depth:int) - np.ndarray: 将海康MV相机的Bayer RG数据转换为RGB8格式图像 :param bayer_data: 相机输出的bayerRG原始字节数据 :param width: 图像宽度像素 :param height: 图像高度像素 :return: RGB8格式的numpy数组uint8 # 步骤1转成np数列 # rg_array np.frombuffer(bayer_data) # 步骤2数据归一化到8位0~255 if bit_depth 8: bayer_array np.frombuffer(bayer_data, dtypenp.uint8).reshape((h, w)) uint8_array (bayer_array 2).astype(np.uint8) elif bit_depth in [10, 12]: bayer_array np.frombuffer(bayer_data, dtypenp.uint16).reshape((h, w)) uint8_array (bayer_array 4).astype(np.uint8) else: raise ValueError(不支持的位深) # 步骤3Bayer RG对应OpenCV的BAYER_RGGB格式转RGB8 rgb8_image cv2.cvtColor(uint8_array, cv2.COLOR_BAYER_RG2RGB) return rgb8_image w, h, bit_depth, pixel_format, data_len, pixel_data read_bayer_raw(bayer-rg12_with_info.raw) bgr_img bayer_to_rgb8(pixel_data, w, h, bit_depth) cv2.imshow(bayer, bgr_img) # cv2.imwrite(bayer10.png, bgr_img) cv2.waitKey(0) cv2.destroyAllWindows() # rg10:12,582,912 3072*20486,291,456*212,582,912
PyQt:从图像文件或字节流生成QImage的速度测试
一幅从相机获取的3072 * 2048像素的图片原始像素格式为bayer-rb将其分别保存为png、bmp、jpg以及raw直接存储元素的原始bayer-rb字节数据格式的本地文件然后使用Qt转换为QImage并显示统计用时。使用QImage()直接读取png格式并生成QImage对象import sys import cv2 import numpy as np from PySide6.QtCore import QElapsedTimer from PySide6.QtGui import QPixmap, QImage from PySide6.QtWidgets import QApplication, QWidget, QVBoxLayout, QLabel class MyWidget(QWidget): def __init__(self): super().__init__() self.init_ui() def init_ui(self): layout QVBoxLayout() self.label QLabel() layout.addWidget(self.label) self.setLayout(layout) if __name__ __main__: app QApplication(sys.argv) widget MyWidget() widget.show() # 计时器 timer QElapsedTimer() timer.start() q_image QImage(out.png) print(f转换QImage耗时{timer.elapsed()} ms) timer.restart() pixmap QPixmap.fromImage(q_image) print(f转换QPixmap耗时{timer.elapsed()} ms) timer.restart() widget.label.setPixmap(pixmap) print(f设置QPixmap耗时{timer.elapsed()} ms) # 宽[3072], 高[2048] sys.exit(app.exec())用时转换QImage耗时136 ms 转换QPixmap耗时0 ms 设置QPixmap耗时0 ms使用QImage()直接读取bmp格式并生成QImage对象import sys import cv2 import numpy as np from PySide6.QtCore import QElapsedTimer from PySide6.QtGui import QPixmap, QImage from PySide6.QtWidgets import QApplication, QWidget, QVBoxLayout, QLabel class MyWidget(QWidget): def __init__(self): super().__init__() self.init_ui() def init_ui(self): layout QVBoxLayout() self.label QLabel() layout.addWidget(self.label) self.setLayout(layout) if __name__ __main__: app QApplication(sys.argv) widget MyWidget() widget.show() # 计时器 timer QElapsedTimer() timer.start() q_image QImage(out.bmp) print(f转换QImage耗时{timer.elapsed()} ms) timer.restart() pixmap QPixmap.fromImage(q_image) print(f转换QPixmap耗时{timer.elapsed()} ms) timer.restart() widget.label.setPixmap(pixmap) print(f设置QPixmap耗时{timer.elapsed()} ms) # 宽[3072], 高[2048] sys.exit(app.exec())用时转换QImage耗时56 ms 转换QPixmap耗时0 ms 设置QPixmap耗时0 msjpg格式import sys import cv2 import numpy as np from PySide6.QtCore import QElapsedTimer from PySide6.QtGui import QPixmap, QImage from PySide6.QtWidgets import QApplication, QWidget, QVBoxLayout, QLabel class MyWidget(QWidget): def __init__(self): super().__init__() self.init_ui() def init_ui(self): layout QVBoxLayout() self.label QLabel() layout.addWidget(self.label) self.setLayout(layout) if __name__ __main__: app QApplication(sys.argv) widget MyWidget() widget.show() # 计时器 timer QElapsedTimer() timer.start() q_image QImage(out.jpg) print(f转换QImage耗时{timer.elapsed()} ms) timer.restart() pixmap QPixmap.fromImage(q_image) print(f转换QPixmap耗时{timer.elapsed()} ms) timer.restart() widget.label.setPixmap(pixmap) print(f设置QPixmap耗时{timer.elapsed()} ms) # 宽[3072], 高[2048] sys.exit(app.exec())用时转换QImage耗时91 ms 转换QPixmap耗时0 ms 设置QPixmap耗时0 msbayer格式字节流由于Qt不直接支持bayer格式使用numpy进行转换import sys import cv2 import numpy as np from PySide6.QtCore import QElapsedTimer from PySide6.QtGui import QPixmap, QImage from PySide6.QtWidgets import QApplication, QWidget, QVBoxLayout, QLabel class MyWidget(QWidget): def __init__(self): super().__init__() self.init_ui() def init_ui(self): layout QVBoxLayout() self.label QLabel() layout.addWidget(self.label) self.setLayout(layout) def bayer_bytes_to_qimage(bayer_bytes, width, height): bayer_array np.frombuffer(bayer_bytes, dtypenp.uint8).reshape((height, width)) # 将bayer格式字节流转换为numpy数组 rgb_array cv2.cvtColor(bayer_array, cv2.COLOR_BAYER_RG2BGR) # 将bayer格式数组转换为RGB格式数组(如果raw是使用opencv创建的有可能cv2.COLOR_BAYER_RG2BGR格式转换 q_image QImage(rgb_array.data, rgb_array.shape[1], rgb_array.shape[0], w * 3, QImage.Format.Format_RGB888) return q_image if __name__ __main__: app QApplication(sys.argv) widget MyWidget() widget.show() # 计时器 timer QElapsedTimer() timer.start() # 模拟字节流读取文件为字节实际场景可能是网络返回、数据库读取 with open(out.raw, rb) as f: bayer_bytes f.read() print(f读取raw文件时间: {timer.elapsed()} ms) h 2048 w 3072 timer.restart() q_image bayer_bytes_to_qimage(bayer_bytes, w, h) print(f转换QImage耗时{timer.elapsed()} ms) timer.restart() pixmap QPixmap.fromImage(q_image) print(f转换QPixmap耗时{timer.elapsed()} ms) timer.restart() widget.label.setPixmap(pixmap) print(f设置QPixmap耗时{timer.elapsed()} ms) # 宽[3072], 高[2048] sys.exit(app.exec())用时读取raw文件时间: 2 ms cv2转换耗时4 ms 转换QPixmap耗时7 ms用时对比读取格式读文件用时(ms)转QImage耗时(ms)转换QPixmap耗时(ms)总耗时(ms)png--1360136bmp--56056jpg--91091raw24713使用原始图像字节流并使用numpy转换效率远远比Qt直接读取图像文件高其中的原因是numpy支持不创建副本的内存视图引用和矩阵转换。验证直接读取bmp图像文件的像素字节并用numpy转换为数组后转为QImageimport sys import cv2 import numpy as np from PySide6.QtCore import QElapsedTimer from PySide6.QtGui import QPixmap, QImage from PySide6.QtWidgets import QApplication, QWidget, QVBoxLayout, QLabel class MyWidget(QWidget): def __init__(self): super().__init__() self.init_ui() def init_ui(self): layout QVBoxLayout() self.label QLabel() layout.addWidget(self.label) self.setLayout(layout) if __name__ __main__: app QApplication(sys.argv) widget MyWidget() widget.show() # 计时器 timer QElapsedTimer() timer.start() h 2048 w 3072 with open(out.bmp, rb) as f: f.seek(54) image_bytes f.read() # 去除bmp头部信息 print(f读取bmp文件时间: {timer.elapsed()} ms) timer.restart() rgb_array np.frombuffer(image_bytes, dtypenp.uint8).reshape((h, w, 3)) # 将字节流转换为numpy数组 rgb_arrayy np.flipud(rgb_array) # 垂直翻转bmp图像像素是从左下角开始存储的 q_image QImage(rgb_array.data, w, h, w * 3, QImage.Format.Format_RGB888) # 将numpy数组转换为QImage print(f转换QImage耗时{timer.elapsed()} ms) timer.restart() pixmap QPixmap.fromImage(q_image) print(f转换QPixmap耗时{timer.elapsed()} ms) timer.restart() widget.label.setPixmap(pixmap) print(f设置QPixmap耗时{timer.elapsed()} ms) # 宽[3072], 高[2048] sys.exit(app.exec())总耗时17ms读取bmp文件时间: 9 ms 转换QImage耗时0 ms 转换QPixmap耗时8 ms 设置QPixmap耗时0 ms然后也使用Qt从图像文件的原始字节流生成QPixmap进行比对import sys import cv2 import numpy as np from PySide6.QtCore import QElapsedTimer from PySide6.QtGui import QPixmap, QImage from PySide6.QtWidgets import QApplication, QWidget, QVBoxLayout, QLabel class MyWidget(QWidget): def __init__(self): super().__init__() self.init_ui() def init_ui(self): layout QVBoxLayout() self.label QLabel() layout.addWidget(self.label) self.setLayout(layout) if __name__ __main__: app QApplication(sys.argv) widget MyWidget() widget.show() # 计时器 timer QElapsedTimer() timer.start() with open(out.bmp, rb) as f: image_bytes f.read() print(f读取bmp文件时间: {timer.elapsed()} ms) timer.restart() pixmap QPixmap() pixmap.loadFromData(image_bytes) print(f转换QPixmap耗时{timer.elapsed()} ms) timer.restart() widget.label.setPixmap(pixmap) print(f设置QPixmap耗时{timer.elapsed()} ms) # 宽[3072], 高[2048] sys.exit(app.exec())总耗时45ms读取bmp文件时间: 9 ms 转换QPixmap耗时36 ms 设置QPixmap耗时0 ms当然也可以分两步先用原始字节流生成QImage再转换QPixmap结果差不多:import sys from PySide6.QtCore import QElapsedTimer from PySide6.QtGui import QPixmap, QImage from PySide6.QtWidgets import QApplication, QWidget, QVBoxLayout, QLabel class MyWidget(QWidget): def __init__(self): super().__init__() self.init_ui() def init_ui(self): layout QVBoxLayout() self.label QLabel() layout.addWidget(self.label) self.setLayout(layout) if __name__ __main__: app QApplication(sys.argv) widget MyWidget() widget.show() # 计时器 timer QElapsedTimer() timer.start() h 2048 w 3072 with open(out.bmp, rb) as f: image_bytes f.read() print(f读取bmp文件时间: {timer.elapsed()} ms) timer.restart() q_image QImage() q_image.loadFromData(image_bytes) print(f转换QImage耗时{timer.elapsed()} ms) timer.restart() pixmap QPixmap.fromImage(q_image) print(f转换QPixmap耗时{timer.elapsed()} ms) timer.restart() widget.label.setPixmap(pixmap) print(f设置QPixmap耗时{timer.elapsed()} ms) # 宽[3072], 高[2048] sys.exit(app.exec())总耗时54ms读取bmp文件时间: 11 ms 转换QImage耗时43 ms 转换QPixmap耗时0 ms 设置QPixmap耗时0 ms结论无论是读取本地文件或者使用字节流使用numpy把像素字节转换成数组再生成QImage的效率远远高于Qt本身的QImage生成功能实测4倍以上。附将相机原始bayer字节流保存为.raw文件的方法def save_bayer_to_raw( pixel_data: bytes, width: int, height: int, pixel_format: str, # BayerRG8, BayerRG10, BayerRG10P, BayerRG12, BayerRG12P output_path: str output.raw ): 海康相机Bayer裸数据 自定义二进制头 → 保存为 .raw 文件 自定义头格式固定128字节方便后续读取解析 # -------------------------- # 1. 构造自定义文件头128字节 # -------------------------- header_size 128 header_bytes bytearray(header_size) # 偏移0宽度 (4字节 int) w_bytes width.to_bytes(4, byteorderlittle) header_bytes[:4] w_bytes # 偏移4高度 (4字节 int) h_bytes height.to_bytes(4, byteorderlittle) header_bytes[4:8 ] h_bytes # 偏移8位深 (4字节 int) bit_depth { BayerRG8: 8, BayerRG10: 10, BayerRG10P: 10, BayerRG12: 12, BayerRG12P: 12 }[pixel_format] bit_depth_bytes bit_depth.to_bytes(4, byteorderlittle) # 位深 header_bytes[8:12] bit_depth_bytes # 偏移12像素格式字符串最多32字节 fmt_bytes pixel_format.encode(utf-8) # 像素格式 fmt_bytes fmt_bytes.ljust(32, b\x00) # 不足补0 header_bytes[12:44] fmt_bytes # 偏移64数据总长度4字节 unsigned long long data_len len(pixel_data) l_bytes data_len.to_bytes(4, byteorderlittle, signedFalse) header_bytes[64:68] l_bytes # -------------------------- # 2. 拼接头 裸像素数据 # -------------------------- raw_file_bytes b.join([header_bytes, pixel_data]) # -------------------------- # 3. 二进制写入文件 # -------------------------- with open(output_path, wb) as f: f.write(raw_file_bytes) print(f✅ 保存成功{output_path}) print(f 尺寸{width}x{height}) print(f 格式{pixel_format}) print(f 头长度{header_size} 像素数据长度{data_len})以及从含宽高信息的.raw文件读取bayer字节流和转换成RGB数组的方法import cv2 import numpy as np def read_bayer_raw(raw_file_path): with open(raw_file_path, rb) as f: # 从本地文件读取bayer-rg格式字节流(这个字节流也可以是从相机获取的) head_bytes f.read(128) # 读取文件头 # 解析头部数据 w int.from_bytes(head_bytes[:4], byteorderlittle) h int.from_bytes(head_bytes[4:8], byteorderlittle) bit_depth int.from_bytes(head_bytes[8:12], byteorderlittle) pixel_format head_bytes[12:44].decode(utf-8).strip(\x00) data_len int.from_bytes(head_bytes[64:68], byteorderlittle) pixel_data f.read(data_len) return w, h, bit_depth, pixel_format, data_len, pixel_data def bayer_to_rgb8(bayer_data: bytes, width: int, height: int, bit_depth:int) - np.ndarray: 将海康MV相机的Bayer RG数据转换为RGB8格式图像 :param bayer_data: 相机输出的bayerRG原始字节数据 :param width: 图像宽度像素 :param height: 图像高度像素 :return: RGB8格式的numpy数组uint8 # 步骤1转成np数列 # rg_array np.frombuffer(bayer_data) # 步骤2数据归一化到8位0~255 if bit_depth 8: bayer_array np.frombuffer(bayer_data, dtypenp.uint8).reshape((h, w)) uint8_array (bayer_array 2).astype(np.uint8) elif bit_depth in [10, 12]: bayer_array np.frombuffer(bayer_data, dtypenp.uint16).reshape((h, w)) uint8_array (bayer_array 4).astype(np.uint8) else: raise ValueError(不支持的位深) # 步骤3Bayer RG对应OpenCV的BAYER_RGGB格式转RGB8 rgb8_image cv2.cvtColor(uint8_array, cv2.COLOR_BAYER_RG2RGB) return rgb8_image w, h, bit_depth, pixel_format, data_len, pixel_data read_bayer_raw(bayer-rg12_with_info.raw) bgr_img bayer_to_rgb8(pixel_data, w, h, bit_depth) cv2.imshow(bayer, bgr_img) # cv2.imwrite(bayer10.png, bgr_img) cv2.waitKey(0) cv2.destroyAllWindows() # rg10:12,582,912 3072*20486,291,456*212,582,912