OpenCV图像处理与编程实践

内容分享2天前发布
0 0 0

table {
border-collapse: collapse;
width: 100%;
margin-bottom: 1rem;
}
th, td {
border: 1px solid #ddd;
padding: 8px;
text-align: left;
}
th {
background-color: #f2f2f2;
}
tr:nth-child(even) {
background-color: #f9f9f9;
}
pre {
background-color: #f8f8f8;
padding: 15px;
border-radius: 4px;
overflow-x: auto;
}

27、加载一张场景图像。使用 cvPyrSegmentation() 函数进行处理并显示结果。

加载图像,可使用

cvLoadImage()

函数;

调用

cvPyrSegmentation()

函数对加载的图像进行分割处理;

创建窗口,使用

cvNamedWindow()

函数;

显示处理后的图像,使用

cvShowImage()

函数;

等待按键,使用

cvWaitKey()

函数;

释放资源和销毁窗口,使用

cvReleaseImage()


cvDestroyWindow()

函数。

示例代码框架如下:


#include "highgui.h"

int main( int argc, char** argv )
{
    IplImage* img = cvLoadImage( argv[1] );

    // 进行金字塔分割
    IplImage* segmented_img;
    cvPyrSegmentation(img, segmented_img, ...); // 此处需补充 cvPyrSegmentation() 具体参数

    cvNamedWindow( "Segmented Image", CV_WINDOW_AUTOSIZE );
    cvShowImage( "Segmented Image", segmented_img );
    cvWaitKey(0);

    cvReleaseImage( &img );
    cvReleaseImage( &segmented_img );
    cvDestroyWindow( "Segmented Image" );

    return 0;
}

28、创建一个新图像,该图像仅包含一条45度的线,白色线条位于黑色背景上。对于给定的一系列光圈大小,我们将获取该图像的一阶x导数(dx)和一阶y导数(dy)。然后按以下方式对这条线进行测量。(dx)和(dy)图像构成输入图像的梯度。位置(i,j)处的幅值为mag(i,j)=sqrt(dx(i,j)^2 + dy(i,j)^2),角度为θ(i,j)=arctan(dy(i,j)/dx(i,j))。扫描整个图像,找出幅值达到或接近最大值的位置。记录这些位置的角度。对这些角度求平均值,并将其作为测量得到的线条角度进行报告。

按照题目描述的步骤进行操作,即:

创建图像

计算导数

计算幅值和角度

找出幅值最大或接近最大的位置

记录角度

求平均值

报告测量的线条角度

29、请描述对OpenCV进行操作及探索其目录结构和各目录所含内容的步骤

操作步骤

先下载并安装 OpenCV。

然后系统地浏览其目录结构。

打开

docs

目录中的

index.htm

以获取该库的主要文档。

进一步探索该库的主要区域,查看

_make

目录和

samples

目录。


各主要目录及所含内容


Cvcore

:包含基本的数据结构和算法。


cv

:包含图像处理和视觉算法。


ml

:包含机器学习和聚类算法。


otherlibs/highgui

:包含输入/输出函数。


_make 目录

:包含 OpenCV 构建文件。


samples 目录

:存放示例代码。

30、创建一个从摄像头读取并将下采样彩色图像存储到磁盘的程序,下采样函数使用高斯滤波,图像尺寸缩小为原来的一半。

推测程序框架如下:

首先使用

cvCreateCameraCapture()

从摄像头获取视频流;

然后对每一帧使用

doPyrDown()

函数进行下采样;

接着使用

cvCreateVideoWriter()

创建视频写入器;

最后将下采样后的帧写入视频文件。

大致代码如下:


#include <opencv2/opencv.hpp>

IplImage* doPyrDown(IplImage* in, int filter = IPL_GAUSSIAN_5x5) {
    assert(in->width % 2 == 0 && in->height % 2 == 0);
    IplImage* out = cvCreateImage(cvSize(in->width / 2, in->height / 2), in->depth, in->nChannels);
    cvPyrDown(in, out);
    return out;
}

