OpenCV编程任务与实现解析

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;
}

45、编写一个程序,添加轨迹条滑块控件,让用户可以动态地将金字塔下采样缩减级别在2到8的因子范围内进行更改,无需将结果写入磁盘,但要显示结果。

需添加轨迹条滑块控件,实现动态调整金字塔下采样缩减级别在2到8之间,并显示结果。

46、紧凑矩阵和向量模板类型:a. 分别使用 cv::Mat<> 和 cv::Vec<> 模板创建一个 3×3 矩阵和一个 3 行向量。b. 能否直接将它们相乘?如果不能,原因是什么?c. 尝试使用 cv::Mat<> 模板将向量对象类型转换为 3×1 矩阵。现在会发生什么?

a. 使用相应模板可按其构造函数规则创建 3×3 矩阵和 3 行向量;

b. 通常不能直接相乘,因为矩阵乘法要求前一个矩阵的列数等于后一个矩阵的行数,3 行向量与 3×3 矩阵维度不匹配;

c. 将向量类型转换为 3×1 矩阵后,若满足矩阵乘法规则可进行相乘运算。

47、创建一个 ASCII 数字打字机,你可以在电脑中输入数字,这些数字将显示在一个高 20 像素、宽 10 像素的方块中。当你输入时,数字将从左到右显示,直到到达图像末尾,然后停止。

要实现这个功能,可使用 Python 和 OpenCV 库,以下是示例代码:


import cv2
import numpy as np

# 创建一个空白图像
image_height = 20
image_width = 10 * 10 # 假设最多显示 10 个数字
image = np.zeros((image_height, image_width), dtype=np.uint8)

# 初始化当前位置
current_x = 0

# 定义数字的 ASCII 字符集
numbers = '0123456789'

# 定义键盘事件处理函数
def on_key_press(key):
    global current_x
    if chr(key) in numbers and current_x < image_width - 10:
        # 获取数字字符
        digit = chr(key)
        # 创建一个小图像来绘制数字
        digit_image = np.zeros((20, 10), dtype=np.uint8)
        # 在小图像上绘制数字
        cv2.putText(digit_image, digit, (0, 15), cv2.FONT_HERSHEY_SIMPLEX, 0.6, 255, 2)
        # 将小图像复制到主图像的当前位置
        image[:, current_x:current_x + 10] = digit_image
        # 更新当前位置
        current_x += 10

# 创建窗口并显示图像
cv2.namedWindow('ASCII Numeric Typewriter')

while True:
    cv2.imshow('ASCII Numeric Typewriter', image)
    key = cv2.waitKey(1) & 0xFF
    if key == 27: # 按 ESC 键退出
        break
    elif key >= 48 and key <= 57: # 处理数字键
        on_key_press(key)

# 关闭窗口
cv2.destroyAllWindows()

这段代码创建了一个空白图像,并允许用户通过键盘输入数字。数字将从左到右显示在图像中,直到达到图像的末尾。

48、对一张图像使用多个头文件。加载一张尺寸至少为100×100的图像。创建两个额外的感兴趣区域(ROI),其宽度为20,高度为30。它们的原点分别位于(5, 10)和(50, 60)。将这些新的图像子区域传递给cv::bitwise_not()函数。显示加载的图像,该图像应在较大的图像内有两个反转的矩形。

以下为实现该功能的大致步骤:

加载一张尺寸至少为100×100的图像;

创建两个宽度为20、高度为30,原点分别位于(5, 10)和(50, 60)的ROI;

将这两个ROI传递给

cv::bitwise_not()

函数进行反转操作;

显示加载的图像,此时图像内会有两个反转的矩形。

49、绘图实践:加载或创建并显示一幅彩色图像。绘制OpenCV能绘制的每种形状和线条的一个示例。

以下是一个使用OpenCV绘制各种形状和线条的Python示例代码:


import cv2
import numpy as np

# 创建一个空白的彩色图像
image = np.zeros((500, 500, 3), dtype=np.uint8)

# 绘制直线
cv2.line(image, (50, 50), (200, 50), (0, 255, 0), 2)

# 绘制矩形
cv2.rectangle(image, (250, 50), (400, 200), (0, 0, 255), 2)

# 绘制圆形
cv2.circle(image, (125, 250), 75, (255, 0, 0), 2)

# 绘制椭圆
cv2.ellipse(image, (375, 250), (75, 50), 0, 0, 360, (255, 255, 0), 2)

# 绘制多边形
pts = np.array([[50, 350], [150, 350], [200, 450], [100, 450]], np.int32)
pts = pts.reshape((-1, 1, 2))
cv2.polylines(image, [pts], True, (0, 255, 255), 2)

# 显示图像
cv2.imshow('Shapes and Lines', image)

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

上述代码创建了一个空白的彩色图像,并在上面绘制了直线、矩形、圆形、椭圆和多边形,最后显示图像。

50、灰度处理:加载并显示彩色图像。a. 将其转换为三通道灰度图像(仍是BGR图像,但用户看到的是灰色)。b. 在图像上绘制彩色文本。

可按以下步骤实现:

a. 使用

cv::imread

函数加载图像,然后使用

cv::cvtColor

函数将图像转换为灰度图像,再将单通道灰度图像复制到三个通道以创建三通道灰度图像。

b. 使用

cv::putText

函数在图像上绘制彩色文本。

51、使用 cv::LineIterator 来计算例如 300×300 图像中不同线段上的像素数量。a. 在什么角度下,4 – 连通和 8 – 连通的线段像素数量相同?b. 对于上述角度之外的线段角度,4 – 连通和 8 – 连通哪种方式统计的像素更多?c. 对于给定的线段,解释 4 – 连通和 8 – 连通情况下,线段长度与沿线段迭代统计的像素数量之间的差异。哪种连通性更接近真实的线段长度?

a. 当线段角度为水平(0°)、垂直(90°)、45°和 135°时,4 – 连通和 8 – 连通的线段像素数量相同;

b. 对于上述角度之外的线段角度,8 – 连通统计的像素更多;

c. 4 – 连通只考虑上下左右相邻的像素,8 – 连通还考虑了对角线相邻的像素,所以 8 – 连通统计的像素数量通常更多。真实的线段长度是连续的,4 – 连通的统计方式更接近真实线段长度,因为它的连接方式更“规整”,更符合欧几里得距离的概念。

