0.配置基本环境

1.配置OpenGL

使用CLion来开发OpenGL应用,下面是配置链接:

Clion配置Freeglut 【计算机图形学】(2025)-CSDN博客


2.配置OpenCV

使用PyCharm来开发OpenGL应用(你需要下载PyCharm),使用python开发会方便很多,下面是配置链接:

Python中cv2 (OpenCV, opencv-python)库的安装、使用方法demo最新详细教程_python cv2-CSDN博客

1.题目(一般是6选3)


实验1  OpenGL中的图形变换 

1. 请使用OpenGL、GLU和GLUT编写一个显示线框立方体的程序。其中立方体的半径为1.5单位,并首先绕(0, 0, 0)~(1, 1, 0)旋转30度,然后远移6.5单位;观察体规定为:视场角=30度,宽高比=1,近=1,远=100;程序窗口的大小为(200, 200),标题为“线框立方体”。 

#include "include/GL/freeglut.h"

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); // 设置模型视图矩阵
}


int main(int argc, char **argv) {
    glutInit(&argc, argv); // 初始化GLUT库
    glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB); // 设置显示模式:单缓冲、RGB颜色模型
    int screen_width = glutGet(GLUT_SCREEN_WIDTH);
    int screen_height = glutGet(GLUT_SCREEN_HEIGHT);
    // 计算窗口的起始位置使其居中
    int window_width = 800;
    int window_height = 500;
    int window_x = (screen_width - window_width) / 2;
    int window_y = (screen_height - window_height) / 2;
    // 设置窗口大小
    glutInitWindowSize(window_width, window_height);
    // 设置窗口位置
    glutInitWindowPosition(window_x, window_y);
    glutCreateWindow("OpenGL"); // 创建窗口并设置标题
    init(); // 调用初始化函数
    glutDisplayFunc(display); // 设置显示回调函数
    glutReshapeFunc(reshape); // 设置重绘回调函数
    glutMainLoop(); // 进入GLUT事件处理循环
    return 0;
}

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

#include "include/GL/freeglut.h"
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个单位到观察点
}
int main(int argc, char **argv) {
    glutInit(&argc, argv); // 初始化GLUT库
    glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB); // 设置显示模式:单缓冲、RGB颜色模型
    int screen_width = glutGet(GLUT_SCREEN_WIDTH);
    int screen_height = glutGet(GLUT_SCREEN_HEIGHT);
    // 计算窗口的起始位置使其居中
    int window_width = 500;
    int window_height = 500;
    int window_x = (screen_width - window_width) / 2;
    int window_y = (screen_height - window_height) / 2;
    // 设置窗口大小
    glutInitWindowSize(window_width, window_height);
    // 设置窗口位置
    glutInitWindowPosition(window_x, window_y);
    glutCreateWindow("OpenGL 简单示例"); // 创建窗口并设置标题
    init(); // 调用初始化函数
    glutDisplayFunc(display); // 设置显示回调函数
    glutReshapeFunc(reshape); // 设置重绘回调函数
    glutMainLoop(); // 进入GLUT事件处理循环
    return 0;
}

3. 请使用OpenGL和GLUT编写一个显示线框椭球体的简单图形程序。其中椭球体的两极方向为上下方向,左右方向的半径为0.98,上下方向的半径为0.49,前后方向的半径为0.6,经线数为48,纬线数为24,使用正投影,裁剪窗口为(-1, -0.5)~(1, 0.5),程序窗口的大小为(400, 200),标题为“线框椭球”。 

#include <GL/glut.h>
#include <cmath>

// 椭球体的参数
#define LAT_SLICES 24
#define LON_SLICES 48
#define RADIUS_X 0.98
#define RADIUS_Y 0.49
#define RADIUS_Z 0.6

// 绘制椭球体的函数
void drawEllipsoid() {
    int i, j;
    for (i = 0; i < LON_SLICES; i++) {
        glBegin(GL_LINE_LOOP);
        for (j = 0; j <= LAT_SLICES; j++) {
            double lat = j * (M_PI / (double)LAT_SLICES) - M_PI_2;
            double lon = i * (2 * M_PI / (double)LON_SLICES) - M_PI;
            double x = RADIUS_X * cos(lat) * cos(lon);
            double y = RADIUS_Y * sin(lat);
            double z = RADIUS_Z * cos(lat) * sin(lon);
            glVertex3d(x, y, z);
        }
        glEnd();
    }
    for (i = 0; i < LAT_SLICES; i++) {
        glBegin(GL_LINE_STRIP);
        for (j = 0; j <= LON_SLICES; j++) {
            double lat = i * (M_PI / (double)LAT_SLICES) - M_PI_2;
            double lon = j * (2 * M_PI / (double)LON_SLICES) - M_PI;
            double x = RADIUS_X * cos(lat) * cos(lon);
            double y = RADIUS_Y * sin(lat);
            double z = RADIUS_Z * cos(lat) * sin(lon);
            glVertex3d(x, y, z);
        }
        glEnd();
    }
}

