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

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

实现原理

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

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

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

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

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

具体流程

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

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

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

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

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

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

// 参数设置
int max = 4;
float bright = light / 100.0f / max;
float mid = 1.0f + max * bright;
// 边缘平滑过渡
cv::Mat midrate = cv::Mat::zeros(input.size(), CV_32FC1);
cv::Mat brightrate = cv::Mat::zeros(input.size(), CV_32FC1);
for (int i = 0; i < input.rows; ++i)
{
	uchar *m = mask.ptr<uchar>(i);
	float *th = thresh.ptr<float>(i);
	float *mi = midrate.ptr<float>(i);
	float *br = brightrate.ptr<float>(i);
	for (int j = 0; j < input.cols; ++j)
	{
		if (m[j] == 255)
		{
			mi[j] = mid;
			br[j] = bright;
		}
		else 
		{
			mi[j] = (mid - 1.0f) / t * th[j] + 1.0f;
			br[j] = (1.0f / t * th[j])*bright;
		}
	}
}

5)对阴影进行调整。

// 阴影提亮或变暗,获取结果图
cv::Mat result = cv::Mat::zeros(input.size(), input.type());
for (int i = 0; i < input.rows; ++i)
{
	float *mi = midrate.ptr<float>(i);
	float *br = brightrate.ptr<float>(i);
	uchar *in = input.ptr<uchar>(i);
	uchar *r = result.ptr<uchar>(i);
	for (int j = 0; j < input.cols; ++j)
	{
		for (int k = 0; k < 3; ++k)
		{
			float temp = pow(float(in[j]) / 255.f, 1.0f / mi[j])*(1.0 / (1 - br[j]));
			uchar utemp = uchar(255 * temp);
			r[j] = utemp;
		}
	}
}

C++测试代码

// C++常用头文件
#include <algorithm>
#include <chrono>
#include <ctime>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <direct.h>
#include <functional>
#include <fstream>
#include <filesystem>
#include <iostream>
#include <io.h>
#include <map>
#include <numeric>
#include <omp.h>
#include <random>
#include <regex>
#include <stdio.h>
#include <sstream>
#include <string>
#include <set>
#include <time.h>
#include <thread>
#include <unordered_map>
#include <unordered_set>
#include <utility>
#include <vector>
#include <Windows.h>
// 第三方相关头文件
#include <opencv2/opencv.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/core/core.hpp>
#include <opencv2/core/fast_math.hpp>

// 引入命名空间
using namespace std;
using namespace cv;

// 图像阴影亮暗调整
cv::Mat Shadow(cv::Mat input, int light)
{
	// 像素归一化
	cv::Mat gray;
	input.convertTo(gray, CV_32FC1);
	gray /= 255.f;

	// 确定阴影区
	cv::Mat thresh = cv::Mat::zeros(gray.size(), gray.type());
	thresh = (1.0f - gray).mul(1.0f - gray);
	// 取平均值作为阈值
	float t = mean(thresh)[0];
	cv::Mat mask = cv::Mat::zeros(gray.size(), CV_8UC1);
	mask.setTo(255, thresh >= t);

	// 参数设置
	int max = 4;
	float bright = light / 100.0f / max;
	float mid = 1.0f + max * bright;

	// 边缘平滑过渡
	cv::Mat midrate = cv::Mat::zeros(input.size(), CV_32FC1);
	cv::Mat brightrate = cv::Mat::zeros(input.size(), CV_32FC1);
	for (int i = 0; i < input.rows; ++i)
	{
		uchar *m = mask.ptr<uchar>(i);
		float *th = thresh.ptr<float>(i);
		float *mi = midrate.ptr<float>(i);
		float *br = brightrate.ptr<float>(i);
		for (int j = 0; j < input.cols; ++j)
		{
			if (m[j] == 255)
			{
				mi[j] = mid;
				br[j] = bright;
			}
			else 
			{
				mi[j] = (mid - 1.0f) / t * th[j] + 1.0f;
				br[j] = (1.0f / t * th[j])*bright;
			}
		}
	}

	// 阴影提亮,获取结果图
	cv::Mat result = cv::Mat::zeros(input.size(), input.type());
	for (int i = 0; i < input.rows; ++i)
	{
		float *mi = midrate.ptr<float>(i);
		float *br = brightrate.ptr<float>(i);
		uchar *in = input.ptr<uchar>(i);
		uchar *r = result.ptr<uchar>(i);
		for (int j = 0; j < input.cols; ++j)
		{
			for (int k = 0; k < 3; ++k)
			{
				float temp = pow(float(in[j]) / 255.f, 1.0f / mi[j])*(1.0 / (1 - br[j]));
				uchar utemp = uchar(255 * temp);
				r[j] = utemp;
			}

		}
	}
	return result;
}

