SSE优化系列七:使用SSE优化盒滤波

深入解析SSE指令集优化:从基础到实战应用 本文深入解析SSE指令集优化,从基础概念到实战应用。文章详细介绍了SSE如何通过SIMD并行处理技术,在C/C++程序中实现性能的倍数级提升,特别适用于图像、音频处理和科学计算等场景。核心内容包括SSE数据类型、加载-计算-存储模式、内存对齐关键点,并通过向量点乘实战案例展示了优化效果与常见陷阱的规避方法。 阅读详情

前言

接着上一篇博客here构造的用直方图技巧加速的滤波算法处理框架,使用SSE实现了近似于O(1)的BoxFilter,参考博客:https://www.cnblogs.com/Imageshop/p/5053013.html ,接下来分享一些实现关键点和细节。本文的函数接口和上一篇博客完全一致。

普通C语言实现

// 函数功能: 实现图像方框模糊效果
// 参数列表:
// Src: 需要处理的源图像的数据结构
// Dest: 保存处理后的图像的数据结构
// Radius: 方框模糊的半径,有效范围[1, 1000]
// EdgeBehavior: 边缘处数据的处理方法,0表示重复边缘像素,1使用镜像的方式对边缘像素求均值
// 补充:
// 1. 能处理8位灰度和24位图像
// 2. Src和Dest可以相同,在相同时速度会稍慢
// 3. SSE优化版本在初始化时和半径是有关的,所以在半径增大时,耗时会略微增加