// 显示回调函数
void display() {
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    // 设置投影矩阵
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glOrtho(-1.0, 1.0, -0.5, 0.5, -1.0, 1.0);

    // 设置模型视图矩阵
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();

    // 绘制椭球体
    drawEllipsoid();

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

// 初始化OpenGL状态
void init() {
    // 设置背景颜色
    glClearColor(0.0, 0.0, 0.0, 1.0);
    // 启用深度测试
    glEnable(GL_DEPTH_TEST);
    // 设置线宽
    glLineWidth(1.0);
}

int main(int argc, char** argv) {
    // 初始化GLUT库
    glutInit(&argc, argv);
    // 设置显示模式:单缓冲、RGB颜色模型
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
    int screen_width = glutGet(GLUT_SCREEN_WIDTH);
    int screen_height = glutGet(GLUT_SCREEN_HEIGHT);
    // 计算窗口的起始位置使其居中
    int window_width = 400;
    int window_height = 200;
    int window_x = (screen_width - window_width) / 2;
    int window_y = (screen_height - window_height) / 2;
    // 设置窗口大小
    glutInitWindowSize(window_width, window_height);
    // 设置窗口位置
    glutInitWindowPosition(window_x, window_y);
    // 创建窗口
    glutCreateWindow("线框椭球");
    // 初始化OpenGL状态
    init();
    // 设置显示回调函数
    glutDisplayFunc(display);
    // 进入GLUT事件处理循环
    glutMainLoop();
    return 0;
}

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

#include "include/GL/freeglut.h"

static int angle = 0; // 初始旋转角度为0
void init() {
    glClearColor(0.0, 0.0, 0.0, 0.0); // 设置背景颜色为黑色
    glShadeModel(GL_FLAT); // 设置着色模式为平面着色
}

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函数
}

int main(int argc, char **argv) {
    glutInit(&argc, argv); // 初始化GLUT库
    glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB); // 设置显示模式:单缓冲、RGB颜色模型
    int screen_width = glutGet(GLUT_SCREEN_WIDTH);
    int screen_height = glutGet(GLUT_SCREEN_HEIGHT);
    // 计算窗口的起始位置使其居中
    int window_width = 500;
    int window_height = 500;
    int window_x = (screen_width - window_width) / 2;
    int window_y = (screen_height - window_height) / 2;
    // 设置窗口大小
    glutInitWindowSize(window_width, window_height);
    // 设置窗口位置
    glutInitWindowPosition(window_x, window_y);
    glutCreateWindow("OpenGL 简单示例"); // 创建窗口并设置标题
    init(); // 调用初始化函数
    glutDisplayFunc(display); // 设置显示回调函数
    glutReshapeFunc(reshape); // 设置重绘回调函数
    glutMainLoop(); // 进入GLUT事件处理循环
    return 0;
}

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),背景为黑色,标题为“多视口演示”。 

#include <GL/glut.h>
void init() {
    glClearColor(0.0, 0.0, 0.0, 0.0); // 设置背景颜色为黑色
    glShadeModel(GL_FLAT); // 设置着色模式为平面着色
}

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(); // 交换前后缓冲区
}
int main(int argc, char** argv) {
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
    // 获取屏幕宽度和高度
    int screen_width = glutGet(GLUT_SCREEN_WIDTH);
    int screen_height = glutGet(GLUT_SCREEN_HEIGHT);
    // 计算窗口的起始位置使其居中
    int window_width = 200;
    int window_height = 200;
    int window_x = (screen_width - window_width) / 2;
    int window_y = (screen_height - window_height) / 2;
    // 设置窗口大小
    glutInitWindowSize(window_width, window_height);
    // 设置窗口位置
    glutInitWindowPosition(window_x, window_y);
    glutCreateWindow("White Sphere with Lighting");
    init();
    glutDisplayFunc(display);
    glutMainLoop();
    return 0;
}


实验2  OpenGL的真实感图形 

6. 使用OpenGL和GLUT编写一个程序,用于模拟一个非常光滑的纯白球面在烈日暴晒下的效果。

#include <GL/glut.h>

