【C++、python】使用OpenCV处理RAW图像数据(读取raw文件、切割raw为图片、根据灰度阈值分割raw输出点云txt、三维模型分割)

概要

在图像处理领域,直接从RAW图像文件中读取数据并进行处理是一个常见的需求。本文介绍了如何使用C++和OpenCV库来读取RAW格式的图像文件,对其进行简单处理,并将结果保存为JPEG格式
此外,还将探讨如何创建和管理输出目录,根据每张图片的灰度值进行阈值分割三维点云模型。最后,给出了实现过程的python代码供参考。

处理结果先附上
处理结果先附上

整体架构流程

1.输入和初始设置

用户指定原始图像文件的路径和输出应存储的目录,系统初始化图像的宽度(w)、高度(h)和深度(depth)的参数。也就是xyz的大小。

2.创建OpenCV的mat对象

用于储存原始图像数据,注意:这里需要指定与raw文件相同的数据类型,例如uint、float.

3.处理并保存每层图像数据

对每个深度层的图像数据进行处理:使用fread从文件读取图像数据到矩阵中。遍历每个像素,如果像素值大于设定阈值,则记录其坐标到输出文本文件。保存处理后的每帧图像为JPEG文件。

代码实现解释

1.所需要的库如下,有些库是刚开始调试时使用的,后续可能没有用到,可以选择性的了解一下。

#include <iostream>    // 用于标准输入输出流
#include <vector>      // 提供向量容器类
#include <fstream>     // 文件流库,用于文件操作
#include <string>      // 字符串类
#include <stdio.h>     // C 标准输入输出库
#include <stdlib.h>    // C 标准库,包括动态内存管理,随机数生成等
#include <sys/stat.h>  // 提供获取文件属性的功能
#include <io.h>        // 输入输出头文件,用于文件的基本操作
#include <direct.h>    // 提供目录操作的函数
#include <opencv2/opencv.hpp> // OpenCV库,用于处理图像数据

using namespace std;         // 使用标准命名空间
using namespace cv;          // 使用OpenCV命名空间