int main(int argc, char** argv) {
    CvCapture* capture = cvCreateCameraCapture(-1);
    if (!capture) {
        return -1;
    }
    CvSize size = cvSize((int)cvGetCaptureProperty(capture, CV_CAP_PROP_FRAME_WIDTH) / 2,
                         (int)cvGetCaptureProperty(capture, CV_CAP_PROP_FRAME_HEIGHT) / 2);
    double fps = cvGetCaptureProperty(capture, CV_CAP_PROP_FPS);
    CvVideoWriter* writer = cvCreateVideoWriter(argv[1], CV_FOURCC('M', 'J', 'P', 'G'), fps, size);
    IplImage* bgr_frame;
    while ((bgr_frame = cvQueryFrame(capture)) != NULL) {
        IplImage* downsampled_frame = doPyrDown(bgr_frame);
        cvWriteFrame(writer, downsampled_frame);
        cvReleaseImage(&downsampled_frame);
    }
    cvReleaseVideoWriter(&writer);
    cvReleaseCapture(&capture);
    return 0;
}

31、现有两份代码,一份用于处理帧,另一份用于创建窗口并显示图像。请修改处理帧的代码,并将其与创建窗口显示图像的代码相结合,以在窗口中显示处理过程中的帧。

需结合处理帧的代码和创建窗口显示图像的代码,将处理后的帧通过创建窗口显示图像的代码进行显示。一般步骤可能包括:

在处理帧的代码中找到处理帧的部分

在合适位置添加用于创建窗口和显示图像的代码

确保窗口能正确显示处理后的帧

32、选择一个负浮点数,取其绝对值,进行四舍五入,然后取其上限和下限。生成一些随机数。创建一个浮点型的 CvPoint2D32f 并将其转换为整型的 CvPoint。将一个 CvPoint 转换为 CvPoint2D32f。

以下是实现思路:

选择负浮点数,使用绝对值函数(如

fabs

)取绝对值,使用四舍五入函数(如

round

)进行四舍五入,使用上限函数(如

ceil

)和下限函数(如

floor

)取上限和下限。

生成随机数可使用

rand

函数。

创建

CvPoint2D32f

并转换为

CvPoint

,可通过强制类型转换实现。


CvPoint

转换为

CvPoint2D32f

,同样可简单赋值实现。

33、创建一个二维矩阵,其数据大小为 100×100,具有三个字节类型的通道。将所有值设置为 0。使用 void cvCircle( CvArr* img, CvPoint center, int radius, CvScalar color, int thickness = 1, int line_type = 8, int shift = 0 ) 函数在矩阵中绘制一个圆。并显示此图像。

可按以下步骤完成:

创建 100×100 大小、三通道字节类型矩阵并初始化为 0;

使用

cvCircle

函数绘制圆;

显示图像。

34、创建一个数据大小为 100×100 的三通道字节类型二维矩阵,并将所有值设置为 0。使用指针元素访问函数 cvPtr2D 指向中间(“绿色”)通道。在 (20, 5) 和 (40, 20) 之间绘制一个绿色矩形。

通常步骤为:先创建矩阵并初始化为 0,再用 cvPtr2D 指向绿色通道,最后在指定坐标绘制矩形。

35、创建一个大小为 100×100 的三通道 RGB 图像。将其清空。使用指针运算在 (20, 5) 和 (40, 20) 之间绘制一个绿色正方形。

以下是使用OpenCV实现该功能的Python代码示例:


import cv2
import numpy as np

# 创建一个100x100的三通道RGB图像并清空
image = np.zeros((100, 100, 3), dtype=np.uint8)

# 定义正方形的坐标
x1, y1 = 5, 20
x2, y2 = 20, 40

# 使用指针运算绘制绿色正方形
for y in range(y1, y2 + 1):
    for x in range(x1, x2 + 1):
        # 绿色对应的RGB值为(0, 255, 0)
        image[y, x] = [0, 255, 0]

# 显示图像
cv2.imshow('Green Square', image)
cv2.waitKey(0)
cv2.destroyAllWindows()

上述代码首先创建了一个100×100的三通道RGB图像并将其清空,然后使用双重循环和指针运算在指定坐标之间绘制了一个绿色正方形,最后显示该图像。

36、使用cvCmp()创建一个掩码。加载一张真实图像。使用cvSplit()将图像分割为红色、绿色和蓝色图像。找出并显示绿色图像。将这个绿色平面图像克隆两次(分别命名为clone1和clone2)。找出绿色平面的最小值和最大值。将clone1的值设置为thresh = (unsigned char)((最大值 – 最小值)/2.0)。将clone2设置为0,并使用cvCmp(绿色图像, clone1, clone2, CV_CMP_GE)。现在clone2将包含一个掩码,指示绿色图像中值超过thresh的位置。最后,使用cvSubS(绿色图像, thresh/2, 绿色图像, clone2)并显示结果。

以下是实现该功能的大致步骤和示例代码(使用OpenCV):


