6

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

 1 year ago
source link: https://www.cnblogs.com/jsxyhelu/p/16994909.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

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

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

 

2917779-20221220190055986-1269807429.png

 

2917779-20221220190107287-2095922340.png
三、整理流程,转换为代码
图像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();
 
2917779-20221220190144855-401388340.png
在这个基础上,进一步进行封装:
//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;
}
2917779-20221220190204949-240483483.png
四、原理解析
算法的效果不错,下一步就是要分析为什么了。
关键点在于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.

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK