opencv基础操作

图像的读取和显示

读写图像

cv::Mat image = cv::imread("image.jpg", cv::IMREAD_COLOR);

使用imread函数读取图像,第一个参数是图像文件的路径,第二个参数是解释图像的颜色和格式(如彩色图像、灰度图像等)。第二个参数可省略,默认是cv::IMREAD_COLOR,以彩色图像读取。

cv::imwrite("output.jpg", image);

使用imwrite存储图像。

显示

使用imshow基于图像用户界面的显示方式,这种方式用于桌面的计算机,直接在屏幕上展示图像。

cv::imshow("Window title", image);
cv::waitKey(0); // 等待按键按下

通过/dev/fb0节点写入,这种方式一般应用在嵌入式平台上,写入屏幕的驱动中。

std::ofstream ofs("/dev/fb0");

cv::Mat framebuffer;
定义一个存储图像像素数据的容器

cv::cvtColor(frame, framebuffer, cv::COLOR_BGR2BGR565);
将图像格式进行转换为与驱动支持的格式BGR565

ofs.write(reinterpret_cast<char*>(framebuffer.ptr(0)), framebuffer.total() * framebuffer.elemSize());

//现将framebuffer强制转换为char*格式,确保内存数据按字节进行处理。
//framebuffer.total()返回的是像素总元素数量,如果framebuffer是多通道,那就返回总数
//framebuffer.elemSize()返回的是每个像素字节大小。
//需要注意的是使用一次性写入,要注意fb的位置,每次写需要重新定位

还有另外一种写法,每一行每一行的刷新

cv::Size2f frame_size = frame.size();

cv::cvtColor(frame, framebuffer_compat, cv::COLOR_BGR2BGR565);

for (int y = 0; y < frame_size.height; y++) {
    ofs.seekp(y * framebuffer_width * 2);
    //定位一行的位置,framebuffer_width是宽,即每行多少个像素
    //*2是每个像素多少个字节,BGR565是2个字节。
    ofs.write(reinterpret_cast<char*>(framebuffer_compat.ptr(y)), frame_size.width * 2);
    //写入一行数据,一行的数据量为frame_size.width * 2
}


下面是封装的示例函数
void display_image(const cv::Mat& image, int framebuffer_width = 720)
{
    static std::ofstream ofs("/dev/fb0");
    if (!ofs) {
        std::cerr << "Error: Could not open framebuffer device!" << std::endl;
        return;
    }

    cv::Mat framebuffer;
    if (image.channels() == 1) {
        cv::cvtColor(image, framebuffer, cv::COLOR_GRAY2BGRA);
    } else if (image.channels() == 3) {
        cv::cvtColor(image, framebuffer, cv::COLOR_BGR2BGRA);
    } else if (image.channels() == 4) {
        image.copyTo(framebuffer);
    } else {
        std::cerr << "Error: Unsupported image format!" << std::endl;
        return;
    }

    cv::Size2f frame_size = framebuffer.size();

    if (frame_size.width > framebuffer_width) {
        // 计算新的宽高,保持图像的纵横比
        float aspect_ratio = frame_size.height / frame_size.width;
        int newWidth = framebuffer_width;
        int newHeight = static_cast<int>(newWidth * aspect_ratio);

        // 调整图像大小
        cv::Mat resizedImage;
        cv::resize(framebuffer, resizedImage, cv::Size(newWidth, newHeight));
        framebuffer = resizedImage;
        frame_size = resizedImage.size();
    }

    for (int y = 0; y < frame_size.height; y++) {
        ofs.seekp(y * framebuffer_width * 4);
        ofs.write(reinterpret_cast<char*>(framebuffer.ptr(y)), frame_size.width * 4);
    }
}

示例

int main()
{
    cv::Mat test_image = cv::imread("test.jpg");
    if (test_image.empty()) {
        std::cerr << "Could not open the image" << std::endl;
        return -1;
    }

    std::ofstream ofs("/dev/fb0");
    if (!ofs.is_open()) {
        std::cerr << "Failed to open framebuffer device." << std::endl;
        return -1;
    }

    // 将 BGR 图像转换为 BGRA 格式(RGB8888)
    cv::Mat framebuffer;
    cv::cvtColor(test_image, framebuffer, cv::COLOR_BGR2BGRA);

    ofs.write(reinterpret_cast<char*>(framebuffer.ptr(0)), 
        framebuffer.total() * framebuffer.elemSize());
    return 0;
}

几何变换

这里的变换指的是缩放、旋转、平移、翻转。

裁剪与缩放

缩放

cv::Mat resizedImage;
cv::resize(image, resizedImage, cv::Size(newWidth, newHeight));

裁剪

Rect roi(100, 100, 200, 200); // (x, y, width, height)
//先于x,y坐标开始,再按照width,height定义一个矩形框。
Mat croppedImage = image(roi);
//将矩形框作用在image上,得到一个新的图像croppedImage

旋转

    // cv::Point2f用于表示二维浮点坐标的类,下面是计算旋转中心点。
    cv::Point2f center(image.cols / 2.0, image.rows / 2.0);

    // 用于计算二维旋转的仿射变换矩阵,45是旋转角度,1.0是缩放比例。
    cv::Mat rotationMatrix = cv::getRotationMatrix2D(center, 45.0, 1.0);
    // 声明存储旋转后图像的变量
    cv::Mat rotatedImage;
    // 执行仿射变换进行图像旋转
    cv::warpAffine(image, rotatedImage, rotationMatrix, image.size());

翻转

cv::Mat flippedImage;
cv::flip(image, flippedImage, 1); // 1表示水平翻转,0表示垂直翻转

颜色变换

图像的颜色空间包括RGB、灰度和HSV等。

  • RGB:RGB是最常见的颜色空间,表示红、绿、蓝三个通道。
  • 灰度:灰度图像只有一个通道,表示亮度。
  • HSV:HSV颜色空间表示色调(Hue)、饱和度(Saturation)和亮度(Value)。

颜色空间转换

RGB转灰度

cv::Mat grayImage;
cv::cvtColor(image, grayImage, cv::COLOR_BGR2GRAY);

RGB转HSV

cv::Mat hsvImage;
cv::cvtColor(image, hsvImage, cv::COLOR_BGR2HSV);

RGB转RGB565

cv::Mat rgb565Image;
cv::cvtColor(rgb565Image, framebuffer, cv::COLOR_BGR2BGR565);

通道分离

这里的通道指的是图像通道比如RGB的R/G/B 3个通道。通道分离的场景是分析每个颜色通道的像素值,比如在检测红色物体时,可以单独分析红色通道。另外还可以用做图像增强,对不同通道应用不同的增强算法,然后再合并。

通道分离

std::vector<cv::Mat> channels; //分离出的通道存储到channels中
cv::split(image, channels);

通道合并

cv::Mat mergedImage;
cv::merge(channels, mergedImage);

示例代码

int main()
{
    cv::Mat test_image = cv::imread("test.jpg");
    if (test_image.empty()) {
        std::cerr << "Could not open the image" << std::endl;
        return -1;
    }
    double angle = 90;

    // 计算旋转中心点
    cv::Point2f center(test_image.cols / 2.0, test_image.rows / 2.0); 
    // 计算旋转矩阵
    cv::Mat rotationMatrix = cv::getRotationMatrix2D(center, angle, 1.0); 
    // 声明存储旋转后图像的变量
    cv::Mat rotatedImage; 
    // 执行仿射变换进行图像旋转
    cv::warpAffine(test_image, rotatedImage, rotationMatrix, test_image.size()); 

    std::ofstream ofs("/dev/fb0");
    if (!ofs.is_open()) {
        std::cerr << "Failed to open framebuffer device." << std::endl;
        return -1;
    }

    // 将 BGR 图像转换为 BGRA 格式(RGB8888)
    cv::Mat framebuffer;
    cv::cvtColor(rotatedImage, framebuffer, cv::COLOR_BGR2BGRA);

    ofs.write(reinterpret_cast<char*>(framebuffer.ptr(0)), 
        framebuffer.total() * framebuffer.elemSize());
    return 0;
}

总结

本章节总结opencv操作常用的数据结构。

Mat

Mat对象的用途主要为存储图像、创建和操作多维矩阵。Mat数据类型分为两部分:信息头+指向像素数据的矩阵指针,信息头存储的是图像的尺寸、存储方法、存储地址。指向像素的矩阵指针为字面意思,即指向存储所有像素值的矩阵指针。

