3、图像读取与显示:imread()函数详解、imshow()窗口管理、waitKey()等待机制
好,咱们进入第三个核心环节。图像读取与显示,说白了就是OpenCV的“敲门砖”。你连图都读不进来、显示不出来,后面那些花里胡哨的处理就全是空谈。
我个人习惯,每次写新项目,第一件事就是先跑通imread和imshow。这就像炒菜前先热锅,是个仪式感,也是个验证环境的好办法。
3.1 imread():把硬盘里的图“请”进内存
这个函数,是OpenCV里最常用的函数之一,没有“之一”可能有点夸张,但绝对排前三。
它的原型很简单:
cv::Mat img = cv::imread(const String& filename, int flags = IMREAD_COLOR);
两个参数:
- filename:图片路径。可以是绝对路径,也可以是相对路径。我建议你一开始就用绝对路径,省得找半天找不到图。
- flags:读取方式。这个参数决定了图片以什么“颜色模式”进入内存。
flags的常用值,我整理了一张表,你存着:
| 标志 | 数值 | 含义 |
|---|---|---|
| IMREAD_COLOR | 1 | 默认。加载为BGR三通道彩色图。忽略透明通道。 |
| IMREAD_GRAYSCALE | 0 | 加载为单通道灰度图。 |
| IMREAD_UNCHANGED | -1 | 加载原图,包括Alpha通道(如果有的话)。 |
嗯,这里要注意一点:OpenCV默认的颜色通道是BGR,不是RGB。很多新手在这里栽跟头。我曾经在项目里用imread读了一张图,然后用matplotlib显示,结果颜色全偏蓝。折腾了半天才发现是通道顺序的问题。
if (img.empty()) {
std::cerr << "图片读失败了,检查路径!" << std::endl;
return -1;
}
3.2 imshow():把内存里的图“亮”出来
图读进来了,怎么看到它?用imshow。
cv::imshow(const String& winname, InputArray mat);
两个参数:
- winname:窗口名字。你可以随便起,但最好有意义。比如“Original Image”、“Processed Result”。
- mat:要显示的图像数据。
窗口管理这块,有几个小细节:
- 窗口大小:默认情况下,窗口会自适应图片大小。如果图片太大,你可以用
cv::namedWindow()先创建窗口,再设置属性。比如cv::WINDOW_NORMAL可以让窗口自由缩放。 - 多个窗口:你可以同时显示多个窗口,只要窗口名字不同就行。我调试的时候经常开三四个窗口,原图、灰度图、边缘检测图,一目了然。
- 窗口销毁:用
cv::destroyWindow("窗口名")销毁单个窗口,或者cv::destroyAllWindows()一键清空。
cv::moveWindow()手动调整位置。不过说实话,我很少用这个,默认位置也挺好。
3.3 waitKey():让程序“等一等”
这个函数,是很多初学者的“噩梦”。为什么我显示了图片,窗口一闪就没了?
答案就在waitKey()。
int cv::waitKey(int delay = 0);
它的作用很简单:等待用户按键。
- delay = 0:无限等待,直到用户按下任意键。
- delay > 0:等待delay毫秒。如果超时没有按键,程序继续往下走。
为什么会这样?因为imshow()只是把图像数据“推送”到了显示缓冲区,但真正刷新到屏幕上,需要waitKey()来触发。说白了,waitKey()给了OpenCV一个“喘口气、刷新一下”的机会。
我刚开始学的时候,也犯过这个错。写了imshow,没写waitKey,结果窗口一闪而过,我还以为是电脑坏了。后来查了文档才明白,原来OpenCV的事件循环是靠waitKey()驱动的。
cv::imshow("窗口名", img);
cv::waitKey(0); // 等待按键
另外,waitKey()的返回值也很有用。它返回的是按键的ASCII码。你可以用它来做交互控制:
int key = cv::waitKey(0);
if (key == 'q') {
std::cout << "用户按了q,退出" << std::endl;
} else if (key == 's') {
cv::imwrite("save.jpg", img);
std::cout << "图片已保存" << std::endl;
}
你想想看,这样是不是就能做一个简单的按键响应程序了?
3.4 完整示例:一条龙走一遍
说了这么多,咱们来一个完整的例子。把读取、显示、等待、销毁串起来:
#include <opencv2/opencv.hpp>
#include <iostream>
int main() {
// 1. 读取图片
cv::Mat img = cv::imread("lena.jpg", cv::IMREAD_COLOR);
if (img.empty()) {
std::cerr << "图片读失败了!" << std::endl;
return -1;
}
// 2. 显示图片
cv::imshow("Lena - 彩色", img);
// 3. 转成灰度图再显示
cv::Mat gray;
cv::cvtColor(img, gray, cv::COLOR_BGR2GRAY);
cv::imshow("Lena - 灰度", gray);
// 4. 等待按键
std::cout << "按任意键关闭窗口..." << std::endl;
cv::waitKey(0);
// 5. 清理
cv::destroyAllWindows();
return 0;
}
这个例子,你直接复制到你的IDE里跑一下。如果能看到两个窗口,一个彩色一个灰度,那就说明你环境没问题,基础操作也掌握了。
嗯,最后再啰嗦一句:imread、imshow、waitKey这三个函数,是OpenCV图像处理的“三驾马车”。你后面学到的所有图像处理操作,都离不开它们。把这三个函数用熟了,后面的路就好走了。