IS_RET BoxBlur(TMatrix *Src, TMatrix *Dest, int Radius, EdgeMode Edge) {
	if (Src == NULL || Dest == NULL) return IS_RET_ERR_NULLREFERENCE;
	if (Src->Data == NULL || Dest->Data == NULL) return IS_RET_ERR_NULLREFERENCE;
	if (Src->Width != Dest->Width || Src->Height != Dest->Height || Src->Channel != Dest->Channel || Src->Depth != Dest->Depth || Src->WidthStep != Dest->WidthStep) return IS_RET_ERR_PARAMISMATCH;
	if (Src->Depth != IS_DEPTH_8U || Dest->Depth != IS_DEPTH_8U) return IS_RET_ERR_NOTSUPPORTED;
	IS_RET Ret = IS_RET_OK;
	TMatrix *Row = NULL, *Col = NULL;
	int *RowPos, *ColPos, *ColSum, *Diff;
	int X, Y, Z, Width, Height, Channel, Index;
	int Value, ValueB, ValueG, ValueR;
	int Size = 2 * Radius + 1, Amount = Size * Size, HalfAmount = Amount / 2;
	Width = Src->Width;
	Height = Src->Height;
	Channel = Src->Channel;
	Ret = GetValidCoordinate(Width, Height, Radius, Radius, Radius, Radius, EdgeMode::Smear, &Row, &Col);		//	获取坐标偏移量
	RowPos = ((int *)Row->Data);
	ColPos = ((int *)Col->Data);		   
	ColSum = (int *)IS_AllocMemory(Width * Channel * sizeof(int), true);
	Diff = (int *)IS_AllocMemory((Width - 1) * Channel * sizeof(int), true);
	unsigned char *RowData = (unsigned char *)IS_AllocMemory((Width + 2 * Radius) * Channel, true);
	TMatrix Sum;
	TMatrix *p = ∑
	TMatrix **q = &p;
	IS_CreateMatrix(Width, Height, IS_DEPTH_32S, Channel, q);
	for (Y = 0; Y < Height; Y++) {
		unsigned char *LinePS = Src->Data + Y * Src->WidthStep;
		int *LinePD = (int *)(p->Data + Y * p->WidthStep);
		//	拷贝一行数据及边缘部分部分到临时的缓冲区中
		if (Channel == 1)
		{
			for (X = 0; X < Radius; X++)
				RowData[X] = LinePS[RowPos[X]];
			memcpy(RowData + Radius, LinePS, Width);
			for (X = Radius + Width; X < Radius + Width + Radius; X++)
				RowData[X] = LinePS[RowPos[X]];
		}
		else if (Channel == 3)
		{
			for (X = 0; X < Radius; X++)
			{
				Index = RowPos[X] * 3;
				RowData[X * 3] = LinePS[Index];
				RowData[X * 3 + 1] = LinePS[Index + 1];
				RowData[X * 3 + 2] = LinePS[Index + 2];
			}
			memcpy(RowData + Radius * 3, LinePS, Width * 3);
			for (X = Radius + Width; X < Radius + Width + Radius; X++)
			{
				Index = RowPos[X] * 3;
				RowData[X * 3 + 0] = LinePS[Index + 0];
				RowData[X * 3 + 1] = LinePS[Index + 1];
				RowData[X * 3 + 2] = LinePS[Index + 2];
			}
		}
		unsigned char *AddPos = RowData + Size * Channel;
		unsigned char *SubPos = RowData;
		for (X = 0; X < (Width - 1) * Channel; X++)
			Diff[X] = AddPos[X] - SubPos[X];
		//	第一个点要特殊处理
		if (Channel == 1)
		{
			for (Z = 0, Value = 0; Z < Size; Z++)	Value += RowData[Z];
			LinePD[0] = Value;

			for (X = 1; X < Width; X++)
			{
				Value += Diff[X - 1];	LinePD[X] = Value;				//	分四路并行速度又能提高很多
			}
		}
		else if (Channel == 3)
		{
			for (Z = 0, ValueB = ValueG = ValueR = 0; Z < Size; Z++)
			{
				ValueB += RowData[Z * 3 + 0];
				ValueG += RowData[Z * 3 + 1];
				ValueR += RowData[Z * 3 + 2];
			}
			LinePD[0] = ValueB;	LinePD[1] = ValueG;	LinePD[2] = ValueR;

			for (X = 1; X < Width; X++)
			{
				Index = X * 3;
				ValueB += Diff[Index - 3];		LinePD[Index + 0] = ValueB;
				ValueG += Diff[Index - 2];		LinePD[Index + 1] = ValueG;
				ValueR += Diff[Index - 1];		LinePD[Index + 2] = ValueR;
			}
		}
	}
	for (Y = 0; Y < Size - 1; Y++)			
	{
		int *LinePS = (int *)(p->Data + ColPos[Y] * p->WidthStep);
		for (X = 0; X < Width * Channel; X++)	ColSum[X] += LinePS[X];
	}

	for (Y = 0; Y < Height; Y++)
	{
		unsigned char* LinePD = Dest->Data + Y * Dest->WidthStep;
		int *AddPos = (int*)(p->Data + ColPos[Y + Size - 1] * p->WidthStep);
		int *SubPos = (int*)(p->Data + ColPos[Y] * p->WidthStep);

		for (X = 0; X < Width * Channel; X++)
		{
			Value = ColSum[X] + AddPos[X];
			LinePD[X] = (Value + HalfAmount) / Amount;					//		+  HalfAmount 主要是为了四舍五入
			ColSum[X] = Value - SubPos[X];
		}
	}
	IS_FreeMemory(RowPos);
	IS_FreeMemory(ColPos);
	IS_FreeMemory(Diff);
	IS_FreeMemory(ColSum);
	IS_FreeMemory(RowData);
	return Ret;
}

从这个普通的实现可以较好的提取算法的思路,我们的盒滤波算法的本质是对每个像素点,将它半径为Radius领域里面的像素求和,然后除以像素个数。我们的代码首先在行方向上进行求和,这里维护了一个Diff数组代表每个像素点在Radius邻域里面的像素和,然后在列方向进行遍历并按行累加和减去上一行的信息,得到每一个像素点Radius邻域的所有像素和,最后和这个领域的像素个数做除法,就得到了最后的结果。这个算法是和半径完全相关的,半径越大,耗时越长,如何用SSE优化这个算法呢?

SSE优化

先看源码:

// 函数功能: 实现图像方框模糊效果(SSE优化)

