对于png透明图片,在QQ,贴吧等平台上,看缩略图时默认是以白色为背景,而点开看原图时默认是以黑色为背景,基于此显示逻辑可以实现一种图像,看缩略图时是表图,看原图时是里图,这就是幻影坦克。
基于图像线性融合原理,公式:g(x)=α*f(x)+(1-α)h(x),可以假设最后生成图与白色叠加得到表图,最后生成图与黑色叠加得到里图,套用公式即可得到幻影坦克的理论公式:
公式和原理都源自B站视频,BV1kV411v7fR,讲的很细致。
需要注意的是,公式里α透明度有一项是表图的灰度值减里图灰度值,必须保证这个值时刻大于0,不然会出现α值大于255的情况,造成错误,即里图显示时出现表图部分,如图:
在我把背景换成橙色以后,图中错误部分也显示为橙色,所以判断为这些地方透明度计算出现问题。
经过初步分析得到:显示错误的地方在原图中的灰度值非常低,导致P表-P里小于0,使α超出上限,造成完全透明。
如何保证P表-P里大于0?我的想法是里图调暗一些,最后保证表图每一像素的灰度大于里图灰度即可。
经过一段时间的调试,我得到了我的解决办法:以里图尺寸为基准,遍历全部像素点,分别读取表图和里图的灰度,将读取到的里图灰度乘0.35,灰度的范围是0-255,因此最大值255*0.35=89.25。对于表图的灰度,如果出现小于上面这个值的灰度,就给他强行赋值为100,这样一定能够保证P表-P里大于0,α正常,里图显示正常。
可能会有人问:改变了表图灰度,会不会对最终结果造成影响?这个问题我还不清楚,但是根据生成的幻影坦克来看,这点影响可以忽略不计。
希望大神指点更好的方法以保证α不出问题。
最后附上源码
#include<opencv2/opencv.hpp>
#include<iostream>using namespace std;
using namespace cv;int main()
{Mat A_origin = imread("A.jpg", 0);Mat A;Mat B = imread("B.jpg",0); resize(A_origin, A, B.size()); //以里图尺寸resize表图Mat C = Mat::zeros(B.size(), CV_8UC4); //以里图尺寸为基础创建新图int PA, PB, PC;int Alpha;for (int i = 0; i < B.rows; i++){for (int j = 0; j < B.cols; j++){PA = (int)A.at<uchar>(i, j);PB = ((int)B.at<uchar>(i, j)) * 0.35;if (PA < 100) //解决问题的关键{PA = 100;}Alpha = 255 - (PA - PB); if (Alpha == 0) {Alpha = 1;}PC = (int)(255 * PB / Alpha);C.at<Vec4b>(i, j) = Vec4b(PC, PC, PC, Alpha);}}imwrite("Mirage Tank.png", C);}