自学内容网 自学内容网

opencv Mat To Heif

高效率图像文件格式(英语:High Efficiency Image File Format, HEIF;也称高效图像文件格式)是一个用于单张图像或图像序列的文件格式。它由运动图像专家组(MPEG)开发,并在MPEG-H Part 12(ISO/IEC 23008-12)中定义。
HEIF规范也定义了高效率视频编码(HEVC)编码的内嵌图像和HEVC编码的图像序列的存储方式,其中以受约束的方式应用帧间预测。
HEIF文件与ISO基本媒体文件格式(ISOBMFF,ISO/IEC 14496-12)兼容,并且还可以包括其他媒体流,例如定时的文本和音频。

环境配置:
相关资源:https://github.com/strukturag/libheif
Windows(最简单、快捷方式,自行编译容易出错)
You can build and install libheif using the vcpkg dependency manager:

git clone https://github.com/Microsoft/vcpkg.git
cd vcpkg
./bootstrap-vcpkg.bat
./vcpkg integrate install
./vcpkg install libheif

Linux/macOS(或参考上述相关资源链接)
1. Install dependencies with Homebrew
brew install cmake make pkg-config x265 libde265 libjpeg libtool
2. Configure and build project (–preset argument needs CMake >= 3.21):

    mkdir build
    cd build
    cmake --preset=release ..
    ./configure
    make

功能:cv::Mat 转 HEIF文件, HEIF文件转cv::Mat