int main()
{
	// 读取图像
	cv::Mat src = imread("test.jpg", 0);

	// 如果图像无法加载,则输出错误信息并返回
	if (src.empty()) 
	{
		std::cout << "Could not open or find the image" << std::endl;
		return -1;
	}

	// 尺寸扩大至4倍,用CUBIC插值算法,更平滑
	Mat enlargedImage;
	cv::resize(src, enlargedImage, Size(), 4.0, 4.0, INTER_CUBIC);

	// 图像阴影变暗:起到黑色字体颜色加深的效果
	cv::Mat shadow = Shadow(enlargedImage, -50);

	cout << "finish." << endl;
	return 0;
}

测试效果

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

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

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/566021.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【PCL】教程global_hypothesis_verification 通过验证模型假设来实现 3D 对象识别与位姿估计...

测试程序1 milk.pcd milk_cartoon_all_small_clorox.pcd 终端输出1&#xff1a; Model total points: 12575; Selected Keypoints: 193 Scene total points: 307200; Selected Keypoints: 7739 [pcl::SHOTEstimation::computeFeature] The local reference frame is not valid!…

若依集成mybatisplus报错找不到xml

引用&#xff1a;https://blog.csdn.net/qq_65080131/article/details/136677276 MybatisPlusAutoConfiguration中可以知道&#xff0c;系统会自动配置SqlSessionFactory&#xff0c;&#xff0c;但是&#xff0c;当你有自定义的SqlSessionFactory&#xff0c;&#xff0c;就会…

Spark-机器学习(4)回归学习之逻辑回归

在之前的文章中&#xff0c;我们来学习我们回归中的线性回归&#xff0c;了解了它的算法&#xff0c;知道了它的用法&#xff0c;并带来了简单案例。想了解的朋友可以查看这篇文章。同时&#xff0c;希望我的文章能帮助到你&#xff0c;如果觉得我的文章写的不错&#xff0c;请…

NCH WavePad for Mac:功能全面的音频编辑利器

NCH WavePad for Mac是一款功能全面的音频编辑软件&#xff0c;专为Mac用户设计。它集音频录制、编辑、处理和效果添加于一体&#xff0c;为用户提供一站式的音频解决方案。 NCH WavePad for Mac v19.16注册版下载 作为一款专业的音频编辑器&#xff0c;WavePad支持对音频文件进…

软件测试之【合理的利用GPT来辅助软件测试一】

读者大大们好呀&#xff01;&#xff01;!☀️☀️☀️ &#x1f525; 欢迎来到我的博客 &#x1f440;期待大大的关注哦❗️❗️❗️ &#x1f680;欢迎收看我的主页文章➡️寻至善的主页 文章目录 前言GPT的原理及技巧GPT辅助接口自动化测试 前言 在编程基础栏目中&#xff…

OSI七层模型、TCP/IP五层模型理解(个人解读,如何理解网络模型)

OSI七层模型 七层模型&#xff0c;亦称OSI&#xff08;Open System Interconnection&#xff09;。参考模型是国际标准化组织&#xff08;ISO&#xff09;制定的一个用于计算机或通信系统间互联的标准体系&#xff0c;一般称为OSI参考模型或七层模型。它是一个七层的、抽象的模…

漫谈HAMR硬盘的可靠性-2

很显然&#xff0c;HAMR已经成为业内用于提升HDD硬盘容量硬盘的技术手段。三家机械硬盘HDD厂商&#xff0c;希捷、西数、东芝都已对HAMR硬盘进行了十多年的研究&#xff0c;但只有希捷大胆押注HAMR。相反&#xff0c;东芝和西部数据在采用HAMR之前选择了能量辅助垂直磁记录&…

Qt 跨平台开发

