OpenCV_002-图像操作入门

遵循软件开发者学习一项新技术的传统流程,先来看一个 "Hello, world" 程序。

目标

在这份教程中,我们将学习如何:

  • 从文件中读取一幅图像 (使用 cv::imread)
  • 在 OpenCV 窗口中显示一幅图像 (使用 cv::imshow)
  • 向文件中写入一幅图像 (使用 cv::imwrite)

C++ 版本

源代码

可下载的代码:点击 这里

代码一览

#include <opencv2/core.hpp>
#include <opencv2/imgcodecs.hpp>
#include <opencv2/highgui.hpp>
#include <iostream>
using namespace cv;

int main() {
  samples::addSamplesDataSearchPath("/media/data/my_multimedia/opencv-4.x/samples/data");
  std::string image_path = samples::findFile("starry_night.jpg");
  Mat img = imread(image_path, IMREAD_REDUCED_COLOR_2);
  if (img.empty()) {
    std::cout << "Could not read the image: " << image_path << std::endl;
    return 1;
  }
  imshow("Display window", img);
  int k = waitKey(0); // Wait for a keystroke in the window
  if (k == 's') {
    imwrite("starry_night.png", img);
  }
  return 0;
}

源码说明

在 OpenCV 4 中我们有多个模块。其中的每个都关注图像处理的一个不同领域或方法。你可能已经从这些教程本身的用户指南的结构看到了这一点。在你使用它们中的任何一个之前,你首先需要包含头文件,其中声明了每个独立的模块的内容。

你几乎总是会使用:

  • core 部分,因为这里定义了库的基本构建块

  • imgcodecs 模块,提供了用于读取和写入的功能

  • highgui 模块,因为它包含了在窗口中显示图像的功能

我们还包含了 iostream 以方便控制台行输出和输入。

通过声明 using namespace cv;,在下面的代码中,库函数可以无需显式声明命名空间即可访问。

#include <opencv2/core.hpp>
#include <opencv2/imgcodecs.hpp>
#include <opencv2/highgui.hpp>

#include <iostream>

using namespace cv;

现在让我们分析一下主要的代码。第一步,我们从 OpenCV 示例中读取图像 "starry_night.jpg" 文件。为了做到这一点,调用 cv::imread 函数使用由第一个参数指定的文件路径加载图像。第二个参数是可选的,它指定了想要的图像的格式。这个值可以是:

  • IMREAD_COLOR 以 BGR 8 位格式加载图像。这个是这里使用的默认值
  • IMREAD_UNCHANGED 按原本的格式加载图像(包括 alpha 通道,如果有的话)
  • IMREAD_GRAYSCALE 将图像加载为一个灰度图
  • IMREAD_ANYDEPTH,当输入具有对应的深度时返回 16位/32位 图像,否则把它转为 8 位
  • IMREAD_ANYCOLOR,以任何可能的格式读取色彩格式
  • IMREAD_LOAD_GDAL,使用 gdal 驱动加载图像
  • IMREAD_REDUCED_GRAYSCALE_2,总是把图像转为单通道灰度图,且图像大小减为原来的 1/2
  • IMREAD_REDUCED_COLOR_2,总是把图像转为 3 通道 BGR 彩色图像,且图像大小减为原来的 1/2
  • IMREAD_REDUCED_GRAYSCALE_4,总是把图像转为单通道灰度图,且图像大小减为原来的 1/4
  • IMREAD_REDUCED_COLOR_4,总是把图像转为 3 通道 BGR 彩色图像,且图像大小减为原来的 1/4
  • IMREAD_REDUCED_GRAYSCALE_8,总是把图像转为单通道灰度图,且图像大小减为原来的 1/8
  • IMREAD_REDUCED_COLOR_8,总是把图像转为 3 通道 BGR 彩色图像,且图像大小减为原来的 1/8
  • IMREAD_IGNORE_ORIENTATION,不要根据 EXIF 的方向标记旋转图像。

读取之后,图像数据将被保存在 cv::Mat 对象中。

  samples::addSamplesDataSearchPath("/media/data/my_multimedia/opencv-4.x/samples/data");
  std::string image_path = samples::findFile("starry_night.jpg");
  Mat img = imread(image_path, IMREAD_REDUCED_COLOR_2);

