仿照“全能扫描王”的图像增强-由原理到实现

一、算法目标:
实现这种背景去除增强的效果,特别是在“全能扫描王”中该算法得到了典型的应用。
二、使用PS进行模拟
图像处理算法很多时候就是对成熟经验的模拟和复现。首先我们来看PS下的处理。
1.手机拍一张需要电子版的纸质文件:
2.打开PS,复制背景,得到图层1:
 
3.对图层1使用滤镜:高斯模糊,半径100像素:
 
4.改变图层1混合模式:划分:
5.添加曲线调整层,压暗文字:
 
6.合并可见图层。
7.柔角画笔擦掉彩色噪点和边缘露出的拍摄背景:
那么,这个效果的确实很好的,关键问题就是“划分”是什么操作?
PS中划分模式的计算公式: 结果色 = (基色 / 混合色) * 255
分析每个颜色通道的数值,并用基色分割混合色 ,基色数值大于或等于混合色数值,混合出的颜色为白色。基色数值小于混合色,结果色比基色更暗。因此结果色对比非常强。两个一样的图层(复制基色图层),图层混合模式改为划分结果色就是白色混合色为白色,结果色就是基色;混合色为黑色,结果色就是白色;公式验证:r值(255/79)*255=823;结果色大于255,系统就默认为最大值255;以公式推断基色数值大于或等于混合色数值,混合出的颜色为白色。

 

 

三、整理流程,转换为代码
图像A,B,为同一场景在不一样的光照拍摄图片,那么:
光照分布 L = A / B
如果已知 A, L ,则 B = A / L (B 为A去光照的结果)
这里L约等于 gaussFilter(A, 大核)
再进一步转换为代码:
 
    Mat src = imread("t1.jpeg");
    src.convertTo(src,CV_32FC3,1.0/255);
    Mat gauss;
    Mat dst = src.clone();
    cv::GaussianBlur(src,gauss,Size(101,101),0);
    
    //划分
    //如果混合色与基色相同则结果色为白色
    //如混合色为白色则结果色为基色不变
    //如混合色为黑色则结果色为白色
    dst = src/gauss;
    waitKey();
 

在这个基础上,进一步进行封装:
//USM图像增
void ImageSharp(Mat &src,Mat &dst,int nAmount = 1000)
{
    double sigma = 3;  
    int threshold = 1;  
    float amount = nAmount/100.0f;  
    Mat imgBlurred;
    GaussianBlur(src, imgBlurred, Size(), sigma, sigma);
    Mat lowContrastMask = abs(src-imgBlurred)<threshold;
    dst = src*(1+amount)+imgBlurred*(-amount);
    src.copyTo(dst, lowContrastMask);
}

int main()
{
    Mat src = imread("E:\\未来项目\\白板增强\\images\\t1.jpeg");
    src.convertTo(src,CV_32FC3,1.0/255);
    Mat gauss;
    Mat dst1 = src.clone();
    Mat dst2 = src.clone();
    cv::GaussianBlur(src,gauss,Size(101,101),0);
    dst1 = src/gauss;
    blur(src,gauss,Size(101,101));
    dst2 = src/gauss;
    //划分
    ImageSharp(dst2,dst2,101);
    waitKey();
    return 0;
}
效果更好
四、原理解析
算法的效果不错,下一步就是要分析为什么了。
关键点在于blur的话就可以思考得更清楚了,在101*101的格子里面算平均值,然后前景和其进行比较,对于那些有字的,肯定是小于平均值,关键是对于背景,肯定大于平均值,这样一除就变成了全白,问题也就清楚了。
我也找到了ImageShop作者对这个算法的分析,他是专门做增强的,分析的比较有道理:

“这个算法在此类图像中能够成功的核心是:在原图中比较黑的文字部分,占用的整体是比较少的,当大半径模糊时,模糊的值是接近纸张之类的颜色的,也就是比较靠近白色,所以结果基本上没什么变化,而纸张那些地方的颜色,因为模糊的值和他们的原始值基本差不多,所以Src/Blur基本接近1,在乘以255,所以结果就变为白色了。” 

五、补充材料
1、发现OpenCV自带Divide算法,未进一步检测,我自信目前的解决是不错的。
divide
Performs per-element division of two arrays or a scalar by an array.
C++: void divide(InputArray src1, InputArray src2, OutputArray dst, double scale=1, int dtype=-1)
C++: void divide(double scale, InputArray src2, OutputArray dst, int dtype=-1)
Python: cv2.divide(src1, src2[, dst[, scale[, dtype]]]) → dst
Python: cv2.divide(scale, src2[, dst[, dtype]]) → dst
C: void cvDiv(const CvArr* src1, const CvArr* src2, CvArr* dst, double scale=1)
Python: cv.Div(src1, src2, dst, scale=1) → None
Parameters:   
src1 – first input array.
src2 – second input array of the same size and type as src1.
scale – scalar factor.
dst – output array of the same size and type as src2.
dtype – optional depth of the output array; if -1, dst will have depth src2.depth(), but in case of an array-by-array division, you can only pass -1 when src1.depth()==src2.depth().
The functions divide divide one array by another:
or a scalar by an array when there is no src1 :
When src2(I) is zero, dst(I) will also be zero. Different channels of multi-channel arrays are processed independently.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

jsxyhelu2015

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值