我有2个Mat对象,overlay
和background
。
如何将overlay
垫放在background
垫的顶部,以便只有overlay
垫的非透明像素完全遮盖background
垫?
我尝试了addWeighted()
,它结合了2 Mat但是两个"层"仍然可见。
overlay
垫具有透明通道,而background
垫则没有。overlay
Mat中的像素要么完全透明,要么完全模糊。答案 0 :(得分:1)
函数addWeighted
将不起作用,因为它将对所有像素使用相同的alpha值。要完全按照你的意思去做,只替换背景中的非透明值,你可以为它创建一个小函数,如下所示:
cv::Mat blending(cv::Mat& overlay, cv::Mat& background){
//must have same size for this to work
assert(overlay.cols == background.cols && overlay.rows == background.rows);
cv::Mat result = background.clone();
for (int i = 0; i < result.rows; i++){
for (int j = 0; j < result.cols; j++){
cv::Vec4b pix = overlay.at<cv::Vec4b>(i,j);
if (pix[3] == 0){
result.at<cv::Vec3b>(i,j) = cv::Vec3b(pix[0], pix[1], pix[2]);
}
}
}
return result;
}
我不确定opencv中的透明值是0还是255,所以相应地改变它....我认为对于非透明的adn 255来说它是完全透明的。
如果您想使用Alpha通道的值作为混合比率,请稍微更改一下:
cv::Mat blending(cv::Mat& overlay, cv::Mat& background){
//must have same size for this to work
assert(overlay.cols == background.cols && overlay.rows == background.rows);
cv::Mat result = background.clone();
for (int i = 0; i < result.rows; i++){
for (int j = 0; j < result.cols; j++){
cv::Vec4b pix = overlay.at<cv::Vec4b>(i,j);
double alphaRate = 1.0 - pix[3]/255.0;
result.at<cv::Vec3b>(i,j) = (1.0 - alphaRate) * cv::Vec3b(pix[0], pix[1], pix[2]) + result.at<cv::Vec3b>(i,j) * alphaRate;
}
}
return result;
}
很抱歉代码是用C ++而不是JAVA,但我想你可以得到一个想法。基本上只是像素中的一个循环,如果它们不透明,则将背景副本中的像素更改为覆盖的像素。
*编辑*
我将通过此编辑回答您的评论,因为它可能需要空间。问题是OpenCV矩阵的工作原理。对于具有alpha的图像,数据被组织为类似BGRA BGRA .... BGRA
的数组,并且基本操作(如add,multiply等)在具有相同尺寸的矩阵中工作.....你总是可以尝试将有分裂的矩阵(这将重写矩阵,因此它可能很慢),然后将alpha通道更改为double(再次,重写),然后进行乘法和矩阵的加法。它应该更快,因为OpenCV优化了这些功能....你也可以在GPU中做到这一点....
这样的事情:
cv::Mat blending(cv::Mat& overlay, cv::Mat& background){
std::vector<cv::Mat> channels;
cv::split(overlay, channels);
channels[3].convertTo(channels[3], CV_64F, 1.0/255.0);
cv::Mat newOverlay, result;
cv::merge(channels, newOverlay);
result = newOverlay * channels[3] + ((1 - channels[3]) * background);
return result;
}
不确定OpenCV是否允许CV_8U
乘以CV_64F
,或者这是否会更快......但可能会更快。
另外,带循环的那些在线程中没有问题,所以它可以被优化...在发布模式下运行它将大大提高速度,因为OpenCV的.at
函数做了几个断言......在发布模式下没有完成。不确定这是否可以在JAVA中改变...
答案 1 :(得分:0)
我能够移植api55编辑的java答案:
private void merge(Mat background, Mat overlay) {
List<Mat> backgroundChannels = new ArrayList<>();
Core.split(background, backgroundChannels);
List<Mat> overlayChannels = new ArrayList<>();
Core.split(overlay, overlayChannels);
// compute "alphaRate = 1 - overlayAlpha / 255"
Mat overlayAlphaChannel = overlayChannels.get(3);
Mat alphaRate = new Mat(overlayAlphaChannel.size(), overlayAlphaChannel.type());
Core.divide(overlayAlphaChannel, new Scalar(255), alphaRate);
Core.absdiff(alphaRate, new Scalar(1), alphaRate);
for (int i = 0; i < 3; i++) {
// compute "(1 - alphaRate) * overlay"
Mat overlayChannel = overlayChannels.get(i);
Mat temp = new Mat(alphaRate.size(), alphaRate.type());
Core.absdiff(alphaRate, new Scalar(1), temp);
Core.multiply(temp, overlayChannel, overlayChannel);
temp.release();
// compute "background * alphaRate"
Mat backgroundChannel = backgroundChannels.get(i);
Core.multiply(backgroundChannel, alphaRate, backgroundChannel);
// compute the merged channel
Core.add(backgroundChannel, overlayChannel, backgroundChannel);
}
alphaRate.release();
Core.merge(backgroundChannels, background);
}
与双嵌套循环计算相比,它快得多。