如何使用 OpenCV C++ 将 24 位深度的 PNG 图像转换为 8 位深度的 PNG 图像

Posted

技术标签:

【中文标题】如何使用 OpenCV C++ 将 24 位深度的 PNG 图像转换为 8 位深度的 PNG 图像【英文标题】:How to Convert 24 to 8 bit depth PNG Image using OpenCV C++ 【发布时间】:2019-02-22 03:07:24 【问题描述】:

我想将 24 位 png 图像转换为 8 位 png 图像

我尝试了几种方法,但都失败了。

我想将彩色 24 位 png_images 转换为 彩色 8 位 png_images

但是,如果我尝试转换为 8 位图像,会变成灰度。

我想使用 imwrite()。 但什么都无所谓。

以下是我的完整代码。

#include <oppencv2/core.hpp>
#include <opencv2/imgcodecs.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp> //for resize
#include <iostream>
#include <string>
#include <vector>
#include <sstream>
#include <stdio.h>

using namespace cv;
using namespace std;

void overlayImage(const Mat &background, const Mat &foreground, Mat &output, 
Point2i location);

int main(int argc, char** argv)


    Mat image_background;
    Mat black_background;
    Mat image_target, image_segmentation;

    image_target = imread("panda.png", IMREAD_UNCHANGED);   //  Transparent PNG


    image_segmentation = imread("panda_segmentation_stroke.png", IMREAD_UNCHANGED);

    string filename, filename2;

    vector<String> fn;

    glob("C:\\Users\\IMRC\\source\\repos\\OpenCVProject\\OpenCVProject\\background\\*.jpg", fn, false);

    size_t count = fn.size();
    cout << "Image Size " << count << "\n";

    float MIN_SIZE = 0.3;
    float MAX_SIZE = 0.8;

    float WIDTH = 300;
    float HEIGHT = 400;
    float SIZE_WIDTH, SIZE_HEIGHT, Point_x, Point_y;  // random size and point 


    string JPEGImagesPath = "C:\\Users\\IMRC\\DESKTOP\\TEST\\JPEGImages\\2019-";
    string SEG_ImagesPath = "C:\\Users\\IMRC\\DESKTOP\\TEST\\SegmentationClass\\2019-";

    srand(static_cast <unsigned> (time(0)));

    black_background = imread(fn[0], IMREAD_COLOR);
    resize(black_background, black_background, Size(500, 500));

    for (size_t i = 0; i < count; i++) 
        cout << fn[i] << "\n";

        image_background = imread(fn[i], IMREAD_COLOR);                           
        black_background.setTo(Scalar(0, 0, 0));

        resize(image_background, image_background, Size(500,500));                    // background image resize

        Mat image_resize_target;
        Mat image_resize_segmentation;


        SIZE_WIDTH = MIN_SIZE + static_cast <float> (rand()) /( static_cast <float> (RAND_MAX / (MAX_SIZE - MIN_SIZE)));
        SIZE_HEIGHT = MIN_SIZE + static_cast <float> (rand()) / (static_cast <float> (RAND_MAX / (MAX_SIZE - MIN_SIZE)));

        Point_x = static_cast <float> (rand()) / (static_cast <float> (RAND_MAX / WIDTH));
        Point_y = static_cast <float> (rand()) / (static_cast <float> (RAND_MAX / HEIGHT));

        resize(image_target, image_resize_target, Size(), SIZE_WIDTH, SIZE_HEIGHT);                
        resize(image_segmentation, image_resize_segmentation, Size(), SIZE_WIDTH, SIZE_HEIGHT);

        overlayImage(image_background, image_resize_target, image_background, cv::Point(Point_x, Point_y));
        overlayImage(black_background, image_resize_segmentation, black_background, cv::Point(Point_x, Point_y));


        stringstream JPEGImages, SEG_Images, SEG_RawImage;
        JPEGImages   << JPEGImagesPath    << i + 1 << ".jpg";
        SEG_Images   << SEG_ImagesPath    << i + 1 << ".png";

        filename = JPEGImages.str();
        imwrite(filename, image_background);  // save JPEGImages

        filename2 = SEG_Images.str();   
        imwrite(filename2, black_background); // save SegmentationClass

    

    return 0;


