当前位置: 首页 > news >正文

使用OpenGL实现双线性插值和双三次插值C++实现

整理一下最近跑的两个小程序,该程序的目的是利用OpenGL实现图像的缩放,同时比较双线性插值和双三次插值两种算法的最终效果,于是将这两种算法代码以及结果展出,使用的语言为C++,放大倍数为2倍。

先置条件

1.使用的IDE为:visual studio2022
2.软件平台:Windows11
3.OpenGL版本:3
4.确保 GLFW 已正确安装:下载预编译的 GLFW 库: GLFW 官方下载页面链接;解压后,将 include 文件夹中的头文件和 lib 文件夹中的库文件添加到你的项目中。
5.链接 GLFW 库:
在这里插入图片描述

6.因为项目要求,输入的是raw图片,需要输入png或jpg图片的自行修改代码

双线性插值

#include <glew.h>
#include <glfw3.h>
#include <iostream>
#include <fstream>
#include <cstdio>

void checkGLError(const char* label) {
    GLenum err;
    while ((err = glGetError()) != GL_NO_ERROR) {
        std::cerr << "OpenGL Error (" << label << "): " << err << std::endl;
    }
}

unsigned char* loadRawImage(const char* path, int& width, int& height, int channels) {
    FILE* file;
    errno_t err = fopen_s(&file, path, "rb");
    if (err != 0 || !file) {
        fprintf(stderr, "Failed to open raw image file: %s\n", path);
        return nullptr;
    }

    fseek(file, 0, SEEK_END);
    long fileSize = ftell(file);
    rewind(file);

    int expectedSize = width * height * channels;
    if (fileSize != expectedSize) {
        fprintf(stderr, "File size does not match expected size for the given dimensions and channels.\n");
        fclose(file);
        return nullptr;
    }

    unsigned char* imageData = new unsigned char[fileSize];
    fread(imageData, 1, fileSize, file);
    fclose(file);

    return imageData;
}

GLuint createTexture(unsigned char* data, int width, int height, int channels) {
    GLuint textureID;
    glGenTextures(1, &textureID);
    glBindTexture(GL_TEXTURE_2D, textureID);

    GLenum internalFormat = (channels == 3) ? GL_RGB : GL_R8;
    GLenum format = (channels == 3) ? GL_RGB : GL_RED;

    glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, width, height, 0, format, GL_UNSIGNED_BYTE, data);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

    return textureID;
}

void saveRawImage(unsigned char* data, int width, int height, int channels, const char* filename) {
    std::ofstream outFile(filename, std::ios::binary);
    if (!outFile) {
        fprintf(stderr, "Failed to open output file: %s\n", filename);
        return;
    }

    int dataSize = width * height * channels;
    outFile.write(reinterpret_cast<char*>(data), dataSize);
    outFile.close();
}