52、在一张图像中显示所有三个处理阶段。(提示:创建另一个高度与视频帧相同,但宽度是视频帧三倍的图像。可以使用指针将图像复制到这个新图像中,或者(更巧妙的方法是)创建三个新的图像头,分别指向图像数据的起始位置、三分之一处和三分之二处。然后使用Mat::copyTo()函数。

可按以下步骤实现:

创建一个高度与视频帧相同、宽度为视频帧三倍的新图像;

可以使用指针或者创建三个新的图像头(分别指向新图像的起始位置、三分之一处和三分之二处);

使用Mat::copyTo()将三个处理阶段的图像分别复制到新图像对应的位置。

53、在一个三图像显示区域的程序中,当在该显示区域内的任意位置点击时,如何显示单个图像的鼠标坐标?

可通过在程序中添加鼠标事件处理函数,在鼠标点击时获取鼠标坐标并显示单个图像的坐标信息来实现。

54、制作一个应用程序,用于读取和显示视频,并通过滑块进行控制。一个滑块将以10个增量控制视频从开始到结束的位置;另一个二进制滑块应控制暂停/播放。为两个滑块都进行适当的标注。

以下是实现思路:

首先使用OpenCV库中的

VideoCapture

读取视频。创建两个滑块,一个用于控制视频位置,另一个用于控制暂停/播放。


控制视频位置的滑块

将其范围设置为视频总帧数。

以10个增量移动。

使用

set

方法设置视频的当前帧。


控制暂停/播放的二进制滑块

根据其状态设置标志位来控制视频的暂停和播放。

以下是简单的示例代码框架:


#include <opencv2/opencv.hpp>
#include <iostream>

int g_slider_position = 0;
int g_pause = 0;
int g_total_frames = 0;
cv::VideoCapture g_cap;

void onPositionSlide(int pos, void*) {
    g_cap.set(cv::CAP_PROP_POS_FRAMES, pos);
}

void onPauseSlide(int pos, void*) {
    g_pause = pos;
}

int main() {
    g_cap.open("your_video.mp4");
    if (!g_cap.isOpened()) {
        std::cout << "Error opening video file" << std::endl;
        return -1;
    }

    g_total_frames = static_cast<int>(g_cap.get(cv::CAP_PROP_FRAME_COUNT));

    cv::namedWindow("Video Player", cv::WINDOW_NORMAL);
    cv::createTrackbar("Position", "Video Player", &g_slider_position, g_total_frames, onPositionSlide);
    cv::createTrackbar("Pause/Play", "Video Player", &g_pause, 1, onPauseSlide);

    cv::Mat frame;
    while (true) {
        if (!g_pause) {
            g_cap >> frame;
            if (frame.empty()) break;
            cv::imshow("Video Player", frame);
            g_slider_position = static_cast<int>(g_cap.get(cv::CAP_PROP_POS_FRAMES));
            cv::setTrackbarPos("Position", "Video Player", g_slider_position);
        }
        if (cv::waitKey(30) == 27) break;
    }

    g_cap.release();
    cv::destroyAllWindows();
    return 0;
}

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

以下是实现思路:首先使用图像处理库(如 OpenCV)创建图像并初始化为 0 后显示。通过监听鼠标事件,当检测到鼠标左键按下和移动时,根据绘图类型(直线、圆形等)在图像上进行绘制。当检测到鼠标右键按下和移动时,将经过的像素值设为 0 实现擦除功能。

以下是简单示例代码框架(使用 OpenCV):


import cv2
import numpy as np

# 创建图像
image = np.zeros((512, 512, 3), np.uint8)

# 绘图状态
drawing = False
shape = 'line'
start_point = (-1, -1)
erasing = False

# 鼠标回调函数
def draw_shape(event, x, y, flags, param):
    global drawing, start_point, erasing
    if event == cv2.EVENT_LBUTTONDOWN:
        drawing = True
        start_point = (x, y)
    elif event == cv2.EVENT_MOUSEMOVE:
        if drawing:
            temp_image = image.copy()
            if shape == 'line':
                cv2.line(temp_image, start_point, (x, y), (0, 255, 0), 2)
            elif shape == 'circle':
                radius = int(((x - start_point[0])**2 + (y - start_point[1])**2)**0.5)
                cv2.circle(temp_image, start_point, radius, (0, 255, 0), 2)
            # 椭圆绘制逻辑
            elif shape == 'ellipse':
                axes = (int(abs(x - start_point[0])), int(abs(y - start_point[1])))
                cv2.ellipse(temp_image, start_point, axes, 0, 0, 360, (0, 255, 0), 2)
            # 多边形绘制逻辑,这里简单以三角形为例,需要手动输入三个点
            elif shape == 'polygon':
                pts = np.array([start_point, (x, y), (start_point[0]+100, start_point[1])], np.int32)
                pts = pts.reshape((-1, 1, 2))
                cv2.polylines(temp_image, [pts], True, (0, 255, 0), 2)
            cv2.imshow('Image', temp_image)
    elif event == cv2.EVENT_LBUTTONUP:
        drawing = False
        if shape == 'line':
            cv2.line(image, start_point, (x, y), (0, 255, 0), 2)
        elif shape == 'circle':
            radius = int(((x - start_point[0])**2 + (y - start_point[1])**2)**0.5)
            cv2.circle(image, start_point, radius, (0, 255, 0), 2)
        # 椭圆绘制逻辑
        elif shape == 'ellipse':
            axes = (int(abs(x - start_point[0])), int(abs(y - start_point[1])))
            cv2.ellipse(image, start_point, axes, 0, 0, 360, (0, 255, 0), 2)
        # 多边形绘制逻辑,这里简单以三角形为例,需要手动输入三个点
        elif shape == 'polygon':
            pts = np.array([start_point, (x, y), (start_point[0]+100, start_point[1])], np.int32)
            pts = pts.reshape((-1, 1, 2))
            cv2.polylines(image, [pts], True, (0, 255, 0), 2)
        cv2.imshow('Image', image)
    elif event == cv2.EVENT_RBUTTONDOWN:
        erasing = True
    elif event == cv2.EVENT_RBUTTONUP:
        erasing = False
    elif event == cv2.EVENT_MOUSEMOVE and erasing:
        cv2.circle(image, (x, y), 5, (0, 0, 0), -1)
        cv2.imshow('Image', image)

# 创建窗口并设置鼠标回调
cv2.namedWindow('Image')
cv2.setMouseCallback('Image', draw_shape)

while True:
    cv2.imshow('Image', image)
    key = cv2.waitKey(1) & 0xFF
    if key == ord('q'):
        break

cv2.destroyAllWindows()

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

读取图像;

初始化透视变换矩阵;

设置键盘事件监听,当按下数字 1 – 9 时增加对应矩阵元素值,按下 Shift + 数字键时减少对应矩阵元素值(不小于 0);

每次矩阵元素值改变后,使用

cv::warpPerspective()

进行透视变换;

显示原始图像和变换后的图像。

57、添加放大或缩小的功能。

在使用 OpenCV 结合 GUI(如 QT)开发时,可通过设置鼠标事件或按钮事件来触发缩放功能。例如,在 QT 界面中添加按钮,为按钮绑定事件处理函数,在函数中使用 OpenCV 的

resize

函数对图像进行缩放,同时更新显示窗口中的图像。

对于鼠标事件,可检测鼠标滚轮操作,根据滚轮滚动方向和幅度对图像进行缩放。

58、添加旋转图像的功能。

可在实现透视变换程序的基础上添加旋转功能。可使用OpenCV中的

cv::getRotationMatrix2D

函数获取旋转矩阵,再使用

cv::warpAffine

函数对图像进行旋转操作。示例代码如下:


#include <opencv2/opencv.hpp>
#include <iostream>

using namespace cv;
using namespace std;

int main() {
    // 读取图像
    Mat image = imread("your_image.jpg");
    if (image.empty()) {
        cout << "Could not open or find the image" << endl;
        return -1;
    }

    // 定义旋转中心、旋转角度和缩放因子
    Point2f center(image.cols / 2.0, image.rows / 2.0);
    double angle = 45.0;  // 旋转角度
    double scale = 1.0;   // 缩放因子

    // 获取旋转矩阵
    Mat rotationMatrix = getRotationMatrix2D(center, angle, scale);

    // 应用旋转矩阵
    Mat rotatedImage;
    warpAffine(image, rotatedImage, rotationMatrix, image.size());

    // 显示原始图像和旋转后的图像
    imshow("Original Image", image);
    imshow("Rotated Image", rotatedImage);
    waitKey(0);

    return 0;
}

上述代码实现了将图像旋转45度的功能,可根据需求修改旋转角度和缩放因子。

59、仅使用HighGui创建一个窗口,在该窗口中可以同时加载并查看四张图像,每张图像的尺寸至少为300×300。你应该能够点击每张图像,并打印出相对于该图像(而非更大的窗口)的正确点击位置 (x, y)。打印内容应以文本形式显示在你点击的图像上。

要实现该功能,可按以下步骤编写代码:

使用HighGui创建一个窗口;

加载四张尺寸至少为300×300的图像到窗口;

为窗口设置鼠标事件回调函数;

在回调函数中,判断点击位置属于哪张图像,并计算相对于该图像的 (x, y) 坐标;

使用绘图函数将坐标以文本形式显示在点击的图像上。

60、使用QT创建一个窗口,可同时加载并查看四张图像。实现框绘制功能,以便能在每个窗口内绘制框,但不允许框超出正在绘制的图像边界。

完成该任务,结合框绘制代码进行实现,同时添加边界检查逻辑以确保框不会超出图像边界。

另外,使用相关命令需要用 CMake 标志:


-D WITH_QT_OPENGL=ON

来构建 OpenCV。

61、使用QT创建一个足以容纳500×500图像的窗口。当为该窗口按下一个按钮时,会出现一个100×100的小窗口,对鼠标所在的第一张图像区域进行放大。应有一个滑块,允许放大倍数为1倍、2倍、3倍和4倍。处理鼠标周围的放大区域超出500×500图像边界的情况,放大窗口中应显示黑色像素。当再次按下按钮时,小窗口消失,放大功能停止工作。该按钮用于切换放大功能的开启和关闭。

可按以下思路实现:

创建一个500×500的QT窗口并加载图像。

添加一个按钮和滑块,按钮用于控制放大窗口的显示与隐藏,滑块用于选择放大倍数(1×、2×、3×、4×)。

监听鼠标位置,当按钮按下时,根据鼠标位置和滑块选择的放大倍数,在100×100的小窗口中显示放大后的图像区域。

检查放大区域是否超出500×500图像边界,若超出则在放大窗口中显示黑色像素。

再次按下按钮时,隐藏小窗口并停止放大功能。

62、使用QT创建一个1000×1000的窗口。当按下一个按钮时,你可以在窗口中点击并输入和编辑文本。不允许文本超出窗口边界。允许输入和退格操作。

可使用 Qt 的

QtWidgets

模块,创建一个 1000×1000 的

QWidget

窗口,在窗口中添加一个

QPushButton

和一个

QTextEdit

。为按钮绑定点击事件,点击后使

QTextEdit

可编辑。通过设置

QTextEdit

的大小和位置确保文本不会超出窗口边界,

QTextEdit

本身支持输入和退格操作。

以下是示例代码:


import sys
from PyQt5.QtWidgets import QApplication, QWidget, QPushButton, QTextEdit

class MainWindow(QWidget):
    def __init__(self):
        super().__init__()
        self.initUI()

    def initUI(self):
        self.setGeometry(100, 100, 1000, 1000)
        self.setWindowTitle('Text Input Window')
        self.button = QPushButton('Enable Text Input', self)
        self.button.setGeometry(50, 50, 150, 30)
        self.button.clicked.connect(self.enable_text_input)
        self.text_edit = QTextEdit(self)
        self.text_edit.setGeometry(50, 100, 900, 800)
        self.text_edit.setReadOnly(True)

    def enable_text_input(self):
        self.text_edit.setReadOnly(False)

if __name__ == '__main__':
    app = QApplication(sys.argv)
    window = MainWindow()
    window.show()
    sys.exit(app.exec_())

此代码使用 Python 和 PyQt5 实现,若使用 C++ 和 Qt,实现思路类似,但语法不同。

© 版权声明

相关文章

暂无评论

none
暂无评论...