Mat A, C;  //只创建信息头
A = imread(argv[1], IMREAD_COLOR); //为矩阵开辟内存空间。
Mat B(A); // Use the copy constructor
C = A; // Assignment operator

如果在读取图像的时候只想获取图像中的部分图像(感兴趣的区域ROI),可以使用Rect和Range来标中数据。

Mat D (A, Rect(10, 10, 100, 100) ); // using a rectangle
Mat E = A(Range::all(), Range(1,3)); // using row and column boundaries

其中Rect(10,10,100,100)前两位是坐标,后两位指定宽高,示例如下。

Range::all()选中所有的行,Range(1,3)选中1~3列。

Mat对象作为通用矩阵类和图像容器,容器中存储的是原始的像素值。在opencv中描述这些像素值有专门的数据格式。

Mat Img(640, 640, CV_8UC3);

上述中640*640大小的图像,存储的数据类型是CV_U8C3,这里的CV_U8C3格式为基本数据类型+通道数。"CV_"表示前缀,"8"表示每个像素值的位深比特数,有8/16/32/64bits,"U"表示数据类型为无符号,"C3"表示通道数RGB。关于通道一般有几种,示例如下:

  • 1通道:灰度图像
  • 3通道:RGB通道,图像由红、绿、蓝三色组成。
  • 4通道:在RGB通道基础上,加一个透明通道。

Scalar

Scalar用于表示颜色或像素的值,他是一个模板类,存储了4个值,每个值代表图像的颜色通道,如BGR或HSV。

template<typename _Tp>
class Scalar_ {
    public: _Tp val[4]; // 颜色值,最多支持四个通道
    ....
};

cv::Scalar(0, 0, 255); // 红色 BGR 颜色,表示 (蓝色 = 0, 绿色 = 0, 红色 = 255) cv::Scalar(255, 0, 0); // 蓝色 BGR 颜色,表示 (蓝色 = 255, 绿色 = 0, 红色 = 0) cv::Scalar(0, 255, 0); // 绿色 BGR 颜色,表示 (蓝色 = 0, 绿色 = 255, 红色 = 0)


cv::Scalar(0, 255, 0, 128); // 半透明绿色
cv::Scalar(255, 0, 0, 255); // 完全不透明的蓝色

Size

Size类用来表示图像的大小、矩阵的尺寸。

class Size {
public:
    Size();
    Size(int _width, int _height);
    Size(double _width, double _height);
    int width; // 图像宽度
    int height; // 图像高度
};

下面是常用的用法。

设置图像的尺寸
cv::Mat image = cv::imread("image.jpg");
cv::Size new_size(800, 600); // 新的尺寸
cv::resize(image, image, new_size); // 将图像调整为新尺寸

获取图像的尺寸
cv::Mat image = cv::imread("image.jpg");
cv::Size image_size = image.size(); // 获取图像尺寸

创建一个尺寸大小的矩阵
cv::Mat mat(cv::Size(400, 300), CV_8UC1); // 创建一个大小为 400x300 的单通道图像

cvtColor

cvtColor用于颜色空间的转化,图像的颜色空间一般有RGB,BGR(RGB存储顺序的不同),HSV,Lab等,其可以将图像从一种颜色空间变换为另一种颜色空间。

cv::Mat cv::cvtColor(const cv::Mat& src, cv::Mat& dst, int code, int dstCn = 0);
  • src: 输入图像
  • dst:输出图像
  • code:指定转化格式,如 cv::COLOR_BGR2GRAY,cv::COLOR_BGR2HSV。
  • dstCn:输出通道数,如果是0,表示通道数与目标颜色空间通道匹配。
RGB转BGR
cv::Mat src = cv::imread("image.jpg"); // 读取 RGB 图像(如果图像是 RGB 格式的话)
cv::Mat bgr; cv::cvtColor(src, bgr, cv::COLOR_RGB2BGR); // 转换为 BGR 图像

HSB转BRG
cv::Mat hsv = cv::imread("image_hsv.jpg"); // 读取 HSV 图像 
cv::Mat bgr; cv::cvtColor(hsv, bgr, cv::COLOR_HSV2BGR); // 转换为 BGR 图像

BRG转灰度
cv::Mat src = cv::imread("image.jpg"); // 读取 BGR 图像
cv::Mat gray; cv::cvtColor(src, gray, cv::COLOR_BGR2GRAY); // 转换为灰度图