HNUST(王志喜)图形图像实验报告

发布时间:2024年01月17日

实验 1 OpenGL 中的图形变换

1. 线框立方体
【问题描述】
请使用 OpenGL、GLU 和 GLUT 编写一个显示线框立方体的程序。其中立方体的半径为 1.5 单位,并首先绕(0, 0, 0)~(1, 1, 0)旋转 30 度,然后远移 6.5 单位;观察体规定为:视场角=30度,宽高比=1,近=1,远=100;程序窗口的大小为(200, 200),标题为“线框立方体”。
【设计思路】
要实现这个功能,我们可以使用OpenGL、GLU和GLUT库来编写一个显示线框立方体的程序。首先,我们需要初始化窗口和OpenGL环境,设置窗口大小为(200, 200),标题为“线框立方体”。然后,我们需要在初始化函数中设置背景颜色、投影矩阵和模型视图矩阵。在显示函数中,我们可以通过调用OpenGL的函数来绘制一个线框立方体,并对其进行旋转和远移操作。最后,在reshape函数中设置投影矩阵,以确保线框立方体在窗口中正确显示。整个程序的主要逻辑是在初始化、显示和重塑函数中完成的。通过使用OpenGL的函数来设置投影矩阵和模型视图矩阵,我们可以实现对立方体的旋转、平移和投影设置。希望这个设计思路能够帮助你完成这个程序。
【关键程序】

void init() {
    glClearColor(0.0, 0.0, 0.0, 0.0); 
    glMatrixMode(GL_PROJECTION); // 设置投影矩阵
    glLoadIdentity(); // 重置当前矩阵为单位矩阵
    gluPerspective(30.0, 1.0, 1.0, 100.0); // 设置透视投影,视场角=30度,宽高比=1,近=1,远=100
    glMatrixMode(GL_MODELVIEW); // 设置模型视图矩阵
}
void display() {
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // 清空颜色缓冲区和深度缓冲区
    glLoadIdentity(); // 重置当前矩阵为单位矩阵
    glTranslatef(0.0, 0.0, -6.5); // 远移6.5单位
    glRotatef(30.0, 1.0, 1.0, 0.0); // 绕(1, 1, 0)旋转30度
    glutWireCube(3.0); // 绘制线框立方体,半径为1.5
    glFlush(); // 刷新绘图
}
void reshape(int w, int h) {
    glViewport(0, 0, (GLsizei)w, (GLsizei)h); // 设置视口
    glMatrixMode(GL_PROJECTION); // 设置投影矩阵
    glLoadIdentity(); // 重置当前矩阵为单位矩阵
    if (w <= h) {
        glOrtho(-3.0, 3.0, -3.0 * (GLfloat)h / (GLfloat)w, 3.0 * (GLfloat)h / (GLfloat)w, -10.0, 10.0);
    }
    else {
        glOrtho(-3.0 * (GLfloat)w / (GLfloat)h, 3.0 * (GLfloat)w / (GLfloat)h, -3.0, 3.0, -10.0, 10.0);
    }
    glMatrixMode(GL_MODELVIEW); // 设置模型视图矩阵
}

【实验结果】
在这里插入图片描述

2. 线框球
【问题描述】
请使用 OpenGL 和 GLUT 编写一个显示线框球体的简单图形程序。其中球体的半径为 0.8,经线数为 24,纬线数为 12,并绕 轴旋转 30 度,程序窗口的大小为(200, 200),标题“为线框球”。
【设计思路】

  1. 引入 OpenGL 和 GLUT 库。
  2. 设置窗口大小为 (200, 200),标题为 “为线框球”。
  3. 定义球体的半径、经线数和纬线数。
  4. 编写一个函数,用于绘制线框球体。该函数需要接收球体的半径、经线数和纬线数作为参数。
  5. 在主循环中,清除屏幕,调用绘制线框球体的函数,更新旋转角度,然后交换缓冲区并处理事件。
  6. 运行程序,观察显示的线框球体。
    【关键程序】
void init() {
    glClearColor(0.0, 0.0, 0.0, 0.0); // 设置背景颜色为黑色
    glShadeModel(GL_FLAT); // 设置着色模式为平面着色
}

void display() {
    glClear(GL_COLOR_BUFFER_BIT); // 清除颜色缓冲区
    glPushMatrix(); // 保存当前模型视图矩阵
    glRotatef(30.0, 1.0, 0.0, 0.0); // 绕x轴旋转30度
    glColor3f(1.0, 1.0, 1.0); // 设置颜色为白色
    glutWireSphere(0.8, 24, 12); // 绘制线框球体,半径为0.8,经线数为24,纬线数为12
    glPopMatrix(); // 恢复之前保存的模型视图矩阵
    glFlush(); // 刷新绘图
}

void reshape(int w, int h) {
    glViewport(0, 0, (GLsizei)w, (GLsizei)h); // 设置视口
    glMatrixMode(GL_PROJECTION); // 设置投影矩阵
    glLoadIdentity(); // 重置当前矩阵为单位矩阵
    if (w <= h) {
        gluPerspective(60.0, (GLfloat)h / (GLfloat)w, 1.0, 20.0); // 根据宽高比设置透视投影
    }
    else {
        gluPerspective(60.0, (GLfloat)w / (GLfloat)h, 1.0, 20.0); // 根据宽高比设置透视投影
    }
    glMatrixMode(GL_MODELVIEW); // 设置模型视图矩阵
    glLoadIdentity(); // 重置当前矩阵为单位矩阵
    glTranslatef(0.0, 0.0, -5.0); // 平移5个单位到观察点
}

【实验结果】
在这里插入图片描述

  1. 线框椭球
    【问题描述】
    请使用OpenGL和GLUT编写一个显示线框椭球体的简单图形程序。其中椭球体的两极方向为上下方向,左右方向的半径为0.98,上下方向的半径为0.49,前后方向的半径为0.6,经线数为48,纬线数为24,使用正投影,裁剪窗口为(-1, -0.5)~(1, 0.5),程序窗口的大小为(400, 200),标题为“线框椭球”。
    【设计思路】
  2. 引入OpenGL和GLUT库。
  3. 设置窗口大小为(400, 200),标题为“线框椭球”。
  4. 定义椭球体的参数,包括左右方向的半径、上下方向的半径、前后方向的半径以及经线数和纬线数。
  5. 编写一个函数,用于绘制线框椭球体。该函数需要接收椭球体的参数作为参数。
  6. 在主循环中,清除屏幕,调用绘制线框椭球体的函数,然后交换缓冲区并处理事件。
  7. 运行程序,观察显示的线框椭球体。
    【关键程序】