// 初始化光照和材质属性
void initLighting() {
    glEnable(GL_DEPTH_TEST); // 启用深度测试

    // 环境光(不考虑环境光,所以设置为0)
    GLfloat ambientLight[] = {0.0, 0.0, 0.0, 1.0};

    // 点光源的漫反射光
    GLfloat diffuseLight[] = {1.0, 1.0, 1.0, 1.0}; // 白色光

    // 点光源的位置
    GLfloat lightPos[] = {1.0, 1.0, 1.0, 0.0}; // 右上角方向

    // 光源属性
    glLightfv(GL_LIGHT0, GL_POSITION, lightPos);
    glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuseLight);
    glLightfv(GL_LIGHT0, GL_AMBIENT, ambientLight);

    // 启用光源0和光照模型
    glEnable(GL_LIGHT0);
    glEnable(GL_LIGHTING);

    // 设置材质属性
    GLfloat mat_ambient[] = {0.2, 0.0, 0.6, 1.0}; // 紫色
    GLfloat mat_diffuse[] = {0.2, 0.0, 0.6, 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);
}

// 绘制球体
void drawSphere() {
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glutSolidSphere(1.0, 20, 20); // 绘制半径为1的球体
}

// 显示回调函数
void display() {
    initLighting();
    drawSphere();
    glutSwapBuffers();
}

// 窗口重绘回调函数
void reshape(int w, int h) {
    glViewport(0, 0, w, h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective(60.0, (GLfloat)w / (GLfloat)h, 1.0, 20.0);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    gluLookAt(0.0, 0.0, 5.0,  // 眼睛位置
              0.0, 0.0, 0.0,  // 观察点
              0.0, 1.0, 0.0); // 上方向
}

// 主函数
int main(int argc, char** argv) {
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
    int screen_width = glutGet(GLUT_SCREEN_WIDTH);
    int screen_height = glutGet(GLUT_SCREEN_HEIGHT);
    // 计算窗口的起始位置使其居中
    int window_width = 500;
    int window_height = 500;
    int window_x = (screen_width - window_width) / 2;
    int window_y = (screen_height - window_height) / 2;
    // 设置窗口大小
    glutInitWindowSize(window_width, window_height);
    // 设置窗口位置
    glutInitWindowPosition(window_x, window_y);
    glutCreateWindow("光照效果演示");

    glutDisplayFunc(display);
    glutReshapeFunc(reshape);
    glutMainLoop();
    return 0;
}

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

#include <GL/glut.h>

// 初始化光照和材质属性
void initLighting() {
    glEnable(GL_DEPTH_TEST); // 启用深度测试

    // 环境光(不考虑环境光,所以设置为0)
    GLfloat ambientLight[] = {0.0, 0.0, 0.0, 1.0};

    // 点光源的漫反射光
    GLfloat diffuseLight[] = {1.0, 1.0, 1.0, 1.0}; // 白色光

    // 点光源的位置
    GLfloat lightPos[] = {1.0, 1.0, 1.0, 0.0}; // 右上角方向

    // 光源属性
    glLightfv(GL_LIGHT0, GL_POSITION, lightPos);
    glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuseLight);
    glLightfv(GL_LIGHT0, GL_AMBIENT, ambientLight);

    // 启用光源0和光照模型
    glEnable(GL_LIGHT0);
    glEnable(GL_LIGHTING);

    // 设置材质属性
    GLfloat mat_ambient[] = {0.2, 0.0, 0.6, 1.0}; // 紫色
    GLfloat mat_diffuse[] = {0.2, 0.0, 0.6, 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);
}

// 绘制球体
void drawSphere() {
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glutSolidSphere(1.0, 20, 20); // 绘制半径为1的球体
}

// 显示回调函数
void display() {
    initLighting();
    drawSphere();
    glutSwapBuffers();
}