import cv2
import numpy as np

# 加载图像
image = cv2.imread('your_image.jpg')

# 分割图像为RGB通道
b, g, r = cv2.split(image)

# 显示绿色图像
cv2.imshow('Green Image', g)

# 克隆绿色图像两次
clone1 = g.copy()
clone2 = g.copy()

# 计算阈值
min_val, max_val, _, _ = cv2.minMaxLoc(g)
thresh = np.uint8((max_val - min_val) / 2.0)

# 设置clone1的值为阈值
clone1[:] = thresh

# 设置clone2为0
clone2[:] = 0

# 使用cvCmp创建掩码
cv2.compare(g, clone1, clone2, cv2.CMP_GE)

# 使用cvSubS操作
cv2.subtract(g, thresh // 2, g, clone2)

# 显示结果
cv2.imshow('Result', g)

# 等待按键退出
cv2.waitKey(0)
cv2.destroyAllWindows()

请将

your_image.jpg

替换为你实际的图像文件路径。

37、创建一个程序,该程序要做到:(1)从视频中读取帧;(2)将结果转换为灰度图;(3)对图像进行Canny边缘检测。在三个不同的窗口中显示处理的三个阶段,每个窗口根据其功能进行适当命名。

以下是一个使用Python和OpenCV实现此功能的示例代码:


import cv2

# 打开视频文件
cap = cv2.VideoCapture('your_video.mp4')

while cap.isOpened():
    ret, frame = cap.read()
    if not ret:
        break

    # 原始帧
    cv2.imshow('Original Frame', frame)

    # 转换为灰度图
    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
    cv2.imshow('Grayscale Frame', gray)

    # Canny边缘检测
    edges = cv2.Canny(gray, 100, 200)
    cv2.imshow('Canny Edge Detection', edges)

    # 按 'q' 键退出循环
    if cv2.waitKey(1) & 0xFF == ord('q'):
        break

# 释放资源
cap.release()
cv2.destroyAllWindows()

请将

'your_video.mp4'

替换为你实际的视频文件路径。

38、创建一个程序,该程序读取并显示一幅图像。当用户用鼠标点击图像时,读取相应像素的(蓝色、绿色、红色)值,并将这些值作为文本显示在鼠标点击的位置。

要实现此程序,可按以下思路进行:

使用图像读取函数(如 OpenCV 中的

cvLoadImage

)读取图像;

创建一个窗口,并使用

cvShowImage

显示图像;

设置鼠标回调函数,当检测到鼠标点击事件时,获取点击位置的像素值;

将获取到的像素值转换为文本,并使用

cvPutText

函数将文本显示在鼠标点击位置。

39、在一个单独的窗口中,使用绘图函数绘制一个蓝、绿、红三色的图表,展示在所选框中每种像素值的像素数量。这就是该颜色区域的颜色直方图。x轴应为八个区间,分别代表像素值范围0 – 31、32 – 63、…、223 – 255。y轴应为在该区间范围内找到的像素数量。对每个颜色通道(BGR)都执行此操作。

可按以下步骤实现:

读取图像并选择感兴趣的区域;

分别对BGR三个颜色通道进行处理,统计每个通道在指定区间内的像素数量;

创建一个单独的窗口;

使用绘图函数在窗口中绘制图表,x轴为八个区间,y轴为像素数量,分别用蓝、绿、红三色绘制三个通道的图表。

40、制作一个能读取并显示视频,且由滑块控制的应用程序。一个滑块应将视频从开始到结束分为10个增量来控制播放位置;另一个二进制滑块应控制视频的暂停和播放。为两个滑块添加合适的标签。

实现此应用程序,可按以下步骤进行:

定义全局变量表示播放位置和播放状态。

创建两个滑块,一个用于控制播放位置,另一个用于控制暂停和播放。

实现两个回调函数,分别处理滑块位置变化和暂停/播放状态切换。

在主循环中根据播放状态显示视频帧。

以下是示例代码框架:


#include "cv.h"
#include "highgui.h"

int g_slider_position = 0;
int g_pause = 0;
CvCapture* g_capture = NULL;

void onPositionTrackbarSlide(int pos) {
    cvSetCaptureProperty(
        g_capture,
        CV_CAP_PROP_POS_FRAMES,
        pos * (int)cvGetCaptureProperty(g_capture, CV_CAP_PROP_FRAME_COUNT) / 10
    );
}

void onPauseTrackbarSlide(int pos) {
    g_pause = pos;
}

int main(int argc, char** argv) {
    cvNamedWindow("VideoPlayer", CV_WINDOW_AUTOSIZE);
    g_capture = cvCreateFileCapture(argv[1]);

    int frames = (int)cvGetCaptureProperty(g_capture, CV_CAP_PROP_FRAME_COUNT);
    if (frames != 0) {
        cvCreateTrackbar("Position", "VideoPlayer", &g_slider_position, 10, onPositionTrackbarSlide);
        cvCreateTrackbar("Pause/Play", "VideoPlayer", &g_pause, 1, onPauseTrackbarSlide);
    }

    IplImage* frame;
    while (1) {
        if (!g_pause) {
            frame = cvQueryFrame(g_capture);
            if (!frame) break;
            cvShowImage("VideoPlayer", frame);
        }

        char c = cvWaitKey(33);
        if (c == 27) break;
    }

    cvReleaseCapture(&g_capture);
    cvDestroyWindow("VideoPlayer");
    return 0;
}

此代码仅为示例,实际应用中可能需要更多错误处理和优化。

41、创建你自己的简单绘图程序。编写一个程序,创建一个图像,将其像素值设为 0,然后显示该图像。允许用户使用鼠标左键在图像上绘制直线、圆形、椭圆和多边形。当按住鼠标右键时,创建一个橡皮擦功能。

可按以下步骤编写程序:

创建一个图像并将其像素值初始化为 0。

显示该图像。

监听鼠标事件,当鼠标左键按下时,根据用户操作绘制直线、圆形、椭圆和多边形。

当鼠标右键被按住时,实现橡皮擦功能。

42、编写一个程序,创建一个图像,将其像素值设为 0 并显示。当用户点击某个位置时,能够在该位置输入标签。允许使用退格键进行编辑,并设置一个取消键。按下回车键应将标签固定在输入的位置。

可按以下思路编写:

创建图像并初始化为 0;

显示图像;

监听鼠标点击事件,记录点击位置;

监听键盘输入事件,处理字符输入、退格键和取消键;

按下回车键时,将标签固定在点击位置。

在 OpenCV 中可借助

cvCreateImage

创建图像,

cvSet

初始化像素值,

cvShowImage

显示图像,通过鼠标和键盘回调函数处理相应事件。

43、编写一个程序,读取一张图像,并使用小键盘上的数字 1 – 9 来控制透视变换矩阵。点击任何数字应使透视变换矩阵中对应的单元格的值增加;按住 Shift 键点击数字应使该单元格关联的数字减少(减到 0 为止)。每次改变数字时,在两个图像中显示结果:原始图像和变换后的图像。


首先使用 OpenCV 读取图像,创建透视变换矩阵。然后设置键盘事件监听,当按下数字 1 - 9 时,对应增加矩阵元素的值;按下 Shift + 数字 1 - 9 时,对应减少矩阵元素的值(不小于 0)。每次矩阵元素改变后,使用 `cvWarpPerspective()` 函数进行透视变换,并显示原始图像和变换后的图像。

44、如何在编程中添加旋转图像的功能?

在实际编程中,可使用OpenCV的相关函数(如cv::rotate)来实现图像旋转功能。

45、添加一个有10个设置,对应从0.0到1.0的滑块。使用这个滑块通过cvAddWeighted函数将头骨图像与面部矩形区域进行阿尔法混合。

可按以下步骤实现:

创建一个有10个设置,范围从0.0到1.0的滑块。

在检测到面部矩形区域后,根据滑块的值作为阿尔法值,使用

cvAddWeighted

函数将头骨图像与面部矩形区域进行阿尔法混合。

46、加载一张有趣的图像。再次使用高斯滤波器通过cvSmooth()对其进行模糊处理。设置param1 = param2 = 9。尝试几个param3的设置(例如1、4和6)。显示结果。这次,在将param3设置为1、4和6之前,先将param1 = param2 = 0。显示结果。它们有区别吗?为什么?再次使用param1 = param2 = 0,但现在设置param3 = 1和param4 = 9。对图片进行平滑处理并显示结果。重复该步骤,但将param3设置为9,param4设置为1。显示结果。现在分别使用上述两种参数设置对图像进行一次平滑处理。显示结果。将该结果与使用param3 = param4 = 9和param3 = param4 = 0(即9×9滤波器)的平滑结果进行比较。结果相同吗?为什么相同或不同?

一般来说,不同参数会影响高斯滤波器的窗口大小、标准差等,从而导致平滑效果不同。

47、使用相机尽可能少地移动相机拍摄同一场景的两张照片。将这些图像作为src1和src2加载到计算机中。取src1减去src2的绝对值(对图像做减法);将其称为diff12并显示。如果操作完美,diff12应该是黑色的。为什么它不是黑色的呢?对diff12使用cvErode()然后cvDilate()创建cleandiff并显示结果。对diff12使用cvDilate()然后cvErode()创建dirtydiff并显示结果。解释cleandiff和dirtydiff之间的区别。

diff12不是黑色的原因

可能原因如下:


相机移动

:在拍摄两张照片时,相机可能仍有微小移动,导致场景中物体的位置发生细微变化。


光照变化

:拍摄两张照片期间,光照条件可能发生改变,从而影响图像的亮度和颜色。


相机噪声

:相机本身的噪声也会导致两张图像之间存在差异。

cleandiff与dirtydiff的创建方式

cleandiff


操作步骤



1. 对

diff12

使用

cvErode()

(腐蚀操作)。

2. 再对结果使用

cvDilate()

(膨胀操作)。


效果

主要去除图像中的小噪声点和孤立亮像素。

保留较大的物体特征。

使图像更加“干净”。

dirtydiff


操作步骤



1. 对

diff12

使用

cvDilate()

(膨胀操作)。

2. 再对结果使用

cvErode()

(腐蚀操作)。


效果

填充图像中物体内部的小空洞。

可能使物体边缘变得粗糙。

可能将原本分离的小物体连接起来,使图像看起来更“脏”。

图像处理操作说明


cvErode()

:腐蚀操作,会使图像中的亮部区域缩小。


cvDilate()

:膨胀操作,会使图像中的亮部区域扩大。

48、创建一个低方差随机图像(使用随机数调用,使数字之间的差异不超过3,且大多数数字接近0)。将该图像导入到绘图程序(如PowerPoint)中,然后绘制一组在单点交汇的线条。对生成的图像进行双边滤波并解释结果。


创建低方差随机图像可使用相关编程语言的随机数生成函数,限制随机数范围使差异不超3且多数接近0。导入绘图程序绘制线条后,双边滤波会在平滑图像的同时保留边缘信息。由于图像初始是低方差随机的且绘制了线条,滤波后线条边缘会被较好保留,而图像其他部分会变得更平滑,减少噪声影响。

49、加载一张场景图像并将其转换为灰度图。对图像执行形态学顶帽操作并显示结果。将结果图像转换为8位掩码。将灰度值复制到顶帽部分并显示结果。

一般实现思路如下:

使用图像处理库(如OpenCV)加载图像并转换为灰度图。

使用库中顶帽操作函数进行顶帽操作并显示结果。

将结果图像通过数据类型转换为8位掩码。

将灰度值复制到顶帽部分可通过掩码索引赋值。

最后再次显示图像。

50、使用 cvFilter2D() 创建一个仅检测图像中 60 度线条的滤波器,并在一个足够有趣的图像场景上显示结果。

可按以下步骤实现:

设计一个能检测 60 度线条的滤波器核;

使用

cvFilter2D()

函数将该滤波器应用到图像上;

选择一个有趣的图像场景进行处理,并显示处理后的结果。

具体实现需根据 OpenCV 库和编程语言进行代码编写。

51、可分离核。使用行[(1/16, 2/16, 1/16), (2/16, 4/16, 2/16), (1/16, 2/16, 1/16)]创建一个3×3的高斯核,锚点位于中间。将此核应用于图像并显示结果。

可使用相关图像处理库(如OpenCV),利用

cv2.filter2D()

函数将此 3×3 高斯核应用于图像,再使用相应的图像显示函数展示结果。

示例代码(Python + OpenCV)如下:


import cv2
import numpy as np

# 创建3x3高斯核
kernel = np.array([[1/16, 2/16, 1/16],
                   [2/16, 4/16, 2/16],
                   [1/16, 2/16, 1/16]])

# 读取图像
image = cv2.imread('your_image.jpg', cv2.IMREAD_GRAYSCALE)

# 应用核
filtered_image = cv2.filter2D(image, -1, kernel)

# 显示结果
cv2.imshow('Original Image', image)
cv2.imshow('Filtered Image', filtered_image)
cv2.waitKey(0)
cv2.destroyAllWindows()

需将

'your_image.jpg'

替换为实际图像路径。

© 版权声明

相关文章

暂无评论

none
暂无评论...