OpenCV 的源码仓库中包含了一些测试文件,位于 opencv/samples/data 目录下,samples::findFile() 默认在当前目录中查找文件。但通过 samples::addSamplesDataSearchPath() 函数,可以为示例文件的查找添加搜索目录。对于 Eclipse 来说,默认的程序运行的当前目录就是项目的根目录,但可以通过执行配置中 "Arguments" 标签下的 "Working directory:" 进行配置。

注意:OpenCV 提供了对 Windows 位图 (bmp),可移植图像格式 (pbm,pgm,ppm) 和 Sun raster (sr,ras) 图像格式的支持。借助于插件 (如果是自己编译库的话,需要指定使用它们,尽管如此,在我们提供的包中默认包含它们)你也可以加载像 JPEG (jpeg,jpg,jpe),JPEG 2000 (jp2 - 在 CMake 中的编码名称为 Jasper),TIFF 文件 (tiff,tif) 和可移植网络图形 (png) 这样的图像格式。

imread() 函数在读取文件并解码之后,还可以对文件做格式转换,具体的转换方式由这个函数的第二个参数指定。

之后,执行了一个检查,检查图像是否被正确加载。

  if (img.empty()) {
    std::cout << "Could not read the image: " << image_path << std::endl;
    return 1;
  }

然后,使用一个对 cv::imshow 函数的调用显示图像。第一个参数是窗口的标题,第二个参数是将要显示的 cv::Mat 对象。

由于我们想要我们的窗口一直显示,直到用户按下了一个键(否则程序将过快结束),我们使用 cv::waitKey 函数,它只接受一个参数,表示应该等用户输入多长时间(以毫秒为单位)。0 表示一直等待。返回值为按下的键。

  imshow("Display window", img);
  int k = waitKey(0); // Wait for a keystroke in the window

最后,当按下的键是 "s" 键时,图像被写入文件。为了做到这一点,以文件路径和 cv::Mat 对象为参数调用 cv::imwrite 函数。

  if (k == 's') {
    imwrite("starry_night.png", img);
  }

Python 版本

源代码

可下载的代码:点击 这里

代码一览

#!/use/bin/env python

import cv2 as cv
import sys


def main():
    cv.samples.addSamplesDataSearchPath("/media/data/my_multimedia/opencv-4.x/samples/data")
    img = cv.imread(cv.samples.findFile("starry_night.jpg"))
    if img is None:
        sys.exit("Could not read the image.")
    print(img.__class__.__name__)

    cv.imshow("Display window", img)

    k = cv.waitKey(0)
    if k == ord("s"):
        cv.imwrite("starry_night.png", img)


if __name__ == "__main__":
    main()

源码说明

这里可以看一下 C++ 代码和 Python 代码之间的转换。对于 Python 程序,首先需要导入 OpenCV python 库。执行这个操作的正确方法是,另外为其分配名称 cv,以下将使用这个名称来引用 OpenCV python 库。

import cv2 as cv
import sys

随后,从 OpenCV 示例中读取 "starry_night.jpg" 文件的过程与 C++ 版完全相同:借助于 cv.samples.addSamplesDataSearchPath()cv.samples.findFile() 查找文件的路径;通过调用 cv.imread() 读取文件。各个函数的接口和参数也与 C++ 版完全相同。Python 版的 cv.imread() 同样支持 C++ 版的所有加载标记参数。

cv.imread() 函数返回一个 numpyndarray 对象。

    cv.samples.addSamplesDataSearchPath("/media/data/my_multimedia/opencv-4.x/samples/data")
    img = cv.imread(cv.samples.findFile("starry_night.jpg"))

OpenCV 支持加载的图像文件格式与 C++ 完全相同。

图像文件加载之后,执行检查,检查图像是否被正确加载。

    if img is None:
        sys.exit("Could not read the image.")
    print(img.__class__.__name__)

随后通过 cv.imshow() 函数在窗口中显示图像,通过 cv.waitKey(0) 等待用户按下按键:

    cv.imshow("Display window", img)

    k = cv.waitKey(0)

这两个接口与 C++ 版完全相同。

最后,如果用户按下 "s" 保存文件(与 C++ 版完全相同)。

    if k == ord("s"):
        cv.imwrite("starry_night.png", img)

Done.

参考 Getting Started with Images

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,937评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,503评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,712评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,668评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,677评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,601评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,975评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,637评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,881评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,621评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,710评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,387评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,971评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,947评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,189评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,805评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,449评论 2 342

推荐阅读更多精彩内容