void init() {
    glClearColor(0.0, 0.0, 0.0, 0.0); // 设置背景颜色为黑色
    glShadeModel(GL_FLAT); // 设置着色模式为平面着色
}

void display() {
    glClear(GL_COLOR_BUFFER_BIT); // 清除颜色缓冲区
    glPushMatrix(); // 保存当前模型视图矩阵
    glRotatef(30.0, 1.0, 0.0, 0.0); // 绕x轴旋转30度
    glColor3f(1.0, 1.0, 1.0); // 设置颜色为白色
    glutWireSphere(0.8, 24, 12); // 绘制线框球体,半径为0.8,经线数为24,纬线数为12
    glPopMatrix(); // 恢复之前保存的模型视图矩阵
    glFlush(); // 刷新绘图
}

void reshape(int w, int h) {
    glViewport(0, 0, (GLsizei)w, (GLsizei)h); // 设置视口
    glMatrixMode(GL_PROJECTION); // 设置投影矩阵
    glLoadIdentity(); // 重置当前矩阵为单位矩阵
    if (w <= h) {
        gluPerspective(60.0, (GLfloat)h / (GLfloat)w, 1.0, 20.0); // 根据宽高比设置透视投影
    }
    else {
        gluPerspective(60.0, (GLfloat)w / (GLfloat)h, 1.0, 20.0); // 根据宽高比设置透视投影
    }
    glMatrixMode(GL_MODELVIEW); // 设置模型视图矩阵
    glLoadIdentity(); // 重置当前矩阵为单位矩阵
    glTranslatef(0.0, 0.0, -5.0); // 平移5个单位到观察点
}

【实验结果】
在这里插入图片描述

4. 犹他茶壶
【问题描述】
请使用OpenGL、GLU和GLUT编写一个三维犹他茶壶程序。其中茶壶的半径为1单位,并远移6.5单位;观察体规定为:视场角=30度,宽高比=1,近=1,远=100;程序窗口的大小为(200, 200),标题为“旋转的尤他茶壶”。茶壶绕z方向中轴不断旋转,旋转的时间间隔为25毫秒,角度间隔为2度。注意旋转角度必须限定在0~360度以内。
【设计思路】

  1. 需要导入OpenGL、GLU和GLUT库。
  2. 需要定义一个函数来初始化OpenGL环境,包括设置视场角、宽高比、近裁剪面和远裁剪面等参数。
  3. 需要定义一个函数来绘制犹他茶壶。这个函数需要使用GLU库中的gluNewQuadric()函数来创建一个新的四边形网格,然后使用gluDisk()和gluCylinder()函数来绘制茶壶的底部和侧面。
  4. 需要定义一个函数来处理窗口的大小改变事件。这个函数需要调用glViewport()函数来设置视口大小,然后调用glMatrixMode()和glLoadIdentity()函数来设置模型视图矩阵。
  5. 需要定义一个函数来处理鼠标的移动事件。这个函数需要调用glRotatef()函数来旋转茶壶。
  6. 需要定义一个函数来处理键盘的按键事件。这个函数需要根据按下的键来调整茶壶的旋转角度。
  7. 需要定义一个主函数来初始化GLUT库,设置窗口属性,注册回调函数,然后进入GLUT事件处理循环。
    【关键程序】
static int angle = 0; // 初始旋转角度为0
void display() {
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // 清除颜色缓冲区和深度缓冲区
    glColor3f(1.0, 1.0, 1.0); // 设置颜色为白色
    glLoadIdentity(); // 重置当前的模型视图矩阵
    gluLookAt(0.0, 0.0, 6.5, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0); // 设置观察体
    glRotatef((GLfloat)angle, 0.0, 0.0, 1.0); // 绕z轴旋转茶壶
    glutWireTeapot(1.0); // 绘制茶壶
    glutSwapBuffers(); // 交换前后缓冲区
}
void reshape(int w, int h) {
    glViewport(0, 0, (GLsizei)w, (GLsizei)h); // 设置视口
    glMatrixMode(GL_PROJECTION); // 设置当前矩阵为投影矩阵
    glLoadIdentity(); // 重置当前的投影矩阵
    gluPerspective(30.0, 1.0, 1.0, 100.0); // 设置透视投影参数
    glMatrixMode(GL_MODELVIEW); // 设置当前矩阵为模型视图矩阵
}
void timer(int value) {
    angle = (angle + 2) % 360; // 更新旋转角度
    glutPostRedisplay(); // 标记窗口需要重新绘制
    glutTimerFunc(25, timer, 0); // 25毫秒后再次调用timer函数
}

【实验结果】
在这里插入图片描述
5. 多视口演示
【问题描述】
请使用OpenGL、GLU和GLUT编写一个多视口演示程序。要求:(1)在屏幕窗口左下角的1/4部分显示一个红色的填充矩形,该矩形的一对对角顶点是(0, 0)和(1, 1);(2)在屏幕窗口右下角的1/4部分显示一个绿色的填充犹他茶壶,茶壶半径为0.4,并向右向上各移0.5;(3)在屏幕窗口上部居中的1/4部分显示一个蓝色的填充正三角形,该正三角形的左下角顶点是(0, 0),右下角顶点是(1, 0);(4)裁剪窗口均为(-0.1, -0.1)~(1.1, 1.1),程序窗口的大小为(200, 200),背景为黑色,标题为“多视口演示”。
【设计思路】

  1. 首先,我们需要导入OpenGL、GLU和GLUT库。
  2. 然后,我们需要创建一个窗口,并设置其大小为200x200像素,标题为“多视口演示”。
  3. 接下来,我们需要设置裁剪窗口的范围,即(-0.1, -0.1)~(1.1, 1.1)。
  4. 然后,我们需要设置背景颜色为黑色。
  5. 接下来,我们需要在屏幕窗口左下角的1/4部分显示一个红色的填充矩形,该矩形的一对对角顶点是(0, 0)和(1, 1)。
  6. 接着,我们需要在屏幕窗口右下角的1/4部分显示一个绿色的填充犹他茶壶,茶壶半径为0.4,并向右向上各移0.5。
  7. 然后,我们需要在屏幕窗口上部居中的1/4部分显示一个蓝色的填充正三角形,该正三角形的左下角顶点是(0, 0),右下角顶点是(1, 0)。
  8. 最后,我们需要进入GLUT事件处理循环,等待用户的交互操作。
    【关键程序】
void display() {
    glClear(GL_COLOR_BUFFER_BIT); // 清除颜色缓冲区
    // 左下角的红色填充矩形
    glViewport(0, 0, 100, 100);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluOrtho2D(0.0, 1.0, 0.0, 1.0);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    glColor3f(1.0, 0.0, 0.0);
    glBegin(GL_POLYGON);
    glVertex2f(0.0, 0.0);
    glVertex2f(1.0, 0.0);
    glVertex2f(1.0, 1.0);
    glVertex2f(0.0, 1.0);
    glEnd();
    // 右下角的绿色填充犹他茶壶
    glViewport(100, 0, 100, 100);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluOrtho2D(-0.1, 1.1, -0.1, 1.1);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    glTranslatef(0.5, 0.5, 0.0);
    glColor3f(0.0, 1.0, 0.0);
    glutSolidTeapot(0.4);
    // 上部居中的蓝色填充正三角形
    glViewport(50, 100, 100, 100);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluOrtho2D(0.0, 1.0, 0.0, 1.0);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    glColor3f(0.0, 0.0, 1.0);
    glBegin(GL_POLYGON);
    glVertex2f(0.5, 1.0);
    glVertex2f(1.0, 0.0);
    glVertex2f(0.0, 0.0);
    glEnd();
    glutSwapBuffers(); // 交换前后缓冲区
}

【实验结果】

在这里插入图片描述

实验 2 OpenGL 的真实感图形

1. 纯白球面在烈日暴晒下的效果
【问题描述】
使用OpenGL和GLUT编写一个程序,用于模拟一个非常光滑的纯白球面在烈日暴晒下的效果。
【设计思路】

  1. 首先,我们需要创建一个窗口,并设置其大小和标题。
  2. 然后,我们需要初始化OpenGL和GLUT库。
  3. 接下来,我们需要定义一个球体的参数,包括半径、位置等。
  4. 然后,我们需要编写一个渲染函数,用于绘制球体。在这个函数中,我们需要使用OpenGL的函数来绘制一个球体,并设置其颜色为白色。
  5. 最后,我们需要在主循环中调用渲染函数,以便不断地更新球体的位置和颜色。
    【关键程序】
void init() {
    glClearColor(0.0, 0.0, 0.0, 0.0); // 设置背景颜色为黑色
    glEnable(GL_DEPTH_TEST); // 启用深度测试
    glEnable(GL_LIGHTING); // 启用光照
    glEnable(GL_LIGHT0); // 启用0号光源
    glEnable(GL_COLOR_MATERIAL); // 启用材质颜色跟踪
}

void display() {
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // 清除颜色缓冲区和深度缓冲区

    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();

    GLfloat light_position[] = { 1.0, 1.0, 1.0, 0.0 }; // 光源位置
    glLightfv(GL_LIGHT0, GL_POSITION, light_position); // 设置光源位置

    glPushMatrix();
    glColor3f(1.0, 1.0, 1.0); // 设置颜色为纯白
    glutSolidSphere(1.0, 50, 50); // 绘制光滑的纯白球体
    glPopMatrix();

    glutSwapBuffers(); // 交换前后缓冲区
}

【实验结果】
在这里插入图片描述

2. 模拟光照照射紫色球体
【问题描述】
已知在一个空旷的场景中有一个粗糙的紫色球体,球体的右上角方向放置了一个白色的点光源,请使用OpenGL和GLUT编写一个程序模拟出球面上的光照效果(不考虑环境光)。【设计思路】

  1. 我们需要创建一个窗口并初始化OpenGL和GLUT。
  2. 我们需要定义球体和点光源的位置、颜色和光照参数。
  3. 我们需要编写一个渲染函数来绘制球体和点光源,并计算光照效果。
  4. 在渲染函数中,我们首先清除颜色缓冲区和深度缓冲区。
  5. 我们启用光照功能,并设置点光源的位置、颜色和强度。
  6. 我们设置材质属性,包括漫反射颜色、镜面反射颜色、镜面反射指数和环境光强度。
  7. 我们使用glBegin(GL_TRIANGLES)和glEnd()函数绘制球体的三角形网格。
  8. 我们在主循环中调用渲染函数并处理用户输入。
    【关键程序】
void init() {
    glClearColor(0.0, 0.0, 0.0, 0.0); // 设置背景颜色为黑色
    glEnable(GL_DEPTH_TEST); // 启用深度测试
    glEnable(GL_LIGHTING); // 启用光照
    glEnable(GL_LIGHT0); // 启用0号光源
}
void display() {
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // 清除颜色缓冲区和深度缓冲区
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    // 设置光源位置
    GLfloat light_position[] = { 1.0, 1.0, 1.0, 0.0 }; // 光源位置
    glLightfv(GL_LIGHT0, GL_POSITION, light_position);
    // 设置材质属性
    GLfloat mat_ambient[] = { 0.5, 0.0, 0.5, 1.0 }; // 紫色材质的环境光分量
    GLfloat mat_diffuse[] = { 0.5, 0.0, 0.5, 1.0 }; // 紫色材质的漫反射分量
    GLfloat mat_specular[] = { 1.0, 1.0, 1.0, 1.0 }; // 高光反射颜色为白色
    GLfloat mat_shininess[] = { 50.0 }; // 高光反射系数
    glMaterialfv(GL_FRONT, GL_AMBIENT, mat_ambient);
    glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse);
    glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular);
    glMaterialfv(GL_FRONT, GL_SHININESS, mat_shininess);
    glPushMatrix();
    glutSolidSphere(1.0, 50, 50); // 绘制紫色的球体
    glPopMatrix();
    glutSwapBuffers(); // 交换前后缓冲区
}

【实验结果】
在这里插入图片描述

实验 3 OpenCV核心功能

1. 实现BMP文件格式的读取功能
【问题描述】
请根据BMP文件的格式编写一个C++函数vector loadBmp24(const string &path)。该函数用于从一个真彩色BMP文件中读取图像数据,保存在uchar数组中(可以忽略非真彩色BMP文件)。
【设计思路】

  1. 打开BMP文件,读取文件头信息,判断是否为真彩色BMP文件(24位颜色深度)。
  2. 如果是真彩色BMP文件,继续读取图像数据。
  3. 将图像数据保存到uchar数组中。
  4. 关闭文件并返回uchar数组。
    【关键程序】
#include <iostream>
#include <fstream>
#include <vector>
#include <cstdint>
#include <string>

using namespace std;

#pragma pack(push, 1)
struct BMPHeader {
    uint16_t type;
    uint32_t size;
    uint16_t reserved1;
    uint16_t reserved2;
    uint32_t offset;
    uint32_t header_size;
    int32_t width;
    int32_t height;
    uint16_t planes;
    uint16_t bit_count;
    uint32_t compression;
    uint32_t image_size;
    int32_t x_pixels_per_meter;
    int32_t y_pixels_per_meter;
    uint32_t colors_used;
    uint32_t colors_important;
};
#pragma pack(pop)

vector<uchar> loadBmp24(const string &path) {
    vector<uchar> image_data;

    ifstream file(path, ios::in | ios::binary);
    if (!file) {
        cerr << "Error: Unable to open file " << path << endl;
        return image_data;
    }

    BMPHeader header;
    file.read(reinterpret_cast<char*>(&header), sizeof(header));

    if (header.type != 0x4D42) {
        cerr << "Error: File is not a BMP image" << endl;
        return image_data;
    }

    if (header.bit_count != 24 || header.compression != 0) {
        cerr << "Error: Not a 24-bit uncompressed BMP image" << endl;
        return image_data;
    }

    file.seekg(header.offset, ios::beg);

    int padding = (4 - ((header.width * 3) % 4)) % 4; // 计算每行的填充字节数

    for (int y = 0; y < header.height; y++) {
        for (int x = 0; x < header.width; x++) {
            uint8_t pixel[3];
            file.read(reinterpret_cast<char*>(pixel), 3);
            image_data.push_back(pixel[2]); // 保存B分量
            image_data.push_back(pixel[1]); // 保存G分量
            image_data.push_back(pixel[0]); // 保存R分量
        }
        file.seekg(padding, ios::cur); // 跳过填充字节
    }

    file.close();

    return image_data;
}

【实验结果】
这个函数首先打开BMP文件并读取文件头信息。接着,它检查文件头中的信息以确保文件是24位真彩色格式且没有压缩。然后,它跳过文件头和调色板数据,直接读取图像数据,并按照BGR顺序保存到vector数组中。最后返回图像数据数组。

2. 使用OpenCV显示指定矩形区域的图像
【问题描述】
使用OpenCV装入一幅大小至少为512×512的真彩色图像,并显示该图像。然后在源图像中指定一个矩形区域(左上顶点和宽高值分别为(128, 256)和(256, 128)的矩形),并在结果图像窗口中显示源图像中被选取的部分。
【设计思路】

  1. 首先,需要导入cv2模块,这是OpenCV的Python接口。
  2. 然后,需要使用cv2.imread()函数来读取一幅真彩色图像,参数为图像的文件名或路径。这个函数会返回一个三维的numpy数组,表示图像的像素值。
  3. 接着,需要使用cv2.imshow()函数来显示图像,参数为窗口的名称和图像的数组。这个函数会在一个新的窗口中显示图像,您可以按任意键关闭窗口。
  4. 然后,需要使用numpy的切片操作来从图像中选取一个矩形区域,参数为行和列的范围。
  5. 最后,需要再次使用cv2.imshow()函数来显示选取的区域,参数为另一个窗口的名称和区域的数组。这个函数会在另一个新的窗口中显示区域,您可以按任意键关闭窗口。
    【关键程序】
int main()
{
    Mat img = imread("D:/xm.jpg", IMREAD_COLOR);
    if (img.empty())
    {
        std::cout << "Could not read the image: " << std::endl;
        return 1;
    }

    namedWindow("Original Image", WINDOW_NORMAL);
    imshow("Original Image", img);

    Rect roi(128, 256, 256, 128);
    Mat cropped = img(roi);

    namedWindow("Cropped Image", WINDOW_NORMAL);
    imshow("Cropped Image", cropped);

    waitKey(0);
    return 0;
}

【实验结果】
在这里插入图片描述在这里插入图片描述

3. 使用OpenCV分离彩色图像的三个通道并显示灰度图像
【问题描述】
请使用OpenCV编写一个简单的程序,该程序首先读入一幅真彩色图像,然后将这幅彩色图像的3个通道分离出来,得到3幅灰度图像,最后显示这3幅灰度图像。
【设计思路】

  1. 首先,我们需要导入cv2库,这是一个用于处理图像和视频的开源库。
  2. 然后,我们使用cv2.imread()函数读取一幅彩色图像。
  3. 接下来,我们使用cv2.split()函数将彩色图像的3个通道分离出来。这个函数会返回一个包含3个元素的列表,每个元素都是一个单通道图像。
  4. 然后,我们使用cv2.cvtColor()函数将每个单通道图像转换为灰度图像。
  5. 最后,我们使用cv2.imshow()函数显示这3幅灰度图像。
    【关键程序】
int main()
{
    Mat img = imread("D:/xm.jpg", IMREAD_COLOR);
    if (img.empty())
    {
        std::cout << "Could not read the image: " << std::endl;
        return 1;
    }
    namedWindow("Original Image", WINDOW_NORMAL);
    imshow("Original Image", img);
    Mat channels[3];
    split(img, channels);
    namedWindow("Blue Channel", WINDOW_NORMAL);
    imshow("Blue Channel", channels[0]);
    namedWindow("Green Channel", WINDOW_NORMAL);
    imshow("Green Channel", channels[1]);
    namedWindow("Red Channel", WINDOW_NORMAL);
    imshow("Red Channel", channels[2]);
    waitKey(0);
    return 0;
}

【实验结果】
在这里插入图片描述在这里插入图片描述
在这里插入图片描述在这里插入图片描述

4. 使用OpenCV处理灰度图像
【问题描述】
首先使用OpenCV装入一幅灰度图像并显示该图像,然后计算出该图像的最小像素值min和最大像素值max,最后将每个像素都减去min再乘以255/max以后显示结果图像。
【设计思路】

  1. 首先,使用OpenCV的imread函数加载一幅灰度图像,并使用imshow函数显示该图像。
  2. 接着,使用cv::minMaxLoc函数计算图像的最小像素值min和最大像素值max。
  3. 然后,创建一个新的Mat对象,将原始图像减去min,然后乘以255再除以max,得到处理后的图像。
  4. 最后,使用imshow函数显示处理后的结果图像。
    【关键程序】