IS_RET BoxBlur_SSE(TMatrix *Src, TMatrix *Dest, int Radius, EdgeMode Edge) {
	if (Src == NULL || Dest == NULL) return IS_RET_ERR_NULLREFERENCE;
	if (Src->Data == NULL || Dest->Data == NULL) return IS_RET_ERR_NULLREFERENCE;
	if (Src->Width != Dest->Width || Src->Height != Dest->Height || Src->Channel != Dest->Channel || Src->Depth != Dest->Depth || Src->WidthStep != Dest->WidthStep) return IS_RET_ERR_PARAMISMATCH;
	if (Src->Depth != IS_DEPTH_8U || Dest->Depth != IS_DEPTH_8U) return IS_RET_ERR_NOTSUPPORTED;
	IS_RET Ret = IS_RET_OK;
	TMatrix *Row = NULL, *Col = NULL;
	int *RowPos, *ColPos, *ColSum, *Diff;
	int X, Y, Z, Width, Height, Channel, Index;
	int Value, ValueB, ValueG, ValueR;
	int Size = 2 * Radius + 1, Amount = Size * Size, HalfAmount = Amount / 2;
	float Scale = 1.0 / (Size * Size);
	Width = Src->Width;
	Height = Src->Height;
	Channel = Src->Channel;
	Ret = GetValidCoordinate(Width, Height, Radius, Radius, Radius, Radius, EdgeMode::Smear, &Row, &Col);		//	获取坐标偏移量
	RowPos = ((int *)Row->Data);
	ColPos = ((int *)Col->Data);
	ColSum = (int *)IS_AllocMemory(Width * Channel * sizeof(int), true);
	Diff = (int *)IS_AllocMemory((Width - 1) * Channel * sizeof(int), true);
	unsigned char *RowData = (unsigned char *)IS_AllocMemory((Width + 2 * Radius) * Channel, true);
	TMatrix Sum;
	TMatrix *p = &Sum;
	TMatrix **q = &p;
	IS_CreateMatrix(Width, Height, IS_DEPTH_32S, Channel, q);
	for (Y = 0; Y < Height; Y++) {
		unsigned char *LinePS = Src->Data + Y * Src->WidthStep;
		int *LinePD = (int *)(p->Data + Y * p->WidthStep);
		//	拷贝一行数据及边缘部分部分到临时的缓冲区中
		if (Channel == 1)
		{
			for (X = 0; X < Radius; X++)
				RowData[X] = LinePS[RowPos[X]];
			memcpy(RowData + Radius, LinePS, Width);
			for (X = Radius + Width; X < Radius + Width + Radius; X++)
				RowData[X] = LinePS[RowPos[X]];
		}
		else if (Channel == 3)
		{
			for (X = 0; X < Radius; X++)
			{
				Index = RowPos[X] * 3;
				RowData[X * 3] = LinePS[Index];
				RowData[X * 3 + 1] = LinePS[Index + 1];
				RowData[X * 3 + 2] = LinePS[Index + 2];
			}
			memcpy(RowData + Radius * 3, LinePS, Width * 3);
			for (X = Radius + Width; X < Radius + Width + Radius; X++)
			{
				Index = RowPos[X] * 3;
				RowData[X * 3 + 0] = LinePS[Index + 0];
				RowData[X * 3 + 1] = LinePS[Index + 1];
				RowData[X * 3 + 2] = LinePS[Index + 2];
			}
		}
		unsigned char *AddPos = RowData + Size * Channel;
		unsigned char *SubPos = RowData;
		X = 0;
		__m128i Zero = _mm_setzero_si128();
		for (; X <= (Width - 1) * Channel - 8; X += 8) {
			__m128i Add = _mm_unpacklo_epi8(_mm_loadl_epi64((__m128i const *)(AddPos + X)), Zero);
			__m128i Sub = _mm_unpacklo_epi8(_mm_loadl_epi64((__m128i const *)(SubPos + X)), Zero);
			_mm_store_si128((__m128i *)(Diff + X + 0), _mm_sub_epi32(_mm_unpacklo_epi16(Add, Zero), _mm_unpacklo_epi16(Sub, Zero)));
			_mm_store_si128((__m128i *)(Diff + X + 4), _mm_sub_epi32(_mm_unpackhi_epi16(Add, Zero), _mm_unpackhi_epi16(Sub, Zero)));
		}
		for (; X < (Width - 1) * Channel; X++)
			Diff[X] = AddPos[X] - SubPos[X];
		// 第一个点要特殊处理
		//	第一个点要特殊处理
		if (Channel == 1)
		{
			for (Z = 0, Value = 0; Z < Size; Z++)	Value += RowData[Z];
			LinePD[0] = Value;

			for (X = 1; X < Width; X++)
			{
				Value += Diff[X - 1];
				LinePD[X] = Value;
			}
		}
		else if (Channel == 3)
		{
			for (Z = 0, ValueB = ValueG = ValueR = 0; Z < Size; Z++)
			{
				ValueB += RowData[Z * 3 + 0];
				ValueG += RowData[Z * 3 + 1];
				ValueR += RowData[Z * 3 + 2];
			}
			LinePD[0] = ValueB;	LinePD[1] = ValueG;	LinePD[2] = ValueR;

			for (X = 1; X < Width; X++)
			{
				Index = X * 3;
				ValueB += Diff[Index - 3];		LinePD[Index + 0] = ValueB;
				ValueG += Diff[Index - 2];		LinePD[Index + 1] = ValueG;
				ValueR += Diff[Index - 1];		LinePD[Index + 2] = ValueR;
			}
		}
	}

	for (Y = 0; Y < Size - 1; Y++) {
		X = 0;
		int *LinePS = (int *)(p->Data + ColPos[Y] * p->WidthStep);
		for (; X <= Width * Channel - 4; X += 4) {
			__m128i SumP = _mm_load_si128((const __m128i*)(ColSum + X));
			__m128i SrcP = _mm_load_si128((const __m128i*)(LinePS + X));
			_mm_store_si128((__m128i *)(ColSum + X), _mm_add_epi32(SumP, SrcP));
		}
		for (; X < Width * Channel; X++) ColSum[X] += LinePS[X];
	}

	for (Y = 0; Y < Height; Y++) {
		unsigned char *LinePD = Dest->Data + Y * Dest->WidthStep;
		int *AddPos = (int*)(p->Data + ColPos[Y + Size - 1] * p->WidthStep);
		int *SubPos = (int*)(p->Data + ColPos[Y] * p->WidthStep);
		X = 0;
		const __m128 Inv = _mm_set1_ps(Scale);
		for (; X <= Width * Channel - 8; X += 8) {
			__m128i Sub1 = _mm_loadu_si128((const __m128i*)(SubPos + X + 0));
			__m128i Sub2 = _mm_loadu_si128((const __m128i*)(SubPos + X + 4));
			__m128i Add1 = _mm_loadu_si128((const __m128i*)(AddPos + X + 0));
			__m128i Add2 = _mm_loadu_si128((const __m128i*)(AddPos + X + 4));
			__m128i Col1 = _mm_load_si128((const __m128i*)(ColSum + X + 0));
			__m128i Col2 = _mm_load_si128((const __m128i*)(ColSum + X + 4));

			__m128i Sum1 = _mm_add_epi32(Col1, Add1);
			__m128i Sum2 = _mm_add_epi32(Col2, Add2);

			__m128i Dest1 = _mm_cvtps_epi32(_mm_mul_ps(Inv, _mm_cvtepi32_ps(Sum1)));
			__m128i Dest2 = _mm_cvtps_epi32(_mm_mul_ps(Inv, _mm_cvtepi32_ps(Sum2)));

			Dest1 = _mm_packs_epi32(Dest1, Dest2);
			_mm_storel_epi64((__m128i *)(LinePD + X), _mm_packus_epi16(Dest1, Dest1));

			_mm_store_si128((__m128i *)(ColSum + X + 0), _mm_sub_epi32(Sum1, Sub1));
			_mm_store_si128((__m128i *)(ColSum + X + 4), _mm_sub_epi32(Sum2, Sub2));
		}
		for (; X < Width * Channel; X++){
			Value = ColSum[X] + AddPos[X];
			LinePD[X] = Value * Scale;
			ColSum[X] = Value - SubPos[X];
		}
	}
	IS_FreeMemory(RowPos);
	IS_FreeMemory(ColPos);
	IS_FreeMemory(Diff);
	IS_FreeMemory(ColSum);
	IS_FreeMemory(RowData);
	return Ret;
}