```cpp
#include <libheif/heif.h>
#include <opencv2/opencv.hpp>
#include <iostream>

bool RGBMatToHeif(cv::Mat& mat, const char* filename)
{
        if (mat.empty() || mat.type() != CV_8UC3)
        {
                std::cerr << "Invalid Mat format. Expected 8UC3." << std::endl;
                return false;
        }

        struct heif_image* image;
        struct heif_context* ctx = heif_context_alloc();
        struct heif_error err;

        int width = mat.cols;
        int height = mat.rows;

        // 创建HEIF图像
        err = heif_image_create(width, height, heif_colorspace_RGB, heif_chroma_interleaved_RGB, &image);
        if (err.code != heif_error_Ok)
        {
                std::cerr << "Error adding plane to HEIF image:" << err.message << std::endl;
                heif_image_release(image);
                heif_context_free(ctx);
                return false;
        }

        // 添加一个用于交错 RGB 通道的图像平面
        err = heif_image_add_plane(image, heif_channel_interleaved, width, height, 8);
        if (err.code != heif_error_Ok) {
                std::cerr << "Error adding plane to HEIF image: " << err.message << std::endl;
                heif_image_release(image);
                heif_context_free(ctx);
                return false;
        }


        // 获取指向HEIF图像数据的指针
        int stride;
        uint8_t* p = heif_image_get_plane(image, heif_channel_interleaved, &stride);

        // 将 cv::Mat 数据复制到 HEIF 图像中
        for (int y = 0; y < height; y++) {
                memcpy(p + y * stride, mat.ptr(y), width * 3);  // 每行的像素数据都是 width * 3 个字节
        }


        //for (int y = 0; y < height; y++) {
        //        // 使用 size_t 来安全地计算偏移量  
        //        size_t rowOffset = static_cast<size_t>(y) * stride;
        //        // 将偏移量转换为 char* 类型的指针偏移  
        //        char* rowPtr = reinterpret_cast<char*>(p) + rowOffset;
        //        // 使用 memcpy 复制数据  
        //        memcpy(rowPtr, mat.ptr<uchar>(y), width * 3);  // 注意:这里假设 mat.ptr 返回 uchar*  
        //        // 注意:如果 mat.ptr 实际上返回的是其他类型的指针(如 RGB 像素的 struct 或类),  
        //        // 你可能需要调整 memcpy 的第三个参数来匹配实际的字节大小。  
        //}

         创建并初始化heif_encoding_options结构体
        //struct heif_encoding_options encoding_options = { 0 };
        //encoding_options.version = 0;
        //encoding_options.save_alpha_channel = false;
        //encoding_options.image_orientation = heif_orientation_normal; // 例如:设置图像方向为正常
        
        // 创建HEIF编码器
        struct heif_encoder* encoder;

        err = heif_context_get_encoder_for_format(ctx, heif_compression_HEVC, &encoder);
        if (err.code != heif_error_Ok)
        {
                std::cerr << "Error getting HEIF encoder: " << err.message << std::endl;
                heif_image_release(image);
                heif_context_free(ctx);
                return false;
        }

        heif_encoder_set_lossy_quality(encoder, 80);  //0-100 压缩比例
        heif_encoder_set_lossless(encoder, true);     // True / False
        heif_encoder_set_logging_level(encoder, 4);   // log信息详细程度0-4

        err = heif_context_encode_image(ctx, image, encoder, nullptr, nullptr);
        if (err.code != heif_error_Ok) {
                std::cerr << "Error encoding HEIF image: " << err.message << std::endl;
                heif_encoder_release(encoder);
                heif_image_release(image);
                heif_context_free(ctx);
                return false;
        }

        err = heif_context_write_to_file(ctx, filename);

        if (err.code != heif_error_Ok) {
                std::cerr << "Error writing HEIF file: " << err.message << std::endl;
                heif_encoder_release(encoder);
                heif_image_release(image);
                heif_context_free(ctx);
                return false;
        }

        heif_encoder_release(encoder);
        heif_image_release(image);
        heif_context_free(ctx);

        return true;
}

bool GRAYMatToHeif(cv::Mat& mat, const char* filename)
{
        // 确保输入的cv::Mat 是8位深度,单通道的灰度图
        if (mat.empty() || mat.type() != CV_8UC1) {
                std::cerr << "Invalid Mat format. Expected 8UC1." << std::endl;
                return false;
        }

        struct heif_image* image;
        struct heif_context* ctx = heif_context_alloc();
        struct heif_error err;

        int width = mat.cols;
        int height = mat.rows;

        // 创建HEIF图像
        err = heif_image_create(width, height, heif_colorspace_monochrome, heif_chroma_monochrome, &image);
        if (err.code != heif_error_Ok) {
                std::cerr << "Error creating HEIF image: " << err.message << std::endl;
                heif_context_free(ctx);
                return false;
        }

        // 添加一个用于灰度通道的图像平面
        err = heif_image_add_plane(image, heif_channel_Y, width, height, 8);
        if (err.code != heif_error_Ok) {
                std::cerr << "Error adding plane to HEIF image: " << err.message << std::endl;
                heif_image_release(image);
                heif_context_free(ctx);
                return false;
        }

        // 获取指向HEIF图像灰度数据的指针
        int stride;
        uint8_t* p = heif_image_get_plane(image, heif_channel_Y, &stride);

        // 将cv::Mat 的灰度数据复制到HEIF图像中
        for (int y = 0; y < height; y++) {
                memcpy(p + y * stride, mat.ptr(y), width);  // 每行的像素数据是 width 个字节
        }

        // 创建HEIF编码器
        struct heif_encoder* encoder;
        err = heif_context_get_encoder_for_format(ctx, heif_compression_HEVC, &encoder);
        if (err.code != heif_error_Ok) {
                std::cerr << "Error getting HEIF encoder: " << err.message << std::endl;
                heif_image_release(image);
                heif_context_free(ctx);
                return false;
        }

        heif_encoder_set_lossy_quality(encoder, 80);  //0-100 压缩比例
        heif_encoder_set_lossless(encoder, true);     // True / False
        heif_encoder_set_logging_level(encoder, 4);   // log信息详细程度0-4

        err = heif_context_encode_image(ctx, image, encoder, nullptr, nullptr);
        if (err.code != heif_error_Ok) {
                std::cerr << "Error encoding HEIF image: " << err.message << std::endl;
                heif_encoder_release(encoder);
                heif_image_release(image);
                heif_context_free(ctx);
                return false;
        }

        err = heif_context_write_to_file(ctx, filename);

        if (err.code != heif_error_Ok) {
                std::cerr << "Error writing HEIF file: " << err.message << std::endl;
                heif_encoder_release(encoder);
                heif_image_release(image);
                heif_context_free(ctx);
                return false;
        }

        heif_encoder_release(encoder);
        heif_image_release(image);
        heif_context_free(ctx);

        return true;

}

cv::Mat HeifToRGBMat(const char* filename)
{
        // 创建HEIF上下文
        heif_context* ctx = heif_context_alloc();
        heif_error error = heif_context_read_from_file(ctx, filename, nullptr);
        if (error.code != heif_error_Ok)
        {
                std::cerr << "读取heif文件失败:" << error.message << std::endl;
                heif_context_free(ctx);
                return cv::Mat();
        }

        // 获取图像句柄
        heif_image_handle* handle;
        error = heif_context_get_primary_image_handle(ctx, &handle);
        if (error.code != heif_error_Ok)
        {
                std::cerr << "获取图像句柄失败:" << error.message << std::endl;
                heif_context_free(ctx);
                return cv::Mat();
        }

        // 解码图像
        heif_image* image;
        error = heif_decode_image(handle, &image, heif_colorspace_RGB, heif_chroma_interleaved_RGB, nullptr);
        if (error.code != heif_error_Ok)
        {
                std::cerr << "解码图像失败:" << error.message << std::endl;
                heif_image_handle_release(handle);
                heif_context_free(ctx);
                return cv::Mat();
        }

        // 获取图像宽度、高度和数据
        int width = heif_image_get_width(image, heif_channel_interleaved);
        int height = heif_image_get_height(image, heif_channel_interleaved);
        int stride;
        const uint8_t* data = heif_image_get_plane_readonly(image, heif_channel_interleaved, &stride);

        // 将图像数据复制到cv::Mat
        cv::Mat mat(height, width, CV_8UC3);
        for (int y = 0; y < height; y++) {
                memcpy(mat.ptr(y), data + y * stride, width * 3);
        }

        cv::cvtColor(mat, mat, cv::COLOR_BGR2RGB);

        //释放资源
        heif_image_release(image);
        heif_image_handle_release(handle);
        heif_context_free(ctx);

        return mat;
}

cv::Mat HeifToGRAYMat(const char* filename)
{
        // 创建HEIF上下文
        heif_context* ctx = heif_context_alloc();
        heif_error error = heif_context_read_from_file(ctx, filename, nullptr);
        if (error.code != heif_error_Ok) {
                std::cerr << "读取HEIF文件失败: " << error.message << std::endl;
                heif_context_free(ctx);
                return cv::Mat();
        }

        // 获取第一个图像句柄
        heif_image_handle* handle;
        error = heif_context_get_primary_image_handle(ctx, &handle);
        if (error.code != heif_error_Ok) {
                std::cerr << "获取图像句柄失败:" << error.message << std::endl;
                heif_context_free(ctx);
                return cv::Mat();
        }

        // 解码图像
        heif_image* image;
        error = heif_decode_image(handle, &image, heif_colorspace_monochrome, heif_chroma_monochrome, nullptr);
        if (error.code != heif_error_Ok) {
                std::cerr << "解码图像失败: " << error.message << std::endl;
                heif_image_handle_release(handle);
                heif_context_free(ctx);
                return cv::Mat();
        }

        // 获取图像宽度、高度和数据
        int width = heif_image_get_width(image, heif_channel_Y);
        int height = heif_image_get_height(image, heif_channel_Y);
        int stride;
        const uint8_t* data = heif_image_get_plane_readonly(image, heif_channel_Y, &stride);

        // 将图像数据复制到 cv::Mat
        cv::Mat mat(height, width, CV_8UC1);
        for (int y = 0; y < height; y++) {
                memcpy(mat.ptr(y), data + y * stride, width);
        }

        cv::imwrite("C:\\Users\\Lenovo\\Desktop\\opencv_decode.png", mat);

        // 释放资源
        heif_image_release(image);
        heif_image_handle_release(handle);
        heif_context_free(ctx);

        return mat;
}

int main()
{
    // RGB
    cv::Mat matRGB;
    cv::Mat matBGR = cv::imread("C:\\Users\\Lenovo\\Desktop\\opencv.png");
    const char* rgbFilename = "C:\\Users\\Lenovo\\Desktop\\opencv.heif";
    const char* dstRgbFilename = "C:\\Users\\Lenovo\\Desktop\\opencv_decode.png";

    cv::cvtColor(matBGR, matRGB, cv::COLOR_BGR2RGB);
    bool isRGB = RGBMatToHeif(matRGB, rgbFilename);

    matBGR = HeifToRGBMat(rgbFilename);
    cv::imwrite(dstRgbFilename, matBGR);


    // GRAY
    cv::Mat matGRAY = cv::imread("C:\\Users\\Lenovo\\Desktop\\opencv_gray.png", cv::IMREAD_GRAYSCALE);
    const char* grayFilename = "C:\\Users\\Lenovo\\Desktop\\opencv_gray.heif";
    const char* dstGrayFilename = "C:\\Users\\Lenovo\\Desktop\\opencv_gray_decode.png";

    bool isGRAY = GRAYMatToHeif(matGRAY, grayFilename);


    cv::Mat dstMatGRAY = HeifToGRAYMat(grayFilename);

    cv::imwrite(dstGrayFilename, dstMatGRAY);


    return 0;
}

原文地址:https://blog.csdn.net/weixin_50918736/article/details/144226320

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!