Qt 跨平台开发 文章目录 Qt 跨平台开发摘要第一 \ & /第二 神奇{不能换行显示第三 预处理宏 关键字&#xff1a; Qt、 win、 linux、 lib、 MSVC 摘要 最近一直在琢磨Qt跨平台开发的问题&#xff0c;缘由有以下几个&#xff0c; 首先第一个&#xff0c;我们目前开发…

如何查看redisson-spring-boot-starter和SpringBoot 对应版本

如何查看redisson-spring-boot-starter和SpringBoot 对应版本 我目前没有找到官网的地址来来查看对应关系。 所以我只能找pom.xml来查看 先在mvnrepository 找到redisson-spring-boot-starter的列表 具体地址是&#xff1a;https://mvnrepository.com/artifact/org.redisso…

查看项目go代码cpu利用率

1.代码添加&#xff1a; "net/http"_ "net/http/pprof"第二步&#xff0c;在代码开始运行的地方加上go func() {log.Println(http.ListenAndServe(":6060", nil))}() 2.服务器上防火墙把6060打开 3.电脑安装&#xff1a;Download | Graphviz …

Quarto Dashboards 教程 2:Dashboard Layout

「写在前面」 学习一个软件最好的方法就是啃它的官方文档。本着自己学习、分享他人的态度&#xff0c;分享官方文档的中文教程。软件可能随时更新&#xff0c;建议配合官方文档一起阅读。推荐先按顺序阅读往期内容&#xff1a; 1.quarto 教程 1&#xff1a;Hello, Quarto 2.qu…

【无监督+自然语言】GPT,GPT-2,GPT-3 方法概述 (Generative Pre-Traning)

主要参考 【GPT&#xff0c;GPT-2&#xff0c;GPT-3 论文精读【李沐论文精读】-2022.03.04】 https://www.bilibili.com/video/BV1AF411b7xQ/ 大语言模型综述&#xff1a; http://t.csdnimg.cn/4obR4 发展节点 2017.06 Transformer: 所有大语言模型LLMs的基础结构 , Attent…

Ubuntu下使用VisualStudioCode进行Java开发

0-1开始Java语言编程之路 一、Ubuntu下Java语言环境搭建 二、Ubuntu下Docker环境安装 三、使用Docker搭建本地Nexus Maven私有仓库 四、Ubuntu下使用VisualStudioCode进行Java开发 Visual Studio Code 下载 点击这个链接Visual Studio Code&#xff0c;进入VisualStudioCode的…

IDEA2023版本创建Sping项目无法使用Java8

1. 问题复现 1.1 当前版本2023.3.2 1.2 创建项目时&#xff1a;不存在jdk8选项 提示报错 1.3 原因分析 Spring官方发布Spring Boot 3.0.0 的时候告知了一些情况&#xff0c;Java 17将成为未来的主流版本 2. 如何解决 2.1 替换创建项目的源 我们只知道IDEA页面创建Spring项目…

CMake 编译项目

一、概述 cmake 是C一个很重要的编译和项目管理工具&#xff0c;我们在git 上以及常见的项目现在多数都是用cmake 管理的&#xff0c;那么我们今天就做一个同时有Opencv和CGAL 以及PCL 的项目。 二、项目管理 重点是CMakeList.txt 1、CMakeList.txt cmake_minimum_requir…

springcloudgateway集成knife4j

上篇我们聊聊springboot是怎么继承knife4j的。springboot3 集成knife4j-CSDN博客 本次我们一起学习springcloudgateway集成knife4j。 环境介绍 java&#xff1a;17 SpringBoot&#xff1a;3.2.0 SpringCloud&#xff1a;2023.0.0 knife4j &#xff1a; 4.4.0 引入maven配置…

javaEE初阶——多线程(八)——常见的锁策略 以及 CAS机制

T04BF &#x1f44b;专栏: 算法|JAVA|MySQL|C语言 &#x1faf5; 小比特 大梦想 此篇文章与大家分享分治算法关于多线程进阶的章节——关于常见的锁策略以及CAS机制 如果有不足的或者错误的请您指出! 多线程进阶 1.常见的锁策略 我们需要了解的是,我们使用是锁,在加锁 / 解锁…

树莓派学习笔记--Raspberry Pi OS系统烧录、SSH远程连接、VNC远程连接、设置静态IP地址

前言&#xff1a; 由于一些比赛的需求&#xff0c;目前我将开启一段时间的树莓派学习。目前还是处于一个啥也不知道的萌新状态。希望通过短期的学习能掌握树莓派的基本使用。 树莓派其实就是一个微型电脑&#xff0c;下面这个图是b站大佬整理的树莓派的各种型号配置&#xff0c…

LabVIEW轴承表面缺陷检测系统

LabVIEW轴承表面缺陷检测系统 为了解决轴承生产中人工检测效率低下、误检率高的问题&#xff0c;实现了一套基于LabVIEW的轴承表面缺陷自动检测系统。该系统利用工业相机采集轴承图像&#xff0c;通过图像处理技术对轴承表面的划痕缺陷和倒角缺陷进行自动识别和分析&#xff0…

使用excel文件生成sql脚本

目录 1、excel文件脚本变量2、公式示例 前言&#xff1a;在系统使用初期有一些基础数据需要从excel中导入到数据库中&#xff0c;直接导入的话可能有些字段用不上&#xff0c;所以就弄一个excel生成sql的导入脚本&#xff0c;这样可以将需要的数据填到指定的列即可生成sql。 1、…