这个SSE优化的代码就是将上面的C语言实现翻译为了指令集加减操作,这几个指令对应的意思在MSDN都可以查到,这里特别要注意的一点是,使用了_mm_malloc申请内存时时自动对齐的,所以在最后存储计算出的SSE变量时,可以直接用_mm_store_si128 ,反之没有内存对齐的数据存储的时候必须强行对齐,使用_mm_storeu_si128 。这个小细节我经常不太注意,引起内存崩溃,需要特别强调一下。
可以看到这个算法的SSE优化代码只有初始化部分和半径有关,所以可以说时近似O(1)实现。

速度测试

在这里插入图片描述这个代码相对于普通实现的盒子滤波大概有30%的加速,且半径越大效果越明显。

参考

https://www.cnblogs.com/Imageshop/p/5053013.html

FIR滤波器SSE优化:原理、实现与性能提升 FIR滤波器作为数字信号处理的核心组件,通过有限脉冲响应实现稳定的线性相位特性,在通信、音频处理等领域具有广泛应用。其核心计算涉及大量乘加运算,传统实现面临性能瓶颈。SIMD指令集如Intel SSE通过单指令多数据并行处理,显著提升计算效率。优化技术包括循环展开消除指令依赖、内存对齐减少访问延迟、以及向量化结果处理等工程实践。在Atom等顺序执行架构上,通过指令级并行优化可达到理论性能的85%。这些方法同样适用于AVX2/AVX-512等现代指令集,为实时信号处理系统提供关键性能保障。 阅读详情

