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,实现思路类似,但语法不同。