• OpenCV-基于阴影勾勒的图纸清晰度增强算法


    作者:翟天保Steven
    版权声明:著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处

    实现原理

           大家在工作和学习中,无论是写报告还是论文,经常有截图的需求,比如图表、图纸等,但是截下来的图像往往是失真模糊的,此时如果可以用算法基于某种逻辑处理下,会使图像效果好很多。

           本文基于阴影识别算法写了一个图纸清晰度增强算法:图纸的特征就是背景色和字体色颜色相对单调,将所有字体和图表框用识别算法提取勾勒出来,对其进行提亮或加暗就能有直观的效果。图像分辨率方面按需求用CUBIC插值扩展。

           图像阴影算法不了解的同学可以参考:

    OpenCV-图像阴影调整_opencv 添加阴影-CSDN博客

           下方介绍基于阴影勾勒的图纸清晰度增强算法的具体流程。

    具体流程

    1)读取识别图像的原图,用CUBIC插值算法进行了长宽的4倍扩展。

    1. // 读取图像
    2. cv::Mat src = imread("test.jpg", 0);
    3. // 如果图像无法加载,则输出错误信息并返回
    4. if (src.empty())
    5. {
    6. std::cout << "Could not open or find the image" << std::endl;
    7. return -1;
    8. }
    9. // 尺寸扩大至4倍,用CUBIC插值算法,更平滑
    10. Mat enlargedImage;
    11. cv::resize(src, enlargedImage, Size(), 4.0, 4.0, INTER_CUBIC);

    2)同样是“像”,插值后的像素感没那么重。

    3)像素归一化后,通过(1-gray)*(1-gray)得到thresh图像,图像中原本暗的地方则为亮,取平均值当阈值,进行二值化得到掩膜mask。下图分别是thresh和mask。

    1. // 像素归一化
    2. cv::Mat gray;
    3. input.convertTo(gray, CV_32FC1);
    4. gray /= 255.f;
    5. // 确定阴影区
    6. cv::Mat thresh = cv::Mat::zeros(gray.size(), gray.type());
    7. thresh = (1.0f - gray).mul(1.0f - gray);
    8. // 取平均值作为阈值
    9. float t = mean(thresh)[0];
    10. cv::Mat mask = cv::Mat::zeros(gray.size(), CV_8UC1);
    11. mask.setTo(255, thresh >= t);

    4)根据midrate和brightrate,进行阴影区调整。假设输入的调整值为-50,对非阴影区而言,midrate都为1,brightrate都为0,即没有变化;对阴影区而言,midrate都为0.5,brightrate都为-0.125,所以色彩数值均有所增加,带来了变暗效果;对边缘地区,midrate和brightrate起到了很好的过渡作用。下图是midrate,brightrate因为看起来都是黑色的就不展示了。

    1. // 参数设置
    2. int max = 4;
    3. float bright = light / 100.0f / max;
    4. float mid = 1.0f + max * bright;
    5. // 边缘平滑过渡
    6. cv::Mat midrate = cv::Mat::zeros(input.size(), CV_32FC1);
    7. cv::Mat brightrate = cv::Mat::zeros(input.size(), CV_32FC1);
    8. for (int i = 0; i < input.rows; ++i)
    9. {
    10. uchar *m = mask.ptr<uchar>(i);
    11. float *th = thresh.ptr<float>(i);
    12. float *mi = midrate.ptr<float>(i);
    13. float *br = brightrate.ptr<float>(i);
    14. for (int j = 0; j < input.cols; ++j)
    15. {
    16. if (m[j] == 255)
    17. {
    18. mi[j] = mid;
    19. br[j] = bright;
    20. }
    21. else
    22. {
    23. mi[j] = (mid - 1.0f) / t * th[j] + 1.0f;
    24. br[j] = (1.0f / t * th[j])*bright;
    25. }
    26. }
    27. }

    5)对阴影进行调整。

    1. // 阴影提亮或变暗,获取结果图
    2. cv::Mat result = cv::Mat::zeros(input.size(), input.type());
    3. for (int i = 0; i < input.rows; ++i)
    4. {
    5. float *mi = midrate.ptr<float>(i);
    6. float *br = brightrate.ptr<float>(i);
    7. uchar *in = input.ptr<uchar>(i);
    8. uchar *r = result.ptr<uchar>(i);
    9. for (int j = 0; j < input.cols; ++j)
    10. {
    11. for (int k = 0; k < 3; ++k)
    12. {
    13. float temp = pow(float(in[j]) / 255.f, 1.0f / mi[j])*(1.0 / (1 - br[j]));
    14. uchar utemp = uchar(255 * temp);
    15. r[j] = utemp;
    16. }
    17. }
    18. }

    C++测试代码

    1. // C++常用头文件
    2. #include <algorithm>
    3. #include <chrono>
    4. #include <ctime>
    5. #include <cstdio>
    6. #include <cstdlib>
    7. #include <cstring>
    8. #include <direct.h>
    9. #include <functional>
    10. #include <fstream>
    11. #include <filesystem>
    12. #include <iostream>
    13. #include <io.h>
    14. #include <map>
    15. #include <numeric>
    16. #include <omp.h>
    17. #include <random>
    18. #include <regex>
    19. #include <stdio.h>
    20. #include <sstream>
    21. #include <string>
    22. #include <set>
    23. #include <time.h>
    24. #include <thread>
    25. #include <unordered_map>
    26. #include <unordered_set>
    27. #include <utility>
    28. #include <vector>
    29. #include <Windows.h>
    30. // 第三方相关头文件
    31. #include <opencv2/opencv.hpp>
    32. #include <opencv2/imgproc/imgproc.hpp>
    33. #include <opencv2/highgui/highgui.hpp>
    34. #include <opencv2/core/core.hpp>
    35. #include <opencv2/core/fast_math.hpp>
    36. // 引入命名空间
    37. using namespace std;
    38. using namespace cv;
    39. // 图像阴影亮暗调整
    40. cv::Mat Shadow(cv::Mat input, int light)
    41. {
    42. // 像素归一化
    43. cv::Mat gray;
    44. input.convertTo(gray, CV_32FC1);
    45. gray /= 255.f;
    46. // 确定阴影区
    47. cv::Mat thresh = cv::Mat::zeros(gray.size(), gray.type());
    48. thresh = (1.0f - gray).mul(1.0f - gray);
    49. // 取平均值作为阈值
    50. float t = mean(thresh)[0];
    51. cv::Mat mask = cv::Mat::zeros(gray.size(), CV_8UC1);
    52. mask.setTo(255, thresh >= t);
    53. // 参数设置
    54. int max = 4;
    55. float bright = light / 100.0f / max;
    56. float mid = 1.0f + max * bright;
    57. // 边缘平滑过渡
    58. cv::Mat midrate = cv::Mat::zeros(input.size(), CV_32FC1);
    59. cv::Mat brightrate = cv::Mat::zeros(input.size(), CV_32FC1);
    60. for (int i = 0; i < input.rows; ++i)
    61. {
    62. uchar *m = mask.ptr<uchar>(i);
    63. float *th = thresh.ptr<float>(i);
    64. float *mi = midrate.ptr<float>(i);
    65. float *br = brightrate.ptr<float>(i);
    66. for (int j = 0; j < input.cols; ++j)
    67. {
    68. if (m[j] == 255)
    69. {
    70. mi[j] = mid;
    71. br[j] = bright;
    72. }
    73. else
    74. {
    75. mi[j] = (mid - 1.0f) / t * th[j] + 1.0f;
    76. br[j] = (1.0f / t * th[j])*bright;
    77. }
    78. }
    79. }
    80. // 阴影提亮,获取结果图
    81. cv::Mat result = cv::Mat::zeros(input.size(), input.type());
    82. for (int i = 0; i < input.rows; ++i)
    83. {
    84. float *mi = midrate.ptr<float>(i);
    85. float *br = brightrate.ptr<float>(i);
    86. uchar *in = input.ptr<uchar>(i);
    87. uchar *r = result.ptr<uchar>(i);
    88. for (int j = 0; j < input.cols; ++j)
    89. {
    90. for (int k = 0; k < 3; ++k)
    91. {
    92. float temp = pow(float(in[j]) / 255.f, 1.0f / mi[j])*(1.0 / (1 - br[j]));
    93. uchar utemp = uchar(255 * temp);
    94. r[j] = utemp;
    95. }
    96. }
    97. }
    98. return result;
    99. }
    100. int main()
    101. {
    102. // 读取图像
    103. cv::Mat src = imread("test.jpg", 0);
    104. // 如果图像无法加载,则输出错误信息并返回
    105. if (src.empty())
    106. {
    107. std::cout << "Could not open or find the image" << std::endl;
    108. return -1;
    109. }
    110. // 尺寸扩大至4倍,用CUBIC插值算法,更平滑
    111. Mat enlargedImage;
    112. cv::resize(src, enlargedImage, Size(), 4.0, 4.0, INTER_CUBIC);
    113. // 图像阴影变暗:起到黑色字体颜色加深的效果
    114. cv::Mat shadow = Shadow(enlargedImage, -50);
    115. cout << "finish." << endl;
    116. return 0;
    117. }

    测试效果

           从测试效果中可以看出,因为尺寸进行了插值,所以像素感没那么明显,同时对阴影进行了变暗,整体感受会好很多,增强了图像的整体清晰度。

           如果函数有什么可以改进完善的地方,非常欢迎大家指出,一同进步何乐而不为呢~

           如果文章帮助到你了,可以点个赞让我知道,我会很快乐~加油!

  • 相关阅读:
    双指针例题
    1024节日快乐!
    采集EtherNET/IP转Profinet在西门子plc中的应用
    DMSQL学习笔记
    苹果 CMS 原生 Java 白菜影视 App 源码【带打包教程】
    [QT入门篇]窗口类的应用
    modbus通信协议
    java83-常用基础类object
    Yolov5斜框检测tensorrt部署(C++)从入门到入坟
    django理解03 数据库引入
  • 原文地址:https://blog.csdn.net/zhaitianbao/article/details/137871185