int main()
{
    Mat img = imread("D:/xm.jpg", IMREAD_GRAYSCALE);
    if (img.empty())
    {
        std::cout << "Could not read the image: " << std::endl;
        return 1;
    }
    namedWindow("Original Image", WINDOW_NORMAL);
    imshow("Original Image", img);
    double minVal, maxVal;
    minMaxLoc(img, &minVal, &maxVal);
    Mat result;
    img.convertTo(result, CV_8U, 255.0 / (maxVal - minVal), -255.0 * minVal / (maxVal - minVal));
    namedWindow("Result Image", WINDOW_NORMAL);
    imshow("Result Image", result);
    waitKey(0);
    return 0;
}

【实验结果】
在这里插入图片描述
在这里插入图片描述

5. 随机生成并处理浮点数灰度图像
【问题描述】
随机生成一幅浮点数灰度图像(大小和亮度都是随机的,大小值位于区间[128, 639]),然后将该图像变换成亮度是0~1的浮点数图像,最后变换成字节图像并显示该图像。
【设计思路】

  1. 首先,我们需要生成一个随机大小的浮点数灰度图像。我们可以使用numpy库中的random模块来生成随机数,然后根据随机大小创建一个二维数组作为图像。
  2. 接下来,我们需要将生成的浮点数灰度图像变换成亮度是0~1的浮点数图像。我们可以通过将每个像素值除以255来实现这一点。
  3. 然后,我们需要将亮度是0~1的浮点数图像变换成字节图像。我们可以使用numpy库中的astype函数将浮点数数组转换成无符号8位整型数组。
  4. 最后,我们需要显示生成的字节图像。我们可以使用matplotlib库中的imshow函数来实现这一点。
    【关键程序】
int main() {
    // 生成随机大小和亮度的浮点数灰度图像
    cv::RNG rng;
    int width = rng.uniform(128, 640);
    int height = rng.uniform(128, 640);
    cv::Mat floatImage = cv::Mat::zeros(height, width, CV_32FC1);
    rng.fill(floatImage, cv::RNG::UNIFORM, 0.0, 1.0);
    // 显示浮点数灰度图像
    cv::namedWindow("Float Image", cv::WINDOW_NORMAL);
    cv::imshow("Float Image", floatImage);
    cv::waitKey(0);
    // 将浮点数图像转换为字节图像
    cv::Mat byteImage;
    floatImage.convertTo(byteImage, CV_8U, 255.0);
    // 显示转换后的字节图像
    cv::namedWindow("Byte Image", cv::WINDOW_NORMAL);
    cv::imshow("Byte Image", byteImage);
    cv::waitKey(0);
    return 0;
}

【实验结果】
在这里插入图片描述

实验 4 图像变换

1. 使用OpenCV进行图像缩放变换
【问题描述】
使用OpenCV编写一个演示对原图像进行缩放变换的程序。该程序首先装入一幅真彩色图像并显示该图像,然后对该图像进 行缩放变换,显示得到的结果。其中旋转中心位于图像中心,缩放系数为(0.707, 0.707),旋转角度为45度。
【设计思路】

  1. 导入所需库,包括cv2和numpy。
  2. 读取图像文件,并将其转换为灰度图像。
  3. 计算图像的中心点坐标。
  4. 定义缩放系数和旋转角度。
  5. 使用cv2.getRotationMatrix2D()函数获取旋转矩阵。
  6. 使用cv2.warpAffine()函数对图像进行缩放和旋转变换。
  7. 显示原始图像和变换后的图像。
    【关键程序】
int main()
{
    Mat img = imread("D:/tom.jpg", IMREAD_COLOR);
    if (img.empty())
    {
        std::cout << "Could not read the image: " << std::endl;
        return 1;
    }
    namedWindow("Original Image", WINDOW_NORMAL);
    imshow("Original Image", img);
    Point2f center(img.cols / 2.0, img.rows / 2.0);
    double angle = 45.0;
    double scale = 0.707;
    Mat rot_mat = getRotationMatrix2D(center, angle, scale);
    Mat result;
    warpAffine(img, result, rot_mat, img.size());
    namedWindow("Result Image", WINDOW_NORMAL);
    imshow("Result Image", result);
    waitKey(0);
    return 0;
}

【实验结果】
在这里插入图片描述在这里插入图片描述

2. 使用OpenCV进行傅立叶变换和逆变换的演示
【问题描述】
使用OpenCV编写一个演示对原图像进行缩放变换的程序。该程序首先装入一幅真彩色图像并显示该图像,然后对该图像进 行缩放变换,显示得到的结果。其中旋转中心位于图像中心,缩放系数为(0.707, 0.707),旋转角度为45度。
【设计思路】

  1. 导入所需的库,包括cv2和numpy。
  2. 使用cv2.imread()函数读取灰度图像。
  3. 使用cv2.imshow()函数显示原始图像。
  4. 使用numpy的fft模块对图像进行傅立叶变换。
  5. 使用numpy的ifft模块对傅立叶变换的结果进行逆变换。
  6. 使用cv2.imshow()函数显示傅立叶逆变换的结果。
  7. 使用cv2.waitKey()函数等待用户按键,然后关闭所有窗口。
    【关键程序】
int main() {
    // 装入一幅灰度图像
    cv::Mat image = cv::imread("D:/tom.jpg", cv::IMREAD_GRAYSCALE);
    // 显示原图像
    cv::namedWindow("Original Image", cv::WINDOW_NORMAL);
    cv::imshow("Original Image", image);
    cv::waitKey(0);
    // 确保图像的尺寸是2的幂次方
    int m = cv::getOptimalDFTSize(image.rows);
    int n = cv::getOptimalDFTSize(image.cols);
    cv::Mat padded;
    cv::copyMakeBorder(image, padded, 0, m - image.rows, 0, n - image.cols, cv::BORDER_CONSTANT, cv::Scalar::all(0));
    // 确保图像的数据类型是浮点型
    padded.convertTo(padded, CV_32F);
    // 进行傅立叶正变换
    cv::Mat fourierTransform;
    cv::dft(padded, fourierTransform, cv::DFT_COMPLEX_OUTPUT);
    // 分离实部和虚部
    cv::Mat planes[2];
    cv::split(fourierTransform, planes);
    cv::magnitude(planes[0], planes[1], planes[0]);
    cv::Mat magnitudeImage = planes[0];
    // 对数变换
    magnitudeImage += cv::Scalar::all(1);
    cv::log(magnitudeImage, magnitudeImage);
    // 归一化
    cv::normalize(magnitudeImage, magnitudeImage, 0, 1, cv::NORM_MINMAX);
    // 显示傅立叶变换结果
    cv::namedWindow("Fourier Transform", cv::WINDOW_NORMAL);
    cv::imshow("Fourier Transform", magnitudeImage);
    cv::waitKey(0);
    // 进行傅立叶逆变换
    cv::Mat inverseTransform;
    cv::dft(fourierTransform, inverseTransform, cv::DFT_INVERSE | cv::DFT_REAL_OUTPUT);
    // 显示傅立叶逆变换结果
    cv::namedWindow("Inverse Transform", cv::WINDOW_NORMAL);
    cv::imshow("Inverse Transform", inverseTransform);
    cv::waitKey(0);
    return 0;
}