相关推荐

深入探究SSE优化的小波变换算法

本文还有配套的精品资源,点击获取 简介:小波变换是一种在信号处理、图像分析和模式识别等领域广泛使用的数学工具。本文档"www.pudn.com.txt"提供进一步资源,而"WVC_c.doc"包含详细实现和应用案例。小波变换利用SSE技术进行优化,通过数据向量化、指令级并行、预测补偿技术和内存访问优化来提升性能。该算法在图像压缩、音频处理等领域有重要应用。 1. ...

weixin_42506884的博客 997

SSE图像算法优化系列2-高斯滤波

前言 本算法的原理以及优化过程都是偷师ImageShop,也可以把这篇文章理解为https://www.cnblogs.com/Imageshop/p/6376028.html 这篇文章的一篇阅读总结。 原理 首先,高斯滤波是可以用递归算法来实现的。这最早见于《Recursive implementation of the Gaussian filter》论文中: 在forward的过程中,n是递...

I good vegetable a! 3047

【电子电力】IEEE33节点系统参数及拓扑图visio

IEEE33节点系统是一种广泛用于电力系统分析和研究的标准配电网络。该系统具有典型的辐射结构,适用于负载流分析、分布式发电优化及电力系统的可靠性评估。本报告主要基于IEEE33节点系统的拓扑图,讨论其在电子电力系统中的应用。通过MATLAB仿真分析了节点的电压分布、功率损耗等关键参数,并验证了系统的稳定性和有效性。

2401_84610415的博客 1万+

ClickHouse为什么这么快(二)SSE指令优化

通过SIMD指令优化数据统计性能

zhangqhn的博客 644

保姆级教程:在Win10/Win11上搞定AppScan 10.0.0安装与许可证激活(含常见报错解决)