int main(int argc, char* argv[]) {
    if (!glfwInit()) {
        fprintf(stderr, "Failed to initialize GLFW\n");
        return -1;
    }

    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

    GLFWwindow* window = glfwCreateWindow(800, 600, "OpenGL Image Scaling", NULL, NULL);
    if (!window) {
        fprintf(stderr, "Failed to create GLFW window\n");
        glfwTerminate();
        return -1;
    }

    glfwMakeContextCurrent(window);
    glewExperimental = GL_TRUE;
    GLenum status = glewInit();

    if (status != GLEW_OK) {
        fprintf(stderr, "GLEW Error: %s\n", glewGetErrorString(status));
        return -1;
    }

    // Load shader program and set up OpenGL state here...
    const char* vertexShaderSource = "#version 330 core\n"
        "layout(location = 0) in vec3 aPos;\n"
        "layout(location = 1) in vec2 aTexCoord;\n"
        "out vec2 TexCoord;\n"
        "void main()\n"
        "{\n"
        "   gl_Position = vec4(aPos, 1.0);\n"
        "   TexCoord = aTexCoord;\n"
        "}\0";

    const char* fragmentShaderSource = "#version 330 core\n"
        "out vec4 FragColor;\n"
        "in vec2 TexCoord;\n"
        "uniform sampler2D ourTexture;\n"
        "void main()\n"
        "{\n"
        "   FragColor = texture(ourTexture, TexCoord);\n"
        "}\0";

    GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER);
    glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
    glCompileShader(vertexShader);

    GLint success;
    GLchar infoLog[512];
    glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);
    if (!success) {
        glGetShaderInfoLog(vertexShader, 512, NULL, infoLog);
        printf("ERROR::SHADER::VERTEX::COMPILATION_FAILED\n%s\n", infoLog);
    };

    GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
    glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);
    glCompileShader(fragmentShader);

    glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success);
    if (!success) {
        glGetShaderInfoLog(fragmentShader, 512, NULL, infoLog);
        printf("ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n%s\n", infoLog);
    };

    GLuint shaderProgram = glCreateProgram();
    glAttachShader(shaderProgram, vertexShader);
    glAttachShader(shaderProgram, fragmentShader);
    glLinkProgram(shaderProgram);

    glGetProgramiv(shaderProgram, GL_LINK_STATUS, &success);
    if (!success) {
        glGetProgramInfoLog(shaderProgram, 512, NULL, infoLog);
        printf("ERROR::PROGRAM::LINKING_FAILED\n%s\n", infoLog);
    }
    glDeleteShader(vertexShader);
    glDeleteShader(fragmentShader);

    GLfloat vertices[] = {
        // positions          // texture coords
         1.0f,  1.0f, 0.0f,   1.0f, 1.0f, // top right
         1.0f, -1.0f, 0.0f,   1.0f, 0.0f, // bottom right
        -1.0f, -1.0f, 0.0f,   0.0f, 0.0f, // bottom left
        -1.0f,  1.0f, 0.0f,   0.0f, 1.0f  // top left 
    };
    GLuint indices[] = {
        0, 1, 3, // first triangle
        1, 2, 3  // second triangle
    };

    GLuint VBO, VAO, EBO;
    glGenVertexArrays(1, &VAO);
    glGenBuffers(1, &VBO);
    glGenBuffers(1, &EBO);

    glBindVertexArray(VAO);

    glBindBuffer(GL_ARRAY_BUFFER, VBO);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);

    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), (GLvoid*)0);
    glEnableVertexAttribArray(0);
    glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), (GLvoid*)(3 * sizeof(GLfloat)));
    glEnableVertexAttribArray(1);

    glBindVertexArray(0); // Unbind VAO

    int originalWidth = 2480; // Set your image width here
    int originalHeight = 3512; // Set your image height here
    int channels = 3; // RGB image
    unsigned char* imageData = loadRawImage("input.raw", originalWidth, originalHeight, channels);
    if (!imageData) {
        glfwDestroyWindow(window);
        glfwTerminate();
        return -1;
    }

    int newWidth = originalWidth * 2; // Scale up by three times
    int newHeight = originalHeight * 2;

    GLuint framebuffer;
    glGenFramebuffers(1, &framebuffer);
    glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);

    GLuint textureColorbuffer;
    glGenTextures(1, &textureColorbuffer);
    glBindTexture(GL_TEXTURE_2D, textureColorbuffer);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, newWidth, newHeight, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textureColorbuffer, 0);

    if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
        std::cout << "ERROR::FRAMEBUFFER:: Framebuffer is not complete!" << std::endl;
    glBindFramebuffer(GL_FRAMEBUFFER, 0);

    GLuint texture = createTexture(imageData, originalWidth, originalHeight, channels);
    delete[] imageData;

    while (!glfwWindowShouldClose(window)) {
        glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT);

        glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
        glViewport(0, 0, newWidth, newHeight);

        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D, texture);
        glUniform1i(glGetUniformLocation(shaderProgram, "ourTexture"), 0);

        glUseProgram(shaderProgram);

        glBindVertexArray(VAO);
        glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
        glBindVertexArray(0);

        glBindFramebuffer(GL_FRAMEBUFFER, 0);

        unsigned char* frameBufferPixels = new unsigned char[newWidth * newHeight * channels];
        glBindFramebuffer(GL_READ_FRAMEBUFFER, framebuffer);
        glReadPixels(0, 0, newWidth, newHeight, GL_RGB, GL_UNSIGNED_BYTE, frameBufferPixels);

        saveRawImage(frameBufferPixels, newWidth, newHeight, channels, "output.raw");

        delete[] frameBufferPixels;
        break; // Only scale once and then exit
    }

    glDeleteVertexArrays(1, &VAO);
    glDeleteBuffers(1, &VBO);
    glDeleteBuffers(1, &EBO);
    glDeleteTextures(1, &texture);
    glDeleteTextures(1, &textureColorbuffer);
    glDeleteFramebuffers(1, &framebuffer);
    glDeleteProgram(shaderProgram);

    glfwDestroyWindow(window);
    glfwTerminate();

    return 0;
}




双三次插值

#include <glew.h>
#include <glfw3.h>
#include <iostream>
#include <fstream>
#include <vector>

// 着色器源码
const char* vertexShaderSource = R"(
#version 330 core
layout(location = 0) in vec2 aPos;
layout(location = 1) in vec2 aTexCoords;