【实验结果】
在这里插入图片描述在这里插入图片描述
在这里插入图片描述

3. OpenCV中的FFTShift函数实现与应用
【问题描述】
请为OpenCV提供一个中心化函数FFTShift,该函数的函数原型为“Mat FFTShift(Mat X);”。
【设计思路】

  1. 首先,我们需要了解FFTShift函数的作用。FFTShift函数用于将图像的零频率分量移动到频谱的中心,这样可以更好地进行傅里叶变换和逆傅里叶变换。
  2. 其次,我们需要确定输入参数X的类型和大小。根据OpenCV的文档,FFTShift函数接受一个Mat类型的参数X,表示需要进行中心化处理的图像。
  3. 然后,我们需要计算图像的中心坐标。这可以通过获取图像的高度和宽度,然后除以2来实现。
  4. 接下来,我们需要创建一个新的Mat对象,用于存储中心化后的图像。新图像的大小应该与原图像相同。
  5. 然后,我们需要遍历原图像的每一个像素,将其值复制到新图像的对应位置。在复制过程中,需要将原图像的像素值减去中心坐标的值,然后将结果加上新图像的中心坐标的值。这样,就可以将原图像的零频率分量移动到新图像的中心。
  6. 最后,我们需要返回中心化后的图像。
    【关键程序】
Mat FFTShift(Mat X)
{
    Mat Y;
    int cx = X.cols / 2;
    int cy = X.rows / 2;

    Mat q1(X, Rect(0, 0, cx, cy));
    Mat q2(X, Rect(cx, 0, cx, cy));
    Mat q3(X, Rect(0, cy, cx, cy));
    Mat q4(X, Rect(cx, cy, cx, cy));

    q1.copyTo(Y(Rect(cx, cy, cx, cy)));
    q2.copyTo(Y(Rect(0, cy, cx, cy)));
    q3.copyTo(Y(Rect(cx, 0, cx, cy)));
    q4.copyTo(Y(Rect(0, 0, cx, cy)));

    return Y;
}

【实验结果】
在OpenCV中,我们可以通过以下步骤实现FFTShift函数:

  1. 首先,我们需要计算图像的中心坐标。这可以通过获取图像的高度和宽度,然后除以2来实现。
  2. 然后,我们需要创建一个新的Mat对象,用于存储中心化后的图像。新图像的大小应该与原图像相同。
  3. 接下来,我们需要遍历原图像的每一个像素,将其值复制到新图像的对应位置。在复制过程中,需要将原图像的像素值减去中心坐标的值,然后将结果加上新图像的中心坐标的值。这样,就可以将原图像的零频率分量移动到新图像的中心。
  4. 最后,我们需要返回中心化后的图像。
    通过以上步骤,我们可以成功地实现FFTShift函数,使得图像的零频率分量位于频谱的中心。

实验 5 图像增强

1. 使用OpenCV进行中值模糊处理
【问题描述】
使用OpenCV编写一个程序,该程序对一幅彩色图像进行一次中值模糊,要求分别显示源图像和模糊化以后的图像。其中内核大小为5×5。
【设计思路】

  1. 导入所需库,包括cv2和numpy。
  2. 读取图像文件,将其转换为灰度图像。
  3. 使用cv2.medianBlur()函数对图像进行中值模糊处理,设置核大小为5×5。
  4. 显示原始图像和模糊化后的图像。
  5. 保存模糊化后的图像。
    【关键程序】
int main()
{
    Mat img = imread("D:/tom.jpg", IMREAD_COLOR);
    if (img.empty())
    {
        std::cout << "Could not read the image: " << std::endl;
        return 1;
    }
    namedWindow("Original Image", WINDOW_NORMAL);
    imshow("Original Image", img);
    Mat result;
    medianBlur(img, result, 5);
    namedWindow("Result Image", WINDOW_NORMAL);
    imshow("Result Image", result);
    waitKey(0);
    return 0;
}

【实验结果】
在这里插入图片描述
在这里插入图片描述

2. 使用OpenCV进行Sobel锐化处理
【问题描述】
使用OpenCV编写一个程序,该程序对一幅灰度图像进行Sobel锐化,要求显示锐化以后的图像。其中内核大小为3×3,x和y方向均使用1阶差分。
【设计思路】

  1. 导入所需的库,包括cv2和numpy。
  2. 读取灰度图像。
  3. 使用cv2.Sobel()函数对图像进行Sobel锐化处理,设置内核大小为3×3,x和y方向均使用1阶差分。
  4. 显示原始图像和锐化后的图像。
  5. 保存锐化后的图像。
    【关键程序】
int main()
{
    Mat img = imread("D:/tom.jpg", IMREAD_GRAYSCALE);
    if (img.empty())
    {
        std::cout << "Could not read the image: " << std::endl;
        return 1;
    }
    namedWindow("Original Image", WINDOW_NORMAL);
    imshow("Original Image", img);

    Mat grad_x, grad_y;
    Mat abs_grad_x, abs_grad_y;
    Sobel(img, grad_x, CV_16S, 1, 0, 3, 1, 0, BORDER_DEFAULT);
    Sobel(img, grad_y, CV_16S, 0, 1, 3, 1, 0, BORDER_DEFAULT);
    convertScaleAbs(grad_x, abs_grad_x);
    convertScaleAbs(grad_y, abs_grad_y);
    Mat result;
    addWeighted(abs_grad_x, 0.5, abs_grad_y, 0.5, 0, result);
    namedWindow("Result Image", WINDOW_NORMAL);
    imshow("Result Image", result);

    waitKey(0);
    return 0;
}

【实验结果】
在这里插入图片描述在这里插入图片描述

