<图像处理> 可分离滤波器核

news2024/12/29 9:59:39

可分离滤波器核

空间滤波器核是一个二维矩阵,若它能够表示为两个一维矩阵的乘积时,则表示该滤波器核是可分离的。
例如,一个3x3的核,
w = [ 1 1 1 1 1 1 1 1 1 ] w=\begin{bmatrix} 1 & 1 & 1\\ 1 & 1& 1\\ 1 & 1& 1\\ \end{bmatrix} w= 111111111
它可以表示为两个一维矩阵的乘积
c = [ 1 1 1 ] c=\begin{bmatrix} 1 & 1 & 1\\ \end{bmatrix} c=[111]
r = [ 1 1 1 ] r=\begin{bmatrix} 1 & 1 & 1\\ \end{bmatrix} r=[111]

w = c r T w=cr^T w=crT

性质

可分离核的重要性是卷积结合律性质导致的计算优势,如果有一个核 w w w,它可以分为两个简单的核并满足 w = w 1 ∗ w 2 w=w_1*w_2 w=w1w2,则其满足
w ∗ f = ( w 1 ∗ w 2 ) ∗ f = ( w 2 ∗ w 1 ) ∗ f = w 2 ∗ ( w 1 ∗ f ) = ( w 1 ∗ f ) ∗ w 2 w*f=(w_1*w_2)*f=(w_2*w_1)*f=w_2*(w_1*f)=(w_1*f)*w_2 wf=(w1w2)f=(w2w1)f=w2(w1f)=(w1f)w2
对于一个大小为 M ∗ N M*N MN的图像与大小为 m ∗ n m*n mn的核实现卷积,需要 M N m n MNmn MNmn次加法和乘法,如果核是可分离的,则需要 M N ( m + n ) MN(m+n) MN(m+n)次,可加速计算。

必要条件

要确定一个核是否可分离,只需要确定其秩是否为1。 因此确定某个矩阵的秩为1后,能够计算其两个分离的一维核,步骤如下

  1. 在核中找到任意一个非零元素,并将其表示为E;
  2. 找他该元素所在的行和列,表示为 c , r c,r c,r
  3. 可以得出两个一维核为 c c c r / E r/E r/E

示例

以x方向上的Sobel滤波核进行性能测试,比较 M N m n MNmn MNmn以及 M N ( m + n ) MN(m+n) MN(m+n)的处理时间,并与自带opencv 的cv::filter2D与cv::Sobel算子进行比较滤波效果。

int main()
{
	//x方向的Sobel核
	Mat kernel = (Mat_<char>(3, 3) <<
		-1, 0, 1,
		-2, 0, 2,
		-1, 0, 1);

	const char* imageName = ".....";
	Mat src = imread(imageName, IMREAD_GRAYSCALE);
	Mat srcBorder;
	copyMakeBorder(src, srcBorder, kernel.cols / 2, kernel.cols / 2, kernel.rows / 2, kernel.rows / 2, cv::BORDER_CONSTANT);//填充边缘

	clock_t start, end;
	//1.MNmn
	Mat dst(src.rows, src.cols, CV_8UC1);
	start = clock();
	int sum = 0;
	for (int i = 1;i <= dst.rows;i++)
	{
		for (int j = 1;j <= dst.cols;j++)
		{
			sum = 0;
			for (int m = 0;m < kernel.rows;m++)
			{
				for (int n = 0;n < kernel.cols;n++)
				{
					sum += (int)(srcBorder.ptr<uchar>(i + m - 1)[j + n - 1] * kernel.ptr<char>(m)[n]);
				}
			}
			dst.ptr<uchar>(i - 1)[j - 1] = (uchar)(sum < 0 ? 0 : (sum > 255 ? 255 : sum));
		}
	}
	end = clock();
	std::cout << "1.常规计算(MNmn):" << end - start << std::endl;

	//2.可分离滤波计算
	Mat _src2(src.rows + kernel.rows / 2 + 1, src.cols + kernel.cols / 2 + 1, CV_32SC1);
	_src2 = Scalar::all(0);
	Mat dst2(src.rows, src.cols, CV_8UC1);
	start = clock();
	//分离卷积核
	char kernelRow[3] = { 1,0,-1 };
	char kernelCol[3] = { -1,-2,-1 };

	for (int i = 1;i <= dst2.rows;i++)
	{
		for (int j = 1;j <= dst2.cols;j++)
		{
			sum = 0;
			for (int m = 0;m < 3;m++)
			{
				sum += (int)(srcBorder.ptr<uchar>(i)[j + m - 1] * kernelRow[m]);
			}
			_src2.ptr<short>(i)[j] = sum;
		}
	}

	for (int i = 1;i <= dst2.rows;i++)
	{
		for (int j = 1;j <= dst2.cols;j++)
		{
			sum = 0;
			for (int n = 0;n < 3;n++)
			{
				sum += (int)(_src2.ptr<short>(i + n - 1)[j] * kernelCol[n]);
			}
			dst2.ptr<uchar>(i - 1)[j - 1] = (uchar)(sum < 0 ? 0 : (sum > 255 ? 255 : sum)); //防止溢出。opencv中使用内联函数saturate_cast<T>()
		}
	}
	end = clock();
	std::cout << "2.可分离核计算MN(m+n):" << end - start << std::endl;

	//3.opencv-filter2D计算
	Mat dst3;
	start = clock();
	cv::filter2D(src, dst3, -1, kernel, Point(-1, -1), 0.0, BORDER_CONSTANT);
	end = clock();
	std::cout << "3.opencv-filter2D计算:" << end - start << std::endl;

	//4.opencv-sobel计算
	Mat dst4;
	start = clock();
	cv::Sobel(src, dst4, -1, 1, 0, 3, 1.0, 0.0, BORDER_CONSTANT);
	end = clock();
	std::cout << "4.opencv-sobel计算:" << end - start << std::endl;

	// 效果比较
	Mat findzero1 = dst2 != dst4; //方法一和方法二比较效果
	Mat findzero2 = dst2 != dst4; //方法二和方法三比较效果
	Mat findzero3 = dst2 != dst4; //方法二和方法四比较效果
	vector<cv::Point> veczero1;
	vector<cv::Point> veczero2;
	vector<cv::Point> veczero3;
	cv::findNonZero(findzero1, veczero1);
	cv::findNonZero(findzero2, veczero2);
	cv::findNonZero(findzero3, veczero3);
	int num1 = veczero1.size();
	int num2 = veczero2.size();
	int num3 = veczero3.size();
	std::cout << "方法一和方法二逐像素比较,像素不同个数:" << num1 << std::endl;
	std::cout << "方法二和方法三逐像素比较,像素不同个数:" << num2 << std::endl;
	std::cout << "方法二和方法四逐像素比较,像素不同个数:" << num3 << std::endl;

	system("pause");
	return 0;
}