// 窗口重绘回调函数
void reshape(int w, int h) {
    glViewport(0, 0, w, h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective(60.0, (GLfloat)w / (GLfloat)h, 1.0, 20.0);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    gluLookAt(0.0, 0.0, 5.0,  // 眼睛位置
              0.0, 0.0, 0.0,  // 观察点
              0.0, 1.0, 0.0); // 上方向
}

// 主函数
int main(int argc, char** argv) {
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
    int screen_width = glutGet(GLUT_SCREEN_WIDTH);
    int screen_height = glutGet(GLUT_SCREEN_HEIGHT);
    // 计算窗口的起始位置使其居中
    int window_width = 500;
    int window_height = 500;
    int window_x = (screen_width - window_width) / 2;
    int window_y = (screen_height - window_height) / 2;
    // 设置窗口大小
    glutInitWindowSize(window_width, window_height);
    // 设置窗口位置
    glutInitWindowPosition(window_x, window_y);
    glutCreateWindow("光照效果演示");

    glutDisplayFunc(display);
    glutReshapeFunc(reshape);
    glutMainLoop();
    return 0;
}


实验3  OpenCV核心功能 

8. 请根据BMP文件的格式编写一个C++函数vector<uchar> loadBmp24(const string &path)。该函数用于从一个真彩色BMP文件中读取图像数据,保存在uchar数组中(可以忽略非真彩色BMP文件)。 

import struct

def load_bmp24(path):
    with open(path, 'rb') as f:
        # 读取BMP文件头 (14字节)
        file_header = f.read(14)
        file_type, file_size, reserved1, reserved2, offset = struct.unpack('<2sIHHI', file_header)

        # 确保是BMP文件
        if file_type != b'BM':
            raise ValueError("Not a BMP file")

        # 定位到DIB头开始位置 (偏移量 - 文件头大小)
        f.seek(offset - 14)

        # 读取DIB头 (40字节)
        dib_header = f.read(40)
        if len(dib_header) != 40:
            raise ValueError("DIB header is not 40 bytes long")

        # 解析DIB头
        header_size, width, height, planes, bit_count, compression, size_image, x_pixels_per_meter, y_pixels_per_meter, colors_used, colors_important = struct.unpack(
            '<IiiHHIIIIII', dib_header)

        print(height)

        # 确保是24位不压缩的BMP文件
        if bit_count != 24 or compression != 0:
            raise ValueError("Not a 24-bit uncompressed BMP file")

        # 每行字节数,填充到4字节边界
        row_size = (width * 3 + 3) & ~3

        # 定位到像素数据开始位置
        f.seek(offset)

        # 读取像素数据
        image_data = f.read(row_size * abs(height))  # 使用abs(height)处理BMP文件的上下顺序

        # 转换为字节列表并返回
        return bytearray(image_data)

# 使用示例
# 替换以下路径为你的BMP文件的实际路径
path = "\\Computer_Graphics\\Experiment_2024_12_02\\picture.bmp"
image_data = load_bmp24(path)
print(image_data)

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

import cv2
import numpy as np

path = "\\Computer_Graphics\\Experiment_2024_12_02\\picture.jpg"
# 读取图像
image = cv2.imread(path)
# 检查图像是否被正确加载
if image is None:
    print("图像加载失败,请检查路径!")
else:
    # 显示原始图像
    cv2.imshow('Original Image', image)
    cv2.waitKey(0)  # 等待按键

    # 定义矩形区域的左上顶点和宽高
    top_left_x = 128
    top_left_y = 256
    width = 256
    height = 128

    # 裁剪图像
    cropped_image = image[top_left_y:top_left_y+height, top_left_x:top_left_x+width]

    # 显示裁剪后的图像
    cv2.imshow('Cropped Image', cropped_image)
    cv2.waitKey(0)  # 等待按键

    # 关闭所有OpenCV窗口
    cv2.destroyAllWindows()

10. 请使用OpenCV编写一个简单的程序,该程序首先读入一幅真彩色图像,然后将这幅彩色图像的3个通道分离出来,得到3幅灰度图像,最后显示这3幅灰度图像。 

import cv2
import numpy as np
path = "D:\\Computer_Graphics\\Experiment_2024_12_02\\picture.jpg"
# 读取图像
image = cv2.imread(path)  # 替换为你的图像路径

# 检查图像是否被正确加载
if image is None:
    print("图像加载失败,请检查路径!")
else:
    # 分离颜色通道
    B, G, R = cv2.split(image)

    # 垂直拼接三个通道
    # 首先确保三个通道的宽度相同,这里我们取图像的原始宽度
    height, width, _ = image.shape
    B = cv2.resize(B, (width//2, height//2))  # 调整高度以适应垂直拼接
    G = cv2.resize(G, (width//2, height//2))  # 调整高度以适应垂直拼接
    R = cv2.resize(R, (width//2, height//2))  # 调整高度以适应垂直拼接

    concatenated_image = np.vstack((B, G, R))  # 垂直拼接

    # 显示拼接后的图像
    cv2.imshow('Concatenated Channels', concatenated_image)

    # 等待按键
    cv2.waitKey(0)

    # 关闭所有窗口
    cv2.destroyAllWindows()

11. 首先使用OpenCV装入一幅灰度图像并显示该图像,然后计算出该图像的最小像素值min和最大像素值max,最后将每个像素都减去min再乘以255/max以后显示结果图像。 

import cv2
import numpy as np
path = "picture.jpg"

gray_image = cv2.imread(path, cv2.IMREAD_GRAYSCALE)
# 计算最小像素值和最大像素值
min_pixel = np.min(gray_image)
max_pixel = np.max(gray_image)
# 对每个像素进行处理
processed_image = (gray_image - min_pixel) * (255 / (max_pixel - min_pixel))
processed_image = np.clip(processed_image, 0, 255).astype(np.uint8)
processed= np.vstack((gray_image, processed_image))  # 垂直拼接
# 显示处理后的图像
cv2.imshow('Processed Image', processed)
cv2.waitKey(0)

# 退出并销毁所有窗口
cv2.destroyAllWindows()

12. 随机生成一幅浮点数灰度图像(大小和亮度都是随机的,大小值位于区间[128, 639]),然后将该图像变换成亮度是0~1的浮点数图像,最后变换成字节图像并显示该图像。 

import cv2
import numpy as np

# 设置随机种子以获得可重复的结果
np.random.seed(0)

# 随机生成一幅浮点数灰度图像,大小值位于区间[128, 639]
# 假设图像大小为512x512,可以根据需要调整
image_size = (512, 512)
random_float_image = np.random.randint(128, 640, size=image_size).astype(np.float32)

# 显示原始浮点数灰度图像
cv2.imshow('Random Float Image', random_float_image / 639.0)  # 先归一化到[0, 1]区间
cv2.waitKey(0)

# 将浮点数灰度图像变换成亮度是0~1的浮点数图像
normalized_float_image = random_float_image / 639.0

# 将浮点数图像变换成字节图像
byte_image = (normalized_float_image * 255).astype(np.uint8)

# 显示字节图像
cv2.imshow('Byte Image', byte_image)
cv2.waitKey(0)

# 关闭所有窗口
cv2.destroyAllWindows()


实验4  图像变换 

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

import cv2
import numpy as np

path = "\\Computer_Graphics\\Experiment_2024_12_02\\picture.jpg"

image = cv2.imread(path)

# 检查图像是否被正确加载
if image is None:
    print("图像加载失败,请检查路径!")
else:
    # 获取图像尺寸
    (h, w) = image.shape[:2]

    # 计算旋转中心
    center = (w // 2, h // 2)

    # 计算旋转矩阵
    M = cv2.getRotationMatrix2D(center, 45, 0.707)

    # 进行缩放变换
    scaled_rotated_image = cv2.warpAffine(image, M, (w, h))

    # 显示缩放变换后的图像
    cv2.imshow('Scaled and Rotated Image', scaled_rotated_image)

    # 等待按键
    cv2.waitKey(0)

    # 关闭所有窗口
    cv2.destroyAllWindows()

14. 使用OpenCV编写一个演示傅立叶变换和逆变换的程序。该程序首先装入一幅灰度图像并显示该图像,然后对该图像进行傅立叶正变换,对得到的结果进行傅立叶逆变换,显示得到的结果以便与原图像进行比对。 

import cv2
import numpy as np

# 读取灰度图像
path = "\\picture.jpg"
image = cv2.imread(path, cv2.IMREAD_GRAYSCALE)

# 检查图像是否被正确加载
if image is None:
    print("图像加载失败,请检查路径!")
else:
    # 显示原始灰度图像
    cv2.imshow('Original Grayscale Image', image)

    # 进行傅立叶变换
    dft = cv2.dft(np.float32(image), flags=cv2.DFT_COMPLEX_OUTPUT)
    dft_shift = np.fft.fftshift(dft)

    # 显示傅立叶变换的结果(显示幅度谱)

    magnitude_spectrum = 20 * np.log(cv2.magnitude(dft_shift[:, :, 0], dft_shift[:, :, 1]))
    magnitude_spectrum = np.uint8(magnitude_spectrum / np.max(magnitude_spectrum) * 255)  # 归一化到0-255范围
    cv2.imshow('Magnitude Spectrum', magnitude_spectrum)

    # 进行傅立叶逆变换
    idft_shift = np.fft.ifftshift(dft_shift)
    img_back = cv2.idft(idft_shift)
    img_back = cv2.magnitude(img_back[:, :, 0], img_back[:, :, 1])

    # 显示傅立叶逆变换的结果
    cv2.imshow('Image after Inverse DFT', img_back)
    cv2.waitKey(0)

    # 关闭所有窗口
    cv2.destroyAllWindows()

15. 请为OpenCV提供一个中心化函数FFTShift,该函数的函数原型为“Mat FFTShift(Mat X);”。 

import cv2
import numpy as np


def FFTShift(X):
    """
    中心化FFT结果的函数。

    参数:
    X -- cv2.Mat对象,即输入的矩阵。

    返回:
    中心化后的矩阵。
    """
    # 获取矩阵的维度
    rows, cols = X.shape[:2]

    # 计算中心点
    crow, ccol = rows // 2, cols // 2

    # 从中心点分割矩阵
    top = X[:crow, :]
    bottom = X[crow:, :]
    left = X[:, :ccol]
    right = X[:, ccol:]

    # 重新组合矩阵,使其中心化
    shifted = np.concatenate((bottom, top), axis=0)
    shifted = np.concatenate((right, left), axis=1)

    return shifted


# 使用示例
# 读取图像并转换为灰度
path = "picture.jpg"
image = cv2.imread(path, cv2.IMREAD_GRAYSCALE)

# 检查图像是否被正确加载
if image is None:
    print("图像加载失败,请检查路径!")
else:

    # 进行傅立叶变换
    dft = cv2.dft(np.float32(image), flags=cv2.DFT_COMPLEX_OUTPUT)

    # 应用中心化FFTShift
    dft_shift = FFTShift(dft)

    # 计算幅度谱
    magnitude_spectrum = 20 * np.log(cv2.magnitude(dft_shift[:, :, 0], dft_shift[:, :, 1]))

    # 归一化到0-255范围
    magnitude_spectrum = cv2.normalize(magnitude_spectrum, None, 0, 255, cv2.NORM_MINMAX)

    # 转换为uint8类型
    magnitude_spectrum = magnitude_spectrum.astype(np.uint8)

    # 显示中心化后的幅度谱
    cv2.imshow('Magnitude Spectrum', magnitude_spectrum)
    cv2.waitKey(0)
    cv2.destroyAllWindows()


实验5  图像增强 

16. 使用OpenCV编写一个程序,该程序对一幅彩色图像进行一次中值模糊,要求分别显示源图像和模糊化以后的图像。其中内核大小为5×5。 

import cv2

path = "picture.jpg"
image = cv2.imread(path)

# 检查图像是否被正确加载
if image is None:
    print("图像加载失败,请检查路径!")
else:
    # 显示源图像
    cv2.imshow('Source Image', image)

    # 对图像进行中值模糊,内核大小为5x5
    blurred_image = cv2.medianBlur(image, 5)

    # 显示模糊后的图像
    cv2.imshow('Blurred Image', blurred_image)

    # 等待按键
    cv2.waitKey(0)

    # 关闭所有窗口
    cv2.destroyAllWindows()

17. 使用OpenCV编写一个程序,该程序对一幅灰度图像进行Sobel锐化,要求显示锐化以后的图像。其中内核大小为3×3,x和y方向均使用1阶差分。 

import cv2
import numpy as np

path = "picture.jpg"
image = cv2.imread(path, cv2.IMREAD_GRAYSCALE)

# 检查图像是否被正确加载
if image is None:
    print("图像加载失败,请检查路径!")
else:
    # 使用Sobel算子进行x方向和y方向的1阶差分
    sobelx = cv2.Sobel(image, cv2.CV_64F, 1, 0, ksize=3)
    sobely = cv2.Sobel(image, cv2.CV_64F, 0, 1, ksize=3)

    # 计算梯度的幅度
    sobel_grad = cv2.magnitude(sobelx, sobely)

    # 将梯度幅度归一化到0-255范围
    sobel_grad = np.uint8(sobel_grad / np.max(sobel_grad) * 255)

    # 将梯度幅度加到原图像上进行锐化
    sharpened_image = cv2.add(image, sobel_grad)

    # 显示锐化后的图像
    cv2.imshow('Sharpened Image', sharpened_image)

    # 等待按键
    cv2.waitKey(0)

    # 关闭所有窗口
    cv2.destroyAllWindows()

18. 使用OpenCV编写一个程序,该程序对一幅灰度图像进行Laplace锐化,要求显示锐化以后的图像。其中内核大小为3×3。 

import cv2
import numpy as np

path = "\\picture.jpg"
image = cv2.imread(path, cv2.IMREAD_GRAYSCALE)

# 检查图像是否被正确加载
if image is None:
    print("图像加载失败,请检查路径!")
else:
    # 使用Laplace算子进行锐化,内核大小为3x3
    laplace_kernel = np.array([[0, 1, 0],
                              [1, -4, 1],
                              [0, 1, 0]], dtype=np.float32) / 8  # 标准化内核
    laplace = cv2.filter2D(image, -1, laplace_kernel)

    # 将拉普拉斯结果转换为uint8类型
    laplace = np.uint8(np.absolute(laplace))

    # 将拉普拉斯结果加到原图像上进行锐化
    sharpened_image = cv2.add(image, laplace)

    # 显示锐化后的图像
    cv2.imshow('Sharpened Image', sharpened_image)

    # 等待按键
    cv2.waitKey(0)

    # 关闭所有窗口
    cv2.destroyAllWindows()

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

import cv2
import numpy as np

path = "D:\\picture.jpg"
image = cv2.imread(path)

# 检查图像是否被正确加载
if image is None:
    print("图像加载失败,请检查路径!")
else:
    # 显示源图像
    cv2.imshow('Original Image', image)

    # 定义3x3正方形模板,锚点位于中心
    kernel = np.ones((3, 3), np.uint8)

    # 对图像进行第一次腐蚀操作
    erosion1 = cv2.erode(image, kernel, iterations=1)

    # 对图像进行第二次腐蚀操作
    erosion2 = cv2.erode(erosion1, kernel, iterations=1)

    # 显示腐蚀后的图像
    cv2.imshow('Eroded Image after 2 iterations', erosion2)

    # 等待按键
    cv2.waitKey(0)

    # 关闭所有窗口
    cv2.destroyAllWindows()

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

import cv2
import numpy as np


def butterworth_lowpass_filter(image, k, n):
    """
    对图像应用n阶巴特沃斯低通滤波器。

    参数:
    image -- 输入的灰度图像。
    k -- 截止频率。
    n -- 滤波器的阶数。

    返回:
    滤波后的图像。
    """
    # 获取图像尺寸
    M, N = image.shape

    # 创建距离矩阵D(u, v)
    u = np.tile(np.arange(N), (M, 1))
    v = np.tile(np.arange(M), (N, 1)).T
    D = np.sqrt(u ** 2 + v ** 2)

    # 创建巴特沃斯低通滤波器
    H = 1 / (1 + (D / k) ** (2 * n))

    # 将滤波器应用于图像的傅立叶变换
    dft = cv2.dft(np.float32(image), flags=cv2.DFT_COMPLEX_OUTPUT)
    dft_shift = np.fft.fftshift(dft)

    # 应用滤波器到复数的实部和虚部
    fshift = dft_shift.copy()
    fshift[:, :, 0] *= H  # 实部
    fshift[:, :, 1] *= H  # 虚部

    # 应用逆傅立叶变换
    f_ishift = np.fft.ifftshift(fshift)
    img_back = cv2.idft(f_ishift)

    # 计算幅度
    img_back = cv2.magnitude(img_back[:, :, 0], img_back[:, :, 1])

    # 归一化到0-255范围
    img_back = np.uint8(img_back / np.max(img_back) * 255)

    return img_back


path = "\\picture.jpg"
image = cv2.imread(path, cv2.IMREAD_GRAYSCALE)

# 检查图像是否被正确加载
if image is None:
    print("图像加载失败,请检查路径!")
else:
    # 显示源图像
    cv2.imshow('Original Image', image)

    # 对图像进行2阶指数低通滤波,截止频率为20
    filtered_image = butterworth_lowpass_filter(image, k=20, n=2)

    # 显示滤波后的图像
    cv2.imshow('Filtered Image', filtered_image)

    # 等待按键
    cv2.waitKey(0)

    # 关闭所有窗口
    cv2.destroyAllWindows()

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

import cv2
import numpy as np


def butterworth_highpass_filter(image, k, n):
    """
    对图像应用n阶巴特沃斯高通滤波器。

    参数:
    image -- 输入的灰度图像。
    k -- 截止频率。
    n -- 滤波器的阶数。

    返回:
    滤波后的图像。
    """
    # 获取图像尺寸
    M, N = image.shape

    # 创建距离矩阵D(u, v)
    u = np.tile(np.arange(N), (M, 1))
    v = np.tile(np.arange(M), (N, 1)).T
    D = np.sqrt(u ** 2 + v ** 2)

    # 创建巴特沃斯高通滤波器
    H = 1 - 1 / (1 + (D / k) ** (2 * n))

    # 将滤波器应用于图像的傅立叶变换
    dft = cv2.dft(np.float32(image), flags=cv2.DFT_COMPLEX_OUTPUT)
    dft_shift = np.fft.fftshift(dft)

    # 应用滤波器到复数的实部和虚部
    fshift = dft_shift.copy()
    fshift[:, :, 0] *= H  # 实部
    fshift[:, :, 1] *= H  # 虚部

    # 应用逆傅立叶变换
    f_ishift = np.fft.ifftshift(fshift)
    img_back = cv2.idft(f_ishift)

    # 计算幅度
    img_back = cv2.magnitude(img_back[:, :, 0], img_back[:, :, 1])

    # 归一化到0-255范围
    img_back = np.uint8(img_back / np.max(img_back) * 255)

    return img_back


path = "D:\picture.jpg"
image = cv2.imread(path, cv2.IMREAD_GRAYSCALE)

# 检查图像是否被正确加载
if image is None:
    print("图像加载失败,请检查路径!")
else:
    # 显示源图像
    cv2.imshow('Original Image', image)

    # 对图像进行2阶指数高通滤波,截止频率为45
    filtered_image = butterworth_highpass_filter(image, k=45, n=2)

    # 显示滤波后的图像
    cv2.imshow('Filtered Image', filtered_image)

    # 等待按键
    cv2.waitKey(0)

    # 关闭所有窗口
    cv2.destroyAllWindows()


实验6  图像解析 

22. 使用OpenCV编写一个程序,该程序对一幅灰度图像进行二值化变换,要求分别显示源图像和二值化以后的图像。其中二值化阈值为127,高亮度改为255。 

import cv2

# 读取灰度图像
image = cv2.imread('path_to_your_image.jpg', cv2.IMREAD_GRAYSCALE)  # 替换为你的图像路径

# 检查图像是否被正确加载
if image is None:
    print("图像加载失败,请检查路径!")
else:
    # 显示源图像
    cv2.imshow('Original Image', image)

    # 进行二值化变换,阈值为127,高亮度改为255
    _, binary_image = cv2.threshold(image, 127, 255, cv2.THRESH_BINARY)

    # 显示二值化后的图像
    cv2.imshow('Binary Image', binary_image)

    # 等待按键
    cv2.waitKey(0)

    # 关闭所有窗口
    cv2.destroyAllWindows()

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

import cv2

# 读取灰度图像
image = cv2.imread('path_to_your_image.jpg', cv2.IMREAD_GRAYSCALE)  # 替换为你的图像路径

# 检查图像是否被正确加载
if image is None:
    print("图像加载失败,请检查路径!")
else:
    # 显示源图像
    cv2.imshow('Original Image', image)

    # 进行Canny边缘检测,小阈值为50,大阈值为150,内核大小为3
    edges = cv2.Canny(image, 50, 150, apertureSize=3)

    # 显示检测到的边缘
    cv2.imshow('Edges Detected', edges)

    # 等待按键
    cv2.waitKey(0)

    # 关闭所有窗口
    cv2.destroyAllWindows()

24. 使用OpenCV编写一个程序,该程序首先使用Canny算法检测边缘,然后从源图像中复制出边缘像素。注意,源图像是彩色图像,边缘检测时需转换成灰度图像,结果图像也是彩色图像。 

import cv2
import numpy as np
path = "picture.jpg"

# 读取彩色源图像
color_image = cv2.imread(path)  # 替换为你的图像路径

# 检查图像是否被正确加载
if color_image is None:
    print("图像加载失败,请检查路径!")
else:
    # 显示源图像
    cv2.imshow('Original Color Image', color_image)

    # 将彩色图像转换为灰度图像
    gray_image = cv2.cvtColor(color_image, cv2.COLOR_BGR2GRAY)

    # 使用Canny算法检测边缘,小阈值为50,大阈值为150,内核大小为3
    edges = cv2.Canny(gray_image, 50, 150, apertureSize=3)

    # 将边缘检测结果转换为三通道图像,以便与彩色图像进行操作
    edges_color = cv2.cvtColor(edges, cv2.COLOR_GRAY2BGR)

    # 从源图像中复制出边缘像素
    # 我们将使用cv2.bitwise_and来逐通道进行操作
    result_image = cv2.bitwise_and(color_image, edges_color)

    # 显示结果图像
    cv2.imshow('Result Image', result_image)

    # 等待按键
    cv2.waitKey(0)

    # 关闭所有窗口
    cv2.destroyAllWindows()

25. 使用OpenCV编写一个程序,该程序对一幅彩色图像(例如当前目录中的lena.jpg)使用指定的模板图像(例如当前目录中的Template.jpg)进行模板匹配。要求使用分别使用OpenCV支持的6种匹配算法分别进行模板匹配,源图像中与模板最匹配的区域分别使用一个指定颜色的矩形标记。 

import cv2
import numpy as np

# 读取彩色源图像和模板图像
source_image = cv2.imread("picture.jpg")  # 确保当前目录中有lena.jpg文件
template = cv2.imread("picture_temp.jpg", 0)  # 确保当前目录中有template.jpg文件
cv2.imshow('Original Color Image', template)
# 检查图像是否被正确加载
if source_image is None or template is None:
    print("图像加载失败,请检查路径!")
else:
    # 将源图像转换为灰度图像
    source_gray = cv2.cvtColor(source_image, cv2.COLOR_BGR2GRAY)

    # 获取模板的高度和宽度
    h, w = template.shape[:2]

    # 遍历所有匹配方法
    methods = [
        ("SQDIFF", cv2.TM_SQDIFF),
        ("SQDIFF_NORMED", cv2.TM_SQDIFF_NORMED),
        ("CCORR", cv2.TM_CCORR),
        ("CCORR_NORMED", cv2.TM_CCORR_NORMED),
        ("CCOEFF", cv2.TM_CCOEFF),
        ("CCOEFF_NORMED", cv2.TM_CCOEFF_NORMED)
    ]

    for method_name, method_flag in methods:
        # 应用模板匹配方法
        result = cv2.matchTemplate(source_gray, template, method_flag)
        # 找到最匹配的位置
        min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(result)

        # 计算最匹配区域的坐标
        top_left = max_loc
        bottom_right = (top_left[0] + w, top_left[1] + h)

        # 在源图像上标记最匹配的区域
        match_image = source_image.copy()
        cv2.rectangle(match_image, top_left, bottom_right, (0, 0, 255), 2)

        # 显示匹配结果
        cv2.imshow(f'{method_name} Match', match_image)

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

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