out vec2 TexCoords;

void main() {
    gl_Position = vec4(aPos, 0.0, 1.0);
    TexCoords = aTexCoords;
}
)";

const char* fragmentShaderSource = R"(
#version 330 core

in vec2 TexCoords;
out vec4 FragColor;

uniform sampler2D image;
uniform float texelSize;

float cubic(float x) {
    float a = -0.5;
    if (x < 0.0) x = -x;
    if (x < 1.0) return (a + 2.0) * x * x * x - (a + 3.0) * x * x + 1.0;
    if (x < 2.0) return a * x * x * x - 5.0 * a * x * x + 8.0 * a * x - 4.0 * a;
    return 0.0;
}

vec4 bicubic(sampler2D tex, vec2 uv) {
    vec2 texel = vec2(texelSize, texelSize);

    vec2 pixel = uv / texel - vec2(0.5, 0.5);
    vec2 f = fract(pixel);
    pixel = floor(pixel) * texel;

    vec4 samples[4];
    for (int i = 0; i < 4; ++i) {
        float weightY = cubic(f.y - float(i));
        vec2 sampleUV = pixel + vec2(0.0, float(i)) * texel;
        vec4 rowSamples[4];
        for (int j = 0; j < 4; ++j) {
            float weightX = cubic(float(j) - f.x);
            vec2 offset = vec2(float(j), 0.0) * texel;
            rowSamples[j] = texture(tex, sampleUV + offset) * weightX;
        }
        samples[i] = rowSamples[0] + rowSamples[1] + rowSamples[2] + rowSamples[3];
        samples[i] *= weightY;
    }

    return samples[0] + samples[1] + samples[2] + samples[3];
}

void main() {
    FragColor = bicubic(image, TexCoords);
}
)";

// 编译着色器
GLuint compileShader(GLenum type, const char* source) {
    GLuint shader = glCreateShader(type);
    glShaderSource(shader, 1, &source, nullptr);
    glCompileShader(shader);

    // 检查编译错误
    GLint success;
    glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
    if (!success) {
        char infoLog[512];
        glGetShaderInfoLog(shader, 512, nullptr, infoLog);
        std::cerr << "ERROR::SHADER::COMPILATION_FAILED\n" << infoLog << std::endl;
    }

    return shader;
}

// 创建着色器程序
GLuint createShaderProgram() {
    GLuint vertexShader = compileShader(GL_VERTEX_SHADER, vertexShaderSource);
    GLuint fragmentShader = compileShader(GL_FRAGMENT_SHADER, fragmentShaderSource);

    GLuint program = glCreateProgram();
    glAttachShader(program, vertexShader);
    glAttachShader(program, fragmentShader);
    glLinkProgram(program);

    // 检查链接错误
    GLint success;
    glGetProgramiv(program, GL_LINK_STATUS, &success);
    if (!success) {
        char infoLog[512];
        glGetProgramInfoLog(program, 512, nullptr, infoLog);
        std::cerr << "ERROR::PROGRAM::LINKING_FAILED\n" << infoLog << std::endl;
    }

    glDeleteShader(vertexShader);
    glDeleteShader(fragmentShader);

    return program;
}

// 加载RAW图像数据
std::vector<unsigned char> loadRawImage(const char* filePath, int width, int height) {
    std::ifstream file(filePath, std::ios::binary);
    if (!file) {
        std::cerr << "Failed to open RAW file: " << filePath << std::endl;
        return {};
    }

    std::vector<unsigned char> data(width * height * 3); // 假设是RGB格式
    file.read(reinterpret_cast<char*>(data.data()), data.size());
    file.close();

    return data;
}

// 保存结果图像为RAW格式
void saveRawImage(const char* filePath, const std::vector<unsigned char>& data, int width, int height) {
    std::ofstream file(filePath, std::ios::binary);
    if (!file) {
        std::cerr << "Failed to open output RAW file: " << filePath << std::endl;
        return;
    }

    file.write(reinterpret_cast<const char*>(data.data()), data.size());
    file.close();
}