本文提供AppScan 10.0.0在Win10/Win11上的详细安装与许可证激活指南,涵盖环境准备、分步安装、文件替换技巧及常见报错解决方案。通过专业的安全测试工具部署实践,帮助用户高效完成安装并避免常见陷阱,提升Web应用安全扫描效率。

weixin_30800987的博客 424

电磁波函数复数表示法:为什么物理学家偏爱e^(ikz)而非cos(kr-wt)?

本文深入解析了电磁波函数复数表示法(如e^(ikz))相较于传统余弦形式的巨大优势。通过欧拉公式,复数形式将复杂的三角运算转化为简洁的指数运算,在微分、积分、线性叠加及处理相位时展现出极高的计算效率,是物理学家和工程师分析波动现象的核心工具。

weixin_29094539的博客 498

使用SSE指令集来优化程序

SSE和SSE2的指令系统非常相似,SSE2比SSE多的仅是少量的额外浮点处理功能、64位浮点数运算支持和64位整数运算支持。 SSE为什么会比传统的浮点运算更快呢?因为它使用了128位的存储单元,这对于32位的浮点数来讲,是可以存下4个的,也就是说,SSE中的所有计算都是一次性针对4个浮点数来完成的。 虽然SSE从理论上来讲要比传统的浮点运算会快,但是所受的限制也很多,首先,虽然它执行一次相当于四次,会比传统的浮点运算执行4次的速度要快,但是它执行一次的速度却并没有想象中的那...

百里杨的博客 4914

ArcGIS标注太乱?手把手教你用‘要素分类标注’搞定河流分级显示

本文详细介绍了如何在ArcGIS Pro中使用要素分类标注功能实现河流分级显示,解决标注混乱问题。通过数据预处理、分类标注规则定义和高级参数调优,帮助用户打造层次分明、专业清晰的水系地图,提升地图信息传达效率。

weixin_30326741的博客 473

SSE图像算法优化系列四:图像转置的SSE优化(支持8位、24位、32位),提速4-6倍...

一、前言 转置操作在很多算法上都有着广泛的应用,在数学上矩阵转置更有着特殊的意义。而在图像处理上,如果说图像数据本身的转置,除了显示外,本身并无特殊含义,但是在某些情况下,确能有效的提高算法效率,比如很多行列可分离的算法,在很多情况下,行和列方向的算法逻辑随相同,但是由于多方面原因(比如Cache miss, 优化水平等)行列处理时间还是由很大的差异的,这个时候如果转置的耗时和处理时...

PixelDemon的博客 328

YOLO26改进 - 注意力机制 | MLLAttention (Mamba-Like Linear Attention) 类Mamba线性注意力:动态感受野适配多尺度目标

本文介绍了Mamba-Like Linear Attention (MLLA)模型及其在yolo26中的集成。MLLA结合了Mamba和线性注意力Transformer的优点,线性注意力降低计算复杂度,Mamba有效建模序列。该模型将Mamba的遗忘门和块设计等关键元素与线性注意力结合,还可用位置编码替代遗忘门。我们将MLLA引入yolo26,在检测头的不同尺度特征图上应用。实验表明,改进后的yolo26在目标检测任务中表现良好,体现了MLLA在提升模型性能上的有效性。

魔改工程师的博客 1148

OmniDrive(2):环境搭建

项目中使用的是全量的数据集(full dataset v1.0), 数据量比较大,训练验证(trainval) + test 总计200多G的数据,单独一个个下载会比较慢。参数,否则安装的时候会自动卸载已安装好的torch,重新安装最新版本的torch,这是不允许的。在cuDNN官网下载,默认下载的是最新版本的cuDNN,我们这里点击。,== 博文中详细介绍了完整的cuda和cudnn的安装过程==分析: 这是因为yapf 安装的版本不对,需要安装特定的版本。出现训练的loss变化,就说明可以正常训练了。

@bangbang的博客 622

SSE图像算法优化系列三十一:RGB2HSL/RGB2HSV及HSL2RGB/HSV2RGB的指令集优化-上。

