我正在加载具有透明度平面的PNG图像。转换为灰度时,图像中的透明区域显示为黑色,这似乎是默认背景。我需要他们变成白色。我能做什么 ?使用透明度设置PNG的背景色
[这不是关于如何保留透明度通常的问题。]
我正在加载具有透明度平面的PNG图像。转换为灰度时,图像中的透明区域显示为黑色,这似乎是默认背景。我需要他们变成白色。我能做什么 ?使用透明度设置PNG的背景色
[这不是关于如何保留透明度通常的问题。]
如果你阅读imread
一个PNG没有通过IMREAD_UNCHANGED
那么你将有一个3通道BGR图像。 如果有第四个alpha通道(0 =完全透明,255 =完全可见),那么会被剪裁作为文档put it。
由于像素的BGR部分为黑色,因此您会得到透明像素的黑色像素。 (Vec3b(0, 0, 0)
)。
如果你不相信,尝试打开为BGR(imread
wihout IMREAD_UNCHANGED
参数)和显示器(imshow
然后waitkey
以下两个图像:
虽然他们看起来这个页面上相似或者在Gimp中,第一个应该有黑色背景,而第二个应该有红色背景。
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgcodecs.hpp>
int main(int argc, char** argv) {
cv::Mat img_4_channels;
img_4_channels = cv::imread(argv[1], cv::IMREAD_UNCHANGED); // gives 8UC4
// img_4_channels = cv::imread(argv[1]); // inappropriate: gives 8UC3
cv::Mat background = cv::Mat(img_4_channels.size(), CV_8UC3, cv::Vec3b(255, 255, 255)); // white background
overlayImage(background, img_4_channels, img_3_channels, cv::Point2i(0, 0));
cv::imshow("3 channels", img_3_channels);
}
该解决方案更轻巧(没有前景的坐标,无需分配一个背景图像)。
最有效的方法(内存和CPU)是让libPNG做到这一点,利用png_set_background
:
如果你不需要,或无法处理,alpha通道,你可以调用 png_set_background()通过与固定的 颜色合成来删除它。不要调用png_set_strip_alpha()来执行此操作 - 它会在此图像的透明部分留下 虚假像素值。
png_set_background(png_ptr, &background_color, PNG_BACKGROUND_GAMMA_SCREEN, 0, 1);
的BACKGROUND_COLOR是根据 数据格式的libpng为你会产生一个RGB或灰度值。
不幸的是,周围的libpng OpenCV的包装不使用它,所以你必须要修补自己的一些初步支持(通过其他选项imread
能力有限阻碍)。
其他可能的方法是使用libPNG编写自己的简单图像加载器来实现这个特定目的。
如果您可以承担一些浪费,请将其作为BGRA加载,然后执行一些后期处理。不过,我会比Gabriel中提到的代码更进一步,并将颜色转换纳入其中。
void remove_transparency(cv::Mat const& source
, cv::Mat& destination
, uint8_t background_color)
{
CV_Assert(source.type() == CV_8UC4);
destination.create(source.rows, source.cols, CV_8UC1);
auto it_src(source.begin<cv::Vec4b>()), it_src_end(source.end<cv::Vec4b>());
auto it_dest(destination.begin<uint8_t>());
std::transform(it_src, it_src_end, it_dest
, [background_color](cv::Vec4b const& v) -> uchar
{
// Conversion constants taken from cvtColor docs...
float gray(v[0] * 0.114f + v[1] * 0.587f + v[2] * 0.299f);
float alpha(v[3]/255.0f);
return cv::saturate_cast<uchar>(gray * alpha + background_color * (1 - alpha));
}
);
}
当然,这仍然是单线程的,因此让我们利用cv::parallel_for_
到位进一步完善它。
class ParallelRemoveTransparency
: public cv::ParallelLoopBody
{
public:
ParallelRemoveTransparency(cv::Mat const& source
, cv::Mat& destination
, uint8_t background_color)
: source_(source)
, destination_(destination)
, background_color_(background_color)
{
CV_Assert(source.size == destination.size);
}
virtual void operator()(const cv::Range& range) const
{
cv::Mat4b roi_src(source_.rowRange(range));
cv::Mat1b roi_dest(destination_.rowRange(range));
std::transform(roi_src.begin(), roi_src.end(), roi_dest.begin()
, [this](cv::Vec4b const& v) -> uint8_t {
float gray(v[0] * 0.114f + v[1] * 0.587f + v[2] * 0.299f);
float alpha(v[3]/255.0f);
return cv::saturate_cast<uint8_t>(gray * alpha + background_color_ * (1 - alpha));
}
);
}
private:
cv::Mat const& source_;
cv::Mat& destination_;
uint8_t background_color_;
};
void remove_transparency(cv::Mat const& source
, cv::Mat& destination
, uint8_t background_color)
{
CV_Assert(source.type() == CV_8UC4);
destination.create(source.rows, source.cols, CV_8UC1);
ParallelRemoveTransparency parallel_impl(source, destination, background_color);
cv::parallel_for_(cv::Range(0, source.rows), parallel_impl);
}
事实证明,你需要这个Python编写的。下面是一个替代方案的简要草案:
import numpy as np
import cv2
def remove_transparency(source, background_color):
source_img = cv2.cvtColor(source[:,:,:3], cv2.COLOR_BGR2GRAY)
source_mask = source[:,:,3] * (1/255.0)
background_mask = 1.0 - source_mask
bg_part = (background_color * (1/255.0)) * (background_mask)
source_part = (source_img * (1/255.0)) * (source_mask)
return np.uint8(cv2.addWeighted(bg_part, 255.0, source_part, 255.0, 0.0))
img = cv2.imread('smile.png', -1)
result = remove_transparency(img, 255)
cv2.imshow('', result)
cv2.waitKey()
感谢所有有价值的信息。我在Python下工作,所以实现这个有点不可能,所以我正在寻找更多的OpenCVish解决方案。 –
啊,我明白了。问题中没有语言标记,因此根据您的个人资料,我认为您需要C++ :) –
@YvesDaoust添加了Python解决方案。不知道你可以轻松做得更好。 –
透明平面是否均匀? (即alpha组件可以有多少个值?两个?)第一个想法:[在白色背景图像上绘制图像](https://stackoverflow.com/questions/20957433/how-to-draw-a-transparent- image-over-live-camera-feed-in-opencv#20958245),然后转换为灰度。第二个想法:首先转换为灰度,然后遍历每个像素,然后在新图像中变为白色所有像素都在原始图像中具有[alpha分量](https://stackoverflow.com/questions/18491998/creating-transparent-image -in-opencv)低于给定的阈值。 –
@ GabrielDevillers:其实我不知道。我正在读取PNG并获取黑色像素的灰度图像。我没有看到阿尔法飞机。 –
@YvesDaoust你如何加载图片?根据你的描述,我猜是灰度。你需要用'IMREAD_UNCHANGED'加载它,以便获得完整的4通道图像,并对其进行一些后期处理。 –