3. 使用OpenCV进行Laplace锐化处理
【问题描述】
使用OpenCV编写一个程序,该程序对一幅灰度图像进行Laplace锐化,要求显示锐化以后的图像。其中内核大小为3×3。
【设计思路】

  1. 导入所需的库,包括cv2和numpy。

  2. 读取灰度图像。

  3. 使用cv2.Laplacian()函数对图像进行Laplace锐化处理,设置内核大小为3×3。

  4. 显示原始图像和锐化后的图像。

  5. 保存锐化后的图像。
    【关键程序】
    int main()
    {
    Mat img = imread(“D:/tom.jpg”, IMREAD_GRAYSCALE);
    if (img.empty())
    {
    std::cout << "Could not read the image: " << std::endl;
    return 1;
    }

    namedWindow(“Original Image”, WINDOW_NORMAL);
    imshow(“Original Image”, img);

    Mat result;
    Laplacian(img, result, CV_16S, 3, 1, 0, BORDER_DEFAULT);
    convertScaleAbs(result, result);

    namedWindow(“Result Image”, WINDOW_NORMAL);
    imshow(“Result Image”, result);

    waitKey(0);
    return 0;
    }
    【实验结果】
    在这里插入图片描述

在这里插入图片描述

4. 使用OpenCV进行2次腐蚀操作
【问题描述】
使用OpenCV编写一个程序,该程序使用大小为3的正方形模板(锚点位于模板中心)对源图像进行2次腐蚀操作,要求显示源图像和腐蚀以后的图像。
【设计思路】

  1. 导入所需的库,包括cv2和numpy。
  2. 读取源图像。
  3. 创建一个大小为3的正方形模板,锚点位于模板中心。
  4. 使用cv2.erode()函数对源图像进行腐蚀操作,设置迭代次数为2。
  5. 显示原始图像和腐蚀后的图像。
  6. 保存腐蚀后的图像。
    【关键程序】
int main()
{
    Mat img = imread("D:/tom.jpg", IMREAD_GRAYSCALE);
    if (img.empty())
    {
        std::cout << "Could not read the image: " << std::endl;
        return 1;
    }

    namedWindow("Original Image", WINDOW_NORMAL);
    imshow("Original Image", img);

    Mat element = getStructuringElement(MORPH_RECT, Size(3, 3), Point(1, 1));
    Mat result;
    erode(img, result, element, Point(-1, -1), 2);

    namedWindow("Result Image", WINDOW_NORMAL);
    imshow("Result Image", result);

    waitKey(0);
    return 0;
}

【实验结果】
在这里插入图片描述在这里插入图片描述

5. 使用OpenCV进行2阶指数低通滤波
【问题描述】
使用OpenCV编写一个程序,该程序对一幅灰度图像进行一次2阶指数低通滤波,其中截止频率为20,要求分别显示源图像和滤波以后的图像。
【设计思路】

  1. 导入所需的库,包括cv2和numpy。
  2. 读取灰度图像。
  3. 使用cv2.getGaussianKernel()函数生成一个高斯核,用于构建低通滤波器。
  4. 使用cv2.filter2D()函数对源图像进行滤波操作,其中滤波器为高斯核,卷积核大小为截止频率的两倍加1,sigma值为0。
  5. 显示原始图像和滤波后的图像。
  6. 保存滤波后的图像。
    【关键程序】
int main()
{
    Mat img = imread("D:/tom.jpg", IMREAD_GRAYSCALE);
    if (img.empty())
    {
        std::cout << "Could not read the image: " << std::endl;
        return 1;
    }
    namedWindow("Original Image", WINDOW_NORMAL);
    imshow("Original Image", img);
    Mat result;
    double d0 = 20;
    int n = 2;
    int rows = img.rows;
    int cols = img.cols;
    Mat_<float> H(rows, cols);
    for (int i = 0; i < rows; i++)
    {
        for (int j = 0; j < cols; j++)
        {
            float d = sqrt(pow(i - rows / 2, 2) + pow(j - cols / 2, 2));
            H(i, j) = 1 / (1 + pow(d / d0, 2 * n));
        }
    }
    Mat planes[] = { Mat_<float>(img), Mat::zeros(img.size(), CV_32F) };
    Mat complex_img;
    merge(planes, 2, complex_img);
    dft(complex_img, complex_img);
    split(complex_img, planes);
    multiply(planes[0], H, planes[0]);
    multiply(planes[1], H, planes[1]);
    merge(planes, 2, complex_img);
    idft(complex_img, complex_img);
    split(complex_img, planes);
    magnitude(planes[0], planes[1], result);
    normalize(result, result, 0, 255, NORM_MINMAX);
    result.convertTo(result, CV_8U);
    namedWindow("Result Image", WINDOW_NORMAL);
    imshow("Result Image", result);
    waitKey(0);
    return 0;
}

【实验结果】
在这里插入图片描述在这里插入图片描述

6. 使用OpenCV进行2阶指数高通滤波
【问题描述】
使用OpenCV编写一个程序,该程序对一幅灰度图像进行一次2阶指数高通滤波,其中截止频率为45,要求分别显示源图像和滤波以后的图像。
【设计思路】

  1. 导入所需的库,包括cv2和numpy。
  2. 读取灰度图像。
  3. 使用cv2.getGaussianKernel()函数生成一个高斯核,用于构建低通滤波器。
  4. 使用cv2.filter2D()函数对源图像进行滤波操作,其中滤波器为高斯核,卷积核大小为截止频率的两倍加1,sigma值为0。
  5. 显示原始图像和滤波后的图像。
  6. 保存滤波后的图像。
    【关键程序】
int main()
{
    Mat img = imread("D:/tom.jpg", IMREAD_GRAYSCALE);
    if (img.empty())
    {
        std::cout << "Could not read the image: " << std::endl;
        return 1;
    }

    namedWindow("Original Image", WINDOW_NORMAL);
    imshow("Original Image", img);
    Mat result;
    double d0 = 45;
    int n = 2;
    int rows = img.rows;
    int cols = img.cols;
    Mat_<float> H(rows, cols);
    for (int i = 0; i < rows; i++)
    {
        for (int j = 0; j < cols; j++)
        {
            float d = sqrt(pow(i - rows / 2, 2) + pow(j - cols / 2, 2));
            H(i, j) = 1 / (1 + pow(d0 / d, 2 * n));
        }
    }
    Mat planes[] = { Mat_<float>(img), Mat::zeros(img.size(), CV_32F) };
    Mat complex_img;
    merge(planes, 2, complex_img);
    dft(complex_img, complex_img);
    split(complex_img, planes);
    multiply(planes[0], H, planes[0]);
    multiply(planes[1], H, planes[1]);
    merge(planes, 2, complex_img);
    idft(complex_img, complex_img);
    split(complex_img, planes);
    magnitude(planes[0], planes[1], result);
    normalize(result, result, 0, 255, NORM_MINMAX);
    result.convertTo(result, CV_8U);
    namedWindow("Result Image", WINDOW_NORMAL);
    imshow("Result Image", result);
    waitKey(0);
    return 0;
}

【实验结果】
在这里插入图片描述在这里插入图片描述

实验 6 图像解析

1. 使用OpenCV进行灰度图像二值化变换
【问题描述】
使用OpenCV编写一个程序,该程序对一幅灰度图像进行二值化变换,要求分别显示源图像和二值化以后的图像。其中二值化阈值为127,高亮度改为255。
【设计思路】

  1. 导入所需的库,包括cv2和numpy。
  2. 读取灰度图像。
  3. 使用cv2.threshold()函数对图像进行二值化处理,设置阈值为127,高亮度改为255。
  4. 显示原始图像和二值化后的图像。
  5. 保存二值化后的图像。
    【关键程序】
int main()
{
    Mat img = imread("D:/tom.jpg", IMREAD_GRAYSCALE);
    if (img.empty())
    {
        std::cout << "Could not read the image: " << std::endl;
        return 1;
    }

    namedWindow("Original Image", WINDOW_NORMAL);
    imshow("Original Image", img);

    Mat result;
    threshold(img, result, 127, 255, THRESH_BINARY);
    namedWindow("Result Image", WINDOW_NORMAL);
    imshow("Result Image", result);

    waitKey(0);
    return 0;
}

【实验结果】
在这里插入图片描述
在这里插入图片描述

2. 使用OpenCV进行灰度图像Canny边缘检测
【问题描述】
使用OpenCV编写一个程序,该程序对一幅灰度图像进行Canny边缘检测,要求分别显示源图像和检测到的边缘。其中小阈值为50,大阈值为150,内核大小为3。
【设计思路】

  1. 导入所需的库,包括cv2和numpy。
  2. 读取灰度图像。
  3. 使用cv2.Canny()函数对图像进行Canny边缘检测,设置小阈值为50,大阈值为150,内核大小为3。
  4. 显示原始图像和检测到的边缘。
  5. 保存检测到的边缘图像。
    【关键程序】
int main()
{
    Mat img = imread("D:/tom.jpg", IMREAD_GRAYSCALE);
    if (img.empty())
    {
        std::cout << "Could not read the image: " << std::endl;
        return 1;
    }
    namedWindow("Original Image", WINDOW_NORMAL);
    imshow("Original Image", img);
    Mat edges;
    Canny(img, edges, 50, 150, 3);
    namedWindow("Edges", WINDOW_NORMAL);
    imshow("Edges", edges);
    waitKey(0);
    return 0;
}

【实验结果】
在这里插入图片描述
在这里插入图片描述

  1. 使用OpenCV进行彩色图像边缘检测并复制边缘像素
    【问题描述】
    使用OpenCV编写一个程序,该程序首先使用Canny算法检测边缘,然后从源图像中复制出边缘像素。注意,源图像是彩色图像,边缘检测时需转换成灰度图像,结果图像也是彩色图像。
    【设计思路】
  2. 导入所需的库,包括cv2和numpy。
  3. 读取彩色图像。
  4. 将彩色图像转换为灰度图像。
  5. 使用Canny算法检测边缘。
  6. 创建一个与源图像大小相同的空白图像。
  7. 遍历源图像的每个像素,如果该像素是边缘像素(即Canny算法检测到的边缘),则在空白图像中复制该像素。
  8. 显示原始图像、灰度图像和结果图像。
  9. 保存结果图像。
    【关键程序】
int main(int argc, char** argv)
{
    // 读取彩色图像
    Mat srcImage = imread("D:/tom.jpg");
    if (srcImage.empty())
    {
        cout << "Could not open or find the image!\n" << endl;
        return -1;
    }

    // 转换为灰度图像
    Mat grayImage;
    cvtColor(srcImage, grayImage, COLOR_BGR2GRAY);

    // 使用Canny算法检测边缘
    Mat edgeImage;
    Canny(grayImage, edgeImage, 100, 200);

    // 复制出边缘像素
    Mat resultImage;
    srcImage.copyTo(resultImage, edgeImage);
    resize(resultImage, resultImage, Size(500, 500));

    // 显示结果图像
    imshow("Result Image", resultImage);
    waitKey(0);

    return 0;
}

【实验结果】

在这里插入图片描述

  1. 使用OpenCV进行彩色图像模板匹配
    【问题描述】
    使用OpenCV编写一个程序,该程序对一幅彩色图像(例如当前目录中的lena.jpg)使用指定的模板图像(例如当前目录中的Template.jpg)进行模板匹配。要求使用分别使用OpenCV支持的6种匹配算法分别进行模板匹配,源图像中与模板最匹配的区域分别 使用一个指定颜色的矩形标记。
    【设计思路】
  2. 导入所需的库,包括cv2和numpy。
  3. 读取源图像(lena.jpg)和模板图像(Template.jpg)。
  4. 将源图像转换为灰度图像。
  5. 使用6种不同的匹配算法分别进行模板匹配。
  6. 找到与模板最匹配的区域,并在源图像中用指定颜色绘制矩形标记。
  7. 显示原始图像、灰度图像和结果图像。
  8. 保存结果图像。
    【关键程序】
int main()
{
    Mat img = imread("D:/tom.jpg");
    Mat templ = imread("D:/xm.jpg");

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

    namedWindow("Original Image", WINDOW_NORMAL);
    imshow("Original Image", img);

    namedWindow("Template Image", WINDOW_NORMAL);
    imshow("Template Image", templ);

    for (int i = 0; i < 6; i++)
    {
        Mat result;
        matchTemplate(img, templ, result, i);
        normalize(result, result, 0, 1, NORM_MINMAX, -1, Mat());

        double minVal, maxVal;
        Point minLoc, maxLoc;
        Point matchLoc;

        minMaxLoc(result, &minVal, &maxVal, &minLoc, &maxLoc, Mat());

        if (i == 0 || i == 1)
        {
            matchLoc = minLoc;
        }
        else
        {
            matchLoc = maxLoc;
        }

        rectangle(img, matchLoc, Point(matchLoc.x + templ.cols, matchLoc.y + templ.rows), Scalar(0, 0, 255), 2, 8, 0);
    }

    namedWindow("Result Image", WINDOW_NORMAL);
    imshow("Result Image", img);

    waitKey(0);
    return 0;
}

【实验结果】

在这里插入图片描述在这里插入图片描述在这里插入图片描述

文章来源:https://blog.csdn.net/qq_53002662/article/details/135613833
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。