计算结果显示,可分离核计算比常规计算快一倍左右,与OpenCV的sobel算子处理时间相当。

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

操作系统(OS)与系统进程

操作系统&#xff08;OS&#xff09;与系统进程 冯诺依曼体系结构操作系统(Operator System)进程基本概念进程的描述&#xff08;PCB&#xff09;查看进程通过系统调用获取进程标示符&#xff08;PID&#xff09;通过系统调用创建进程&#xff08;fork&#xff09;进程状态&…

安防监控/视频汇聚/云存储/AI智能视频融合平台页面新增地图展示功能

AI智能分析网关包含有20多种算法&#xff0c;包括人脸、人体、车辆、车牌、行为分析、烟火、入侵、聚集、安全帽、反光衣等等&#xff0c;可应用在安全生产、通用园区、智慧食安、智慧城管、智慧煤矿等场景中。将网关硬件结合我们的视频汇聚/安防监控/视频融合平台EasyCVR一起使…

使用Python进行健身手表数据分析

健身手表(Fitness Watch)数据分析涉及分析健身可穿戴设备或智能手表收集的数据&#xff0c;以深入了解用户的健康和活动模式。这些设备可以跟踪所走的步数、消耗的能量、步行速度等指标。本文将带您完成使用Python进行Fitness Watch数据分析的任务。 Fitness Watch数据分析是健…

QT多线程

1.QT4.7以前的版本-----线程处理方式 1. 出现的警告 直接使用从UI—>转到槽&#xff0c;就会出现警告 2. 出现的错误 error: invalid operands of types QTimer* and void (QTimer::*)(QTimer::QPrivateSignal) to binary operator& 错误:无效的操作数类型’QTimer…

【人工智能】—_有信息搜索、最佳优先搜索、贪心搜索、A_搜索

文章目录 【人工智能】— 有信息搜索、最佳优先搜索、贪心搜索、A*搜索无/有信息的搜索Informed Search AlgorithmsBest-first search(最佳优先搜索)Greedy SearchA* Search解释说明A*搜索是代价最优的和完备的对搜索等值线如何理解 【人工智能】— 有信息搜索、最佳优先搜索、…

2023年文旅地产行业研究报告

第一章 行业概况 1.1 定义 文旅地产&#xff0c;作为一个综合性的产业形态&#xff0c;融合了文化、旅游和地产三大元素&#xff0c;是住宅地产的补充和延伸。它不仅包含了文化和旅游的业态&#xff0c;还融入了商业等多元化元素&#xff0c;被誉为地产中的轻奢品。 在核心业…

AVR128单片机 自动售水机

一、系统方案 1、设计使用两个按键分别为S1和S2及一个发光二极管LED。S1为出水控制按键&#xff0c;当S1按下&#xff0c;表示售水机持续出水&#xff0c;继电器&#xff08;库元件relay&#xff09;接通&#xff0c;指示灯LED亮。S2为停水控制键&#xff0c;当S2按下&#xff…

Jenkins详解(三)

Jenkins详解(三) 目录 Jenkins详解(三) 1、Jenkins介绍2、Jenkins CI/CD 流程3、部署环境 3.1 环境准备3.2 安装GitLab3.3 初始化GitLab3.4 GitLab中文社区版补丁包安装3.5 修改GitLab配置文件/etc/gitlab/gitlab.rb3.6 在宿主机输入 http://192.168.200.26:88 地址就可以访问了…

Dom-clobbering原理和例题

目录 引入 1.获取标签 2.覆盖 3.多层覆盖 利用Dom-clobbering 1.tostring 2.集合取值 3.层级关系取值 4.三层取值 5.自定义属性 例题 1 2. 3. 引入 分析 引入 先用三个小例子看看dom-clobbering干了什么 1.获取标签 这个例子给img标签分别做了一个id和一个name…

热释电矢量传感器设计

1 概述 使用4个热释电传感器组成一个2X2的矩阵。通过曲线的相位差、 峰峰值等特征量来计算相关信息。本文使用STM32单片机设计、制作了热释电传感器矩阵&#xff1b;使用C#.NET设计了上位机软件。为以上研究做了试验平台。 2 硬件电路设计 2.1 热释电传感器介绍 热释电红外…

CCKS2023:基于企业数仓和大语言模型构建面向场景的智能应用

8月24日-27日&#xff0c;第十七届全国知识图谱与语义计算大会&#xff08;CCKS 2023&#xff09;在沈阳召开。大会以“知识图谱赋能通用AI”为主题&#xff0c;探讨知识图谱对通用AI技术的支撑能力&#xff0c;探索知识图谱在跨平台、跨领域等AI任务中的作用和应用途径。 作为…

MAC系统“无法验证开发者”问题

参考:https://blog.csdn.net/suxiang198/article/details/126550955 对于使用MAC电脑的同学而言&#xff0c;许多时候因为使用需要&#xff0c;从第三方源&#xff08;比如github等&#xff09;下载工具或软件&#xff0c;而在运行时会受到MAC系统的安全限制&#xff0c;老是弹…

【STM32】学习笔记-SPI通信

SPI通信 SPI通信&#xff08;Serial Peripheral Interface&#xff09;是一种同步的串行通信协议&#xff0c;用于在微控制器、传感器、存储器、数字信号处理器等之间进行通信。SPI通信协议需要使用4个线路进行通信&#xff1a;时钟线(SCLK)、主输入/主输出线(MISO)、主输出/主…

深入浅出AXI协议(5)——数据读写结构读写响应结构

目录 一、前言 二、写选通&#xff08;Write strobes&#xff09; 三、窄传输&#xff08;Narrow transfers&#xff09; 1、示例1 2、示例2 四、字节不变性&#xff08;Byte invariance&#xff09; 五、未对齐的传输&#xff08;Unaligned transfers&#xff09; 六…

网络版五子棋C++实现

目录 1.项目介绍 2.开发环境 3.核心技术 4.环境搭建 5.WebSocketpp介绍 5.1WebSocketpp是什么 5.2为什么使用WebSocketpp 5.3原理解析&#xff1a; 5.4WebSocketpp主要特性 6.WebSocketpp使用 7.JsonCpp使用 8.MySQL API 9.项目模块设计以及流程图 10.封装日志宏…

基于单片机的太阳能热水器控制器设计

一、项目介绍 随着环保意识的逐渐增强&#xff0c;太阳能热水器作为一种清洁能源应用得越来越广泛。然而&#xff0c;传统的太阳能热水器控制器通常采用机械式或电子式温控器&#xff0c;存在精度低、控制不稳定等问题。为了解决这些问题&#xff0c;本项目基于单片机技术设计…

Qt鼠标点击事件处理:按Escape键退出程序

创建项目 Qt 入门实战教程&#xff08;目录&#xff09; 首先&#xff0c;创建一个名称为QtKeyEscape的Qt默认的窗口程序。 参考 &#xff1a;Qt Creator 创建 Qt 默认窗口程序 Qt响应键盘Escape事件 打开Qt Creator >>编辑 >> 项目 >> Headers>> …

服务运营 | MS文章精读:基于强化学习和可穿戴设备的帕金森治疗方案

作者信息&#xff1a;庞硕&#xff0c;李舒湉 编者按 帕金森疾病的治疗是一个备受关注的医疗问题。本文通过患者的可穿戴传感器收集数据&#xff0c;提出了一个基于强化学习的帕金森药物治疗方案。这是第一篇关于可穿戴治疗设备在慢性疾病管理中的应用研究。原文于2023年4月发…

如何在你的Android工程中启用K2编译器?

如何在你的Android工程中启用K2编译器&#xff1f; K2编译器是用于Kotlin代码编译的最新、高效编译器&#xff0c;你现在可以尝试使用了。 Kotlin编译器正在为Kotlin 2.0进行重写&#xff0c;新的编译器实现&#xff08;代号K2&#xff09;带来了显著的构建速度改进&#xff…

K210-调用自定义py库

调用自定义py库 导入py库文件调用py库 用过Python的朋友应该知道&#xff0c;Python是支持将自定义py库&#xff08;或者第三方py库&#xff09;放到同一个目录下调用的&#xff0c;MicroPython也是支持调用自定义py库的。在调用自定义py库之前&#xff0c;需要提前将py库文件导…