int main() {
    // 初始化OpenGL上下文(使用离屏渲染)
    glfwInit();
    glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE); // 不创建窗口
    GLFWwindow* window = glfwCreateWindow(1, 1, "", nullptr, nullptr);
    glfwMakeContextCurrent(window);
    glewInit();

    // 创建着色器程序
    GLuint shaderProgram = createShaderProgram();
    glUseProgram(shaderProgram);

    // 加载RAW图像数据
    int inputWidth = 2480, inputHeight = 3512; // 输入图像尺寸
    auto inputData = loadRawImage("input.raw", inputWidth, inputHeight);
    if (inputData.empty()) {
        std::cerr << "Failed to load input RAW image." << std::endl;
        return -1;
    }

    // 创建纹理
    GLuint texture;
    glGenTextures(1, &texture);
    glBindTexture(GL_TEXTURE_2D, texture);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, inputWidth, inputHeight, 0, GL_RGB, GL_UNSIGNED_BYTE, inputData.data());

    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

    glUniform1f(glGetUniformLocation(shaderProgram, "texelSize"), 1.0f / inputWidth);

    // 设置帧缓冲区和渲染目标
    int outputWidth = inputWidth*2, outputHeight = inputHeight*2; // 输出图像尺寸
    GLuint framebuffer;
    glGenFramebuffers(1, &framebuffer);
    glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);

    GLuint renderTexture;
    glGenTextures(1, &renderTexture);
    glBindTexture(GL_TEXTURE_2D, renderTexture);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, outputWidth, outputHeight, 0, GL_RGB, GL_UNSIGNED_BYTE, nullptr);
    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, renderTexture, 0);

    if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
        std::cerr << "Framebuffer is not complete!" << std::endl;
        return -1;
    }

    // 设置顶点数据
    float vertices[] = {
        // 位置          // 纹理坐标
        -1.0f,  1.0f,   0.0f, 1.0f,
        -1.0f, -1.0f,   0.0f, 0.0f,
         1.0f, -1.0f,   1.0f, 0.0f,

        -1.0f,  1.0f,   0.0f, 1.0f,
         1.0f, -1.0f,   1.0f, 0.0f,
         1.0f,  1.0f,   1.0f, 1.0f
    };

    GLuint VBO, VAO;
    glGenVertexArrays(1, &VAO);
    glGenBuffers(1, &VBO);

    glBindVertexArray(VAO);

    glBindBuffer(GL_ARRAY_BUFFER, VBO);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

    glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void*)0);
    glEnableVertexAttribArray(0);

    glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void*)(2 * sizeof(float)));
    glEnableVertexAttribArray(1);

    // 渲染到帧缓冲区
    glViewport(0, 0, outputWidth, outputHeight);
    glClear(GL_COLOR_BUFFER_BIT);

    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, texture);
    glUniform1i(glGetUniformLocation(shaderProgram, "image"), 0);

    glBindVertexArray(VAO);
    glDrawArrays(GL_TRIANGLES, 0, 6);

    // 读取渲染结果
    std::vector<unsigned char> outputData(outputWidth * outputHeight * 3);
    glReadPixels(0, 0, outputWidth, outputHeight, GL_RGB, GL_UNSIGNED_BYTE, outputData.data());

    // 保存结果图像
    saveRawImage("output.raw", outputData, outputWidth, outputHeight);

    // 清理资源
    glDeleteVertexArrays(1, &VAO);
    glDeleteBuffers(1, &VBO);
    glDeleteTextures(1, &texture);
    glDeleteTextures(1, &renderTexture);
    glDeleteFramebuffers(1, &framebuffer);
    glDeleteProgram(shaderProgram);

    glfwTerminate();
    return 0;
}

最终结果展示

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

相关文章:

  • 【C++】类和对象(二)默认成员函数之拷贝构造函数、运算符重载
  • IPShocks:行星际激波数据库
  • 机器学习——Bagging、随机森林
  • leetcode day31 453+435
  • 代理服务器中的代理服务器与SSL协议有什么关系?
  • 元宇宙中的“数字护照“:代理IP如何重构虚拟世界的网络规则
  • 无序抓取系列(四)
  • linux的基础命令
  • 微信小程序登录和获取手机号
  • [学习笔记]NC工具安装及使用
  • 【AI News | 20250326】每日AI进展
  • 最大字段和问题 C++(穷举、分治法、动态规划)
  • h5运行在手机浏览器查看控制台信息
  • leetcode41.缺失的第一个正数
  • 数智读书笔记系列025《智能医疗:医学人工智能的未来》
  • Rust安装并配置配置vscode编译器
  • CPP从入门到入土之类和对象Ⅲ
  • UMI-OCR Docker 部署
  • Python:计算机二级:简单应用
  • g对象在flask中主要是用来实现什么
  • 成就彼此,照亮世界:“中欧建交50周年论坛”在沪成功举行
  • 宇数科技王兴兴:第一桶金来自上海,欢迎上海的年轻人加入
  • 新城市志|上海再攻坚,营商环境没有最好只有更好
  • 市自规局公告收回新校区建设用地,宿迁学院:需变更建设主体
  • 夜读丨古代有没有近视眼?
  • Meta正为AI眼镜开发人脸识别功能