2.read_raw函数(处理模型的函数

void read_raw(string raw_path, string result_dir)
{
    int w = 974;              // 图像宽度
    int h = 970;              // 图像高度
    int depth = 55;           // 图像深度

    FILE* fp = fopen(raw_path.c_str(), "rb");  // 打开原始图像数据文件
    if (!fp)                                   // 判断文件是否成功打开
    {
        cerr << "Error: Unable to open file " << raw_path << endl;
        exit(EXIT_FAILURE);                    // 打开失败,退出程序
    }

    Mat src(h, w, CV_8UC1);                   // 创建OpenCV Mat对象用于存储图像数据

    char* result_path = new char[result_dir.length() + 1];  // 创建字符数组保存结果目录路径
    strcpy(result_path, result_dir.c_str());                // 复制目录路径字符串

    if (_access(result_path, 0) == -1)   // 判断结果目录是否存在
    {
        _mkdir(result_path);             // 如果不存在,则创建结果目录
    }

    ofstream out(result_dir + "cloud.txt");  // 创建输出文件流对象,用于保存处理后的数据

    for (int n = 0; n < depth; n++)        // 遍历图像深度
    {
        fread(src.data, sizeof(unsigned char), w * h, fp);  // 读取原始图像数据

        // 处理图像数据,将像素值大于阈值的像素位置信息写入文件
        for (int r = 0; r < h; r++)
        {
            for (int c = 0; c < w; c++)
            {
                if (src.at<unsigned char>(r, c) > 150)
                {
                    out << c << " " << r << " " << n << endl;
                }
            }
        }

        String filename = result_dir + to_string(n) + ".jpg";  // 构造结果图像文件名
        imwrite(filename, src);                                // 将处理后的图像数据保存为JPEG文件
    }
    out.close();                       // 关闭输出文件流
    delete[] result_path;              // 释放结果目录路径字符数组内存

    if (fclose(fp) != 0)               // 判断是否成功关闭文件
    {
        cerr << "Error: Unable to close file " << raw_path << endl;
    }
}

下面我对处理函数的重点部分进行详细的解释,整个代码可以分成两部分,一部分是:参数的设置和文件的管理,因为这个过程涉及的对文件的操作还是比较多的,几乎涉及了常用的文件操作。另一部分是:图像模型的循环处理,因为是三维模型,现在将其转到二维图像上进行处理。
上述代码都带了注释,就不逐句的解释了,下面主要解释重点部分以及大家在使用的时候需要修改和调试的部分:

    int w = 974;              // 图像宽度
    int h = 970;              // 图像高度
    int depth = 55;           // 图像深度
    Mat src(h, w, CV_8UC1);      // 创建OpenCV Mat对象用于存储图像数据

上述这里是肯定需要修改的部分,因为这是你三维模型xyz轴的大小以及数据类型,没有它,任何图像也不可能读取成功。我这里的三维模型是974×970×55,无符号8位的类型。

for (int n = 0; n < depth; n++)        // 遍历图像深度
    {
        fread(src.data, sizeof(unsigned char), w * h, fp);  // 读取原始图像数据
        //这里需要注意,sizeof是根据你数据类型字节的位数来确定的,绝对不能照搬!!!

        // 处理图像数据,将像素值大于阈值的像素位置信息写入文件
        for (int r = 0; r < h; r++)
        {
            for (int c = 0; c < w; c++)
            {
                if (src.at<unsigned char>(r, c) > 150)
                //这里的at<unsigned char>也是数据类型的位数,注意!!!!!
                {
                    out << c << " " << r << " " << n << endl;
                }
            }
        }

        String filename = result_dir + to_string(n) + ".jpg";  // 构造结果图像文件名
        imwrite(filename, src);                                // 将处理后的图像数据保存为JPEG文件
    }

上述这里是处理的重中之重,我们首先遍历三维图像的深度,也就是我们常说的模型的高,也就是先一层一层的遍历三维图像,然后再遍历图像的高和宽。这样的话就是对二维图像进行灰度上的处理,就比较简单了。找到对应的坐标输出到txt中,这样就形成了阈值分割后的三维点云。
图片输出的话就更简单了,我们设置了opencv的矩阵,将三维模型的宽高设为二维图像的宽高,保存成jpg格式即可。

三维模型处理示意图

小结

这里要强调一下,上述代码是以uint8为例进行处理的,我们平时还会遇到uint16和float甚至double类型的。以float32位为例,我们在处理的过程中就需要添加一步归一化的过程,否则会读取切割失败或者丢失信息。

Mat img_normalized;
normalize(src, img_normalized, 0, 255, NORM_MINMAX);
img_normalized.convertTo(img_normalized, CV_8U);

这里的img_normalized将会在保存的时候替换src矩阵进行保存

imwrite(filename, img_normalized);

该系统流程基于输入的原始图像数据文件,利用OpenCV库进行图像处理,最终输出处理后的结果图像和相关信息。可以根据具体需求和应用场景,调整和扩展流程中的各个步骤,以实现更复杂的图像处理任务。

大概流程就是这样,欢迎大家进行参考学习,如有不当之处欢迎讨论。如对程序有疑问,欢迎留言讨论!!!

附:python实现代码

由于python对处理模型数据非常友好,所以我也在python语言上进行测试。同时再次提醒大家一定要注意数据类型和xyz大小,以及是否需要归一化

import numpy as np
import cv2
import os


def read_raw(raw_path, result_dir, w=974, h=970, depth=55):
    # 确保结果目录存在
    if not os.path.exists(result_dir):
        os.makedirs(result_dir)

    # 读取RAW文件
    try:
        with open(raw_path, "rb") as file:
            for n in range(depth):
                # 读取图像数据
                buffer = file.read(w * h)
                if not buffer:
                    break

                # 将数据转换为numpy数组,对应于CV_8UC1
                src = np.frombuffer(buffer, dtype=np.uint8).reshape(h, w)

                # 创建输出文本文件
                with open(os.path.join(result_dir, f"cloud.txt"), 'a') as out:
                    for r in range(h):
                        for c in range(w):
                            if src[r, c] > 150:
                                out.write(f"{c} {r} {n}\n")

                # 保存图像
                filename = os.path.join(result_dir, f"{n}.jpg")
                cv2.imwrite(filename, src)

    except Exception as e:
        print(f"Error: {str(e)}")


def main():
    print("Processing raw image...")
    raw_path = "974_970_55_uint8.raw"
    result_dir = "result_974_970_55_uint8"  # 请确保此目录存在或被创建
    read_raw(raw_path, result_dir)
    print("Processing complete")


if __name__ == "__main__":
    main()```

  • 17
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值