void overlayImage(const Mat &background, const Mat &foreground, Mat &output, Point2i location)

    background.copyTo(output);

    // start at the row indicated by location, or at row 0 if location.y is negative.
    for (int y = std::max(location.y, 0); y < background.rows; ++y)
    
    int fY = y - location.y;   // because of the translation

    if (fY >= foreground.rows) // we are done of we have processed all rows of the foreground image.
        break;

    // start at the column indicated by location, 

    // or at column 0 if location.x is negative.
    for (int x = std::max(location.x, 0); x < background.cols; ++x)
    
        int fX = x - location.x;    // because of the translation.

        if (fX >= foreground.cols)  // we are done with this row if the column is outside of the foreground image.
            break;

        // determine the opacity of the foregrond pixel, using its fourth (alpha) channel.
        double opacity = ((double)foreground.data[fY * foreground.step + fX * foreground.channels() + 3]) / 255.;
            // and now combine the background and foreground pixel, using the opacity, 

            // but only if opacity > 0.
            for (int c = 0; opacity > 0 && c < output.channels(); ++c)
            
                unsigned char foregroundPx = foreground.data[fY * foreground.step + fX * foreground.channels() + c];
                unsigned char backgroundPx = background.data[y * background.step + x * background.channels() + c];
                output.data[y*output.step + output.channels()*x + c] = backgroundPx * (1. - opacity) + foregroundPx * opacity;
            
        
    

这段代码的目的是合成。

准备好背景图片和另一个png_images,并导出合成的图片。

我想将此图像打印为 8 位彩色 png 图像。

如何修改源代码?

添加图片 enter image description here

【问题讨论】:

24-bit,指的是24-bit pixel size8 bits per color channel 您的意思是带有调色板的单通道图像(例如PNG格式允许)?不能用OpenCV写那些,因为你在C++,也许直接使用libpng是要走的路。 您想要的过程称为抖动,快速搜索 OpenCV 文档并没有找到任何东西——显然没有提供这个常用功能。您需要在其他地方搜索,*** 上甚至有几个答案。 看看这里...***.com/a/54906864/2836621 【参考方案1】:

您可以使用Mat::convertTo 函数来更改cv::Mat 的类型。我假设您要转换为 8 位图像的图像类型为 CV_32SC3(如果您有 alpha 通道,则为 CV_32SC4)。即使我的猜测不正确,您也可以使用cv::Mat::type() 学习正确的图像类型。然后,您可以使用上面的第一个函数将您的图像转换为CV_8UC3。请注意,转换函数接受比例因子alpha。这应该正确设置,否则会导致整数溢出。您可以根据cv::Mat::type() 为您提供的内容找出正确的比例因子。希望这会有所帮助!

编辑:您可以检查type() 的结果是什么意思here。

cv::imwrite 的概要说,您可以调整的唯一参数是写入PNG 文件时的图像质量。更改 OpenCV 图像的通道数是设置我们上面已经讨论过的图像属性的唯一第二种方法。因此,只有使用调色板才能获得 8 位 color PNG。检查libpng 的文档,上面写着If you are writing an indexed colors image, you should provide a palette (colormap)。

【讨论】:

感谢您的回答。我使用cout &lt;&lt; black_background.type()得到了16的结果,我使用black_background.convertTo(Output, CV_8UC3, 1);提取图像。但结果是一样的。这是我第一次使用 Opencv 项目。所以,太难了。给我更多帮助。 我在答案中添加了一个链接。显然,您的图像具有三个通道并且已经是 8 位分辨率。您能否更清楚地了解 8 位 PNG 的含义? 哦,我很抱歉。我添加了一张图片。在我国的语言中,它只是作为图像位写的。当我将语言切换为英语进行捕获时,它是位深度。我该怎么办?

以上是关于如何使用 OpenCV C++ 将 24 位深度的 PNG 图像转换为 8 位深度的 PNG 图像的主要内容,如果未能解决你的问题,请参考以下文章

使用 C++ 在 OpenCV 中按深度维度排序

OpenCV 拼接,C++ - 未处理的异常

如何将 Mat 重塑为张量以在 C++ 中的深度神经网络中使用?

OpenCV 拼接 RGB 和深度(英特尔实感)

如何在 MacOS 上使用 OpenCV 链接 C++ 程序 [重复]

C++:OpenCV:快速像素迭代