RGB和HSL/HSV颜色空间的相互转换在我们的图像处理中是有着非常广泛的应用的,无论是是图像调节,还是做一些肤色算法,HSL/HSV颜色空间都非常有用,他提供了RGB颜色空间不具有的一些独特的特性,但是由于HSL/HSV颜色空间的复杂性,他们之间的转换的效率一直不是很高的,有一些基于定点算法的尝试,对速度有一定的提升,但一个是提升不是特别的明显,另外就是对结果的精度有一定的影响。...

PixelDemon的博客 458

11年电赛综合测评——单运放三角波发生器

目录 背景知识科普: 原理:局部负反馈+大环负反馈+积分器 仿真图如下: 背景知识科普: 反馈电路是将放大器输出信号(电压或电流)的一部分或全部,回收到放大器输入端与输入信号进行比较(相加或相减),并用比较所得的有效输入信号去控制输出,这就是放大器的反馈过程。 一、反馈放大器反馈极性的经验分析法—符号法 分析步骤1.在输入端上加上一个“+”或“-”的输入信号,按瞬时极性法标出反馈放大器中各个电极(基极b,集电极c,发射极e)电压的瞬时性。 2.判断经放大和反馈后得到的反馈信号Xf的瞬时极性

沉迷单车的追风少年 9905

SSE优化系列八:使用SSE优化多尺度细节提升算法

前言 这篇文章的原始来源请看我以前写的这篇博客:https://blog.youkuaiyun.com/just_sort/article/details/85007555 上面有算法原理和普通的opencv实现。然后这篇文章是在SSE优化系列七: 对盒滤波的SSE优化(https://blog.youkuaiyun.com/just_sort/article/details/98075712)的基础上,将多尺度提升算...

I good vegetable a! 2142

目标检测YOLO实战应用案例100讲-基于YOLOv7的番茄采摘机械手场景感知及试验(下)

根据软件系统的设计需求,制定系统的可行性方案分析,编写系统的相关程序和 调试运行环境,对系统进行测试,以实现对番茄数据的采集及监测。登陆界面登陆后,进入选择界面,菜单包括了对番茄种植棚室的固定相机数据的监测、 对番茄采摘机械手的双目相机采集的番茄的静态、动态数据的获取、对番茄静态、动态数 据处理的功能菜单等选项。与利用人工去进行对于番茄果实的识别和数目的统计相比,番茄视觉系统自动记录番 茄数量以及红色已经成熟的番茄、青色未成熟番茄的数量,减轻人工记录的工作量,减少 人工记录的误差,从经济方面是可行的。

qq_36130719的博客 811

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

SSE算法优化系列六:使用SSE优化最大值滤波

前言

I good vegetable a! 2165

数学建模模型总结

这是本人在两年内准备参加数学建模竞赛搜集到的模型资料,已经经过了本人的筛选和整理,希望能够对准备参加数学建模竞赛的学弟学妹们提供一些帮助!

SSE优化系列九:小半径中值滤波的SSE极速优化(比OpenCV3.1.0快9-10倍)

前言 在优化系列六:https://blog.youkuaiyun.com/just_sort/article/details/97280807 中提出了一个直方图算法处理框架,可以处理中值滤波,最大值滤波等,算法复杂度是和半径大小无关的。但是这里忽略了一个点在半径很小的时候,这个算法实际上是比直接暴力处理更慢的。而我们在日常图像处理过程中,小半径的中值滤波是最常用的例如3 * 3, 5 * 5 在PS中都...

I good vegetable a! 1573

锂电池充电器电路设计 里面有电路

锂电池充电器电路设计 里面有电路 3.7V升压到5V电路设计。

精雕软件(JDpaint) 5.5 绿色版

精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版

数字图像处理之高斯滤波加速优化

在上一篇文章中,我们讲了高斯滤波以及分离高斯滤波的原理与C++实现。本文将在此基础上,分别详细讲解使用SSE指令和CUDA来对分离高斯滤波算法的优化加速。一、SSE指令优化我们知道,SS...

shandianfengfan的博客 4371

优化均值滤波算法

本文参考:https://blog.youkuaiyun.com/blogshinelee/article/details/80997324 对于均值滤波的算法优化,可以从如下几个方面入手: 1.积分图 2.卷积核分离 3.SSE指令集 积分图方法优化 积分图算法详解可以参看我的另...

贾路飞的博客 2614

【AI PC端算法优化】八,小半径中值滤波的SSE极速优化

1. 前言 大家应该经常碰到这种需求,那就是使用3×33\times 33×3或者5×55\times 55×5这种相对比较小的窗口进行中值滤波,而如果在图像的分辨率比较大的情况下这种操作也是比较耗时的。所以在这种固定场景下设计一个定制的优化算法是有意义的。 2. 普通的3*3中值滤波实现 普通的实现没什么好说,就是直接在窗口区域内遍历寻找中位数即可,这里获取中值直接使用了c语言的qsort。代码实现如下: int ComparisonFunction(const void *X, const void *

I good vegetable a! 1046

如何使用SSE指令提高FIR算法效率

Normal 0 7.8 磅 0 2 <!-- /* Font Definitions */@font-face {font-family:Wingdings; panose-1:5 0 0 0 0 0 0 0 0 0; mso

燕青专栏 3378

SSE图像算法优化系列三:超高速导向滤波实现过程纪要(欢迎挑战)

  自从何凯明提出导向滤波后,因为其算法的简单性和有效性,该算法得到了广泛的应用,以至于新版的matlab都将其作为标准自带的函数之一了,利用他可以解决的所有的保边滤波器的能解决的问题,比如细节增强、HDR压缩、细节羽化、去雾、风格化,而且由于其保边特性,如果很多传统函数中使用高斯滤波或者均值滤波的地方用他代替,能很好解决一些强边缘的过渡不自然问题,比如retinex、Highlight/sha...

PixelDemon的博客 360

SSE图像算法优化系列二十三: 基于value-and-criterion structure 系列滤波器(如Kuwahara,MLV,MCV滤波器)的优化。...

基于value-and-criterion structure方式的实现的滤波器在原理上其实比较简单,感觉下面论文中得一段话已经描述的比较清晰了,直接贴英文吧,感觉翻译过来反而失去了原始的韵味了。 The value-and-criterion filter structure is based on thegeometrical structure of mat...

PixelDemon的博客 274

利用SSE优化图像转置

平时我们做图像处理或者视频处理, 很多地方会用到矩阵转置: 比如: DCT变换, 图像旋转, 图像滤波, 以及一些数据的内存行和列的交换等, 会大量使用转置这个动作. 然而由于数据量很大,处理速度很慢!如何来提高处理速度呢? 下面看看分析:  HEVC中有个地方是如下这样实现(直接行和列对应的位置交换): [cpp] view plaincopyprint

南宋星球的专栏 3582

SSE图像算法优化系列十九:一种局部Gamma校正对比度增强算法及其SSE优化。

  这是一篇2010年比较古老的文章了,是在QQ群里一位群友提到的,无聊下载看了下,其实也没有啥高深的理论,抽空实现了下,虽然不高大上,还是花了点时间和心思优化了代码,既然这样,就顺便分享下优化的思路和经历。   文章的名字为:Contrast image correction method,由于本人博客的后台文件已经快超过博客园所容许的最大空间,这里就不直接上传文章了,大家可以直接点我提供的链...

weixin_33841722的博客 1014

你如何利用SIMD(如SSE/AVX)优化图像处理的性能?

使用 SIMD(SSE/AVX) 优化图像处理,不仅能显著提升性能,还能降低 CPU 负载,尤其适用于 工业检测、实时图像处理、深度学习预处理 等领域。

chengbin20101的博客 1550
上一篇: ParseNet 论文阅读及代码实现
下一篇: 卷积神经网络学习路线(十二)| 继往开来的DenseNet
just_sort
博客等级 码龄11年 2356粉丝 1667原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值