OpenCV数字图像处理——基于目标边缘适用于目标部分遮挡或不同光照模板匹配

简介

模板匹配是一种常见的计算机视觉问题,通常用于在一张图像中查找特定的模板图像。在处理模板匹配时,经常会面临对象的姿态未知的情况,其中姿态包括位置(X,Y坐标)和旋转角度(θ)。

模板匹配的步骤包括:

  1. 获取图像: 从图像中获取源图像和模板图像。

  2. 特征提取: 对源图像和模板图像进行特征提取,常常使用边缘检测等方法,以捕捉图像中的关键特征。

  3. 模板匹配: 在源图像中滑动模板图像,计算源图像中每个位置与模板的相似度。这通常涉及使用相关性或其他相似性度量。

  4. 位置识别: 通过找到相似度最高的位置,确定对象在源图像中的位置。

  5. 姿态估计: 如果需要,进行进一步的姿态估计,包括位置和旋转角度。

一、模型匹配

1.模板匹配API

OpenCV库提供了模板匹配API,适合应用在比较简单的场景下:

#include <opencv2/opencv.hpp>

int main() {
    // 读取源图像和模板图像
    cv::Mat sourceImage = cv::imread("source_image.jpg", cv::IMREAD_GRAYSCALE);
    cv::Mat templateImage = cv::imread("template_image.jpg", cv::IMREAD_GRAYSCALE);

    // 边缘检测
    cv::Mat edgesSource, edgesTemplate;
    cv::Canny(sourceImage, edgesSource, 50, 150);
    cv::Canny(templateImage, edgesTemplate, 50, 150);

    // 模板匹配
    cv::Mat result;
    cv::matchTemplate(edgesSource, edgesTemplate, result, cv::TM_CCOEFF_NORMED);

    // 获取最大匹配值和位置
    double minVal, maxVal;
    cv::Point minLoc, maxLoc;
    cv::minMaxLoc(result, &minVal, &maxVal, &minLoc, &maxLoc);

    // 绘制矩形框标记匹配位置
    cv::rectangle(sourceImage, maxLoc, cv::Point(maxLoc.x + edgesTemplate.cols, maxLoc.y + edgesTemplate.rows), 255, 2);

    // 显示结果
    cv::imshow("Matched Image", sourceImage);
    cv::waitKey(0);
    cv::destroyAllWindows();

    return 0;
}

这里使用了OpenCV的cv::Canny函数进行边缘检测,然后使用cv::matchTemplate函数进行模板匹配。最后,通过cv::rectangle函数在源图像中标记匹配位置。

2. 模板匹配的局限性与解决方法

模板匹配是一项具有挑战性的任务,主要受到其速度和可靠性的限制。当对象只有部分可见或与其他对象混合在一起时,解决方案必须具备鲁棒性,特别是对亮度变化要有稳健的适应能力。在此问题中,算法的计算效率也至关重要。为解决这一问题,存在两种主要方法:基于灰度值的匹配(或基于区域的匹配)和基于特征的匹配(非基于区域的匹配)。

基于灰度值的模板匹配:
基于灰度值的匹配方法使用归一化互相关(NCC)算法,这一算法在过去已经得到广泛应用。在该方法中,每一步的操作包括对模板 t(x, y) 和子图像 f(x, y) 进行互相关计算。公式如下:

N C C = 1 n − 1 ∑ x ⋅ v ( f ( x , y ) − μ f ) ( t ( x , y ) − μ t ) σ f . σ t N C C={\frac{1}{n-1}}\sum_{x\cdot v}{\frac{(f(x,y)-\mu f)(t(x,y)-\mu t)}{\sigma f.\sigma t}} NCC=n11xvσf.σt(f(x,y)μf)(t(x,y)μt)

该互相关计算通常在每一步中通过减去平均值并除以标准偏差来完成。这种匹配方法的目标是提高对亮度变化的鲁棒性,确保在对象部分可见或混合的情况下仍能有效工作。

基于特征的方法:
基于特征的模板匹配方法在图像处理领域中有多种应用。与基于边缘的物体识别相似,这些方法利用物体的特征来进行匹配。在广义霍夫变换等技术中,物体的几何特征被用于匹配过程。

二、基于边缘特征的模板匹配

边缘是数字图像中亮度急剧变化或具有不连续性的点,通常通过计算图像强度函数的梯度近似值来定义。
边缘检测方法主要分为两类:基于搜索和基于过零。基于搜索的方法首先计算边缘强度,通常使用一阶导数表达式(如梯度幅度),然后通过估计的局部方向搜索梯度幅度的局部最大值,该方向通常是梯度方向。可以使用 Sobel 算子实现了一种基于搜索的方法,它计算每个点的图像强度梯度,提供从明到暗的最大可能增加方向以及该方向的变化率。

1. 模板边缘检测

对于模板图像,为了获模板的特征属性,首先是要确定目标的位置边缘,这里采用了 Canny 边缘检测方法的一种变体来寻找目标边缘。

Canny 边缘检测方法检测算法步骤如下:

  1. 获取图像的亮度梯度

对模板图像应用 Sobel 过滤器,该过滤器返回 X(Gx)和 Y(Gy)方向的梯度。基于这些梯度,将使用以下公式计算边缘的大小和方向:

M a g n i t u d e = G x 2 + G y 2 M a g n i t u d e={\sqrt{G x^{2}+G y^{2}}} Magnitude=Gx2+Gy2

D i r e c t i o n = i n v t a n ( G y G x ) D i r e c t i o n=i n v t a n(\frac{G y}{G x}) Direction=invtan(GxGy)

一旦确定了边缘的方向,接下来的步骤是将可追踪的边缘方向与图像中的相邻像素关联。有四个可能的方向来描述周围像素的位置:0 度、45 度、90 度和 135 度。就可以将将所有方向映射到这些角度之一。

  1. 应用非极大值抑制

在确定了边缘方向后,执行非极大值抑制算法。该算法沿边缘方向追踪左右像素,如果当前像素的幅度小于左右像素的幅度,则抑制当前像素的幅度。这一步导致图像中边缘部分的细化。

  1. 滞后阈值
    这里使用两个阈值:高阈值和低阈值。使用高阈值来标记那些相当确定是真实边缘的像素。然后,利用先前导出的方向信息,可以通过图像追踪其他边缘。在跟踪边缘时,应用较低的阈值,这样只要找到一个起点,就能够追踪边缘的较弱部分。

  2. 保存特征数据
    提取边缘后,将所选边缘的 X 和 Y 导数与坐标信息一起保存为模板模型。这些坐标将重新排列以反映作为重心的起点。

使用OpenCV 4.5实现的代码如下:

int GeoMatch::create_match_model(Mat &cv_template,double maxContrast,double minContrast)
{

	Mat gx;		//Matrix to store X derivative
	Mat gy;		//Matrix to store Y derivative
	Mat nmsEdges;		//Matrix to store temp restult
	Size Ssize;

	Mat src(cv_template);  //Above step replicated
	// set width and height
	Ssize.width = src.cols;  //src->width;
	Ssize.height = src.rows; //src->height;
	modelHeight = src.rows;  //src->height;		//Save Template height
	modelWidth = src.cols;   //src->width;		//Save Template width

	cordinates = std::vector<cv::Point>(modelWidth * modelHeight);		//Allocate memory for coorinates of selected points in template image

	edge_magnitude = std::vector<double>(modelWidth * modelHeight);		//Allocate memory for edge magnitude for selected points
	edge_derivativeX = std::vector<double>(modelWidth * modelHeight);	//Allocate memory for edge X derivative for selected points
	edge_derivativeY = std::vector<double>(modelWidth * modelHeight);	Allocate memory for edge Y derivative for selected points


	// Calculate gradient of Template
	gx = Mat( Ssize.height, Ssize.width, CV_16SC1 );		//create Matrix to store X derivative
	gy = Mat( Ssize.height, Ssize.width, CV_16SC1 );		//create Matrix to store Y derivative
	
	
	Sobel( src, gx, CV_16S, 1, 0, 3 );		//gradient in X direction			
	Sobel( src, gy, CV_16S, 0, 1, 3 );	//gradient in Y direction

	nmsEdges = Mat(Ssize.height, Ssize.width, CV_32F);		//create Matrix to store Final nmsEdges
	const short* _sdx; 
	const short* _sdy; 
	double fdx,fdy;	
    double MagG, DirG;
	double MaxGradient = -99999.99;
	double direction;
	int *orients = new int[Ssize.height * Ssize.width];
	// count variable;
	int count = 0,i,j; 
	
	//double **magMat;
	std::vector<std::vector<double>> magMat;
	create_double_matrix(magMat ,Ssize);
	for( i = 1; i < Ssize.height - 1; i++ )
    {
    	for( j = 1; j < Ssize.width - 1; j++ )
        {       
			fdx = gx.at<short>(i, j);
			fdy = gy.at<short>(i, j);
			

			MagG = sqrt((float)(fdx * fdx) + (float)(fdy * fdy)); //Magnitude = Sqrt(gx^2 +gy^2)
			direction = atan2f((float)fdy, (float)fdx);//cvFastArctan((float)fdy,(float)fdx);	 //Direction = invtan (Gy / Gx)
			magMat[i][j] = MagG;
				
			if(MagG > MaxGradient)
				MaxGradient = MagG; // get maximum gradient value for normalizing.

			// get closest angle from 0, 45, 90, 135 set
            if ( (direction > 0 && direction < 22.5) || (direction > 157.5 && direction < 202.5) || (direction > 337.5 && direction < 360)  )
                direction = 0;
            else if ( (direction > 22.5 && direction < 67.5) || (direction > 202.5 && direction < 247.5)  )
                direction = 45;
            else if ( (direction > 67.5 && direction < 112.5)||(direction > 247.5 && direction < 292.5) )
                direction = 90;
            else if ( (direction > 112.5 && direction < 157.5)||(direction > 292.5 && direction < 337.5) )
                direction = 135;
            else 
				direction = 0;
				
			orients[count] = (int)direction;
			count++;
		}
	}
	count = 0; // init count
	// non maximum suppression
	double leftPixel, rightPixel;
	
	for( i = 1; i < Ssize.height - 1; i++ )
    {
		for( j = 1; j < Ssize.width - 1; j++ )
        {
				switch ( orients[count] )
                {
                   case 0:
                        leftPixel  = magMat[i][j - 1];
                        rightPixel = magMat[i][j + 1];
                        break;
                    case 45:
                        leftPixel  = magMat[i - 1][j + 1];
						rightPixel = magMat[i + 1][j - 1];
                        break;
                    case 90:
                        leftPixel  = magMat[i - 1][j];
                        rightPixel = magMat[i + 1][j];
                        break;
                    case 135:
                        leftPixel  = magMat[i - 1][j - 1];
                        rightPixel = magMat[i + 1][j + 1];
                        break;
				 }
				// compare current pixels value with adjacent pixels
				if ((magMat[i][j] < leftPixel) || (magMat[i][j] < rightPixel))
					nmsEdges.at<float>(i, j) = 0.0f;//(nmsEdges->data.ptr + nmsEdges->step*i)[j]=0;
                else
					nmsEdges.at<float>(i, j) = (uchar)(magMat[i][j] / MaxGradient * 255);//(nmsEdges->data.ptr + nmsEdges->step*i)[j]=(uchar)(magMat[i][j]/MaxGradient*255);
			
				count++;
			}
		}


	int RSum = 0,CSum = 0;
	int curX, curY;
	int flag = 1;

	//Hysterisis threshold
	for( i = 1; i < Ssize.height - 1; i++ )
    {
		for( j = 1; j < Ssize.width; j++ )
        {

			fdx = gx.at<short>(i, j);
			fdy = gy.at<short>(i, j);


			MagG = sqrt(fdx * fdx + fdy * fdy); 
			DirG = atan2f((float)fdy, (float)fdx);  
		
			flag = 1;
			double val = nmsEdges.at<float>(i, j);
		
			if( val < maxContrast)
			{

				if(val < minContrast)
				{
					nmsEdges.at<float>(i, j) = 0;
					flag = 0; // remove from edge
				}
				else
				{   
					if ( (nmsEdges.at<float>(i - 1, j - 1) < maxContrast) &&
						 (nmsEdges.at<float>(i - 1, j) < maxContrast) &&
						 (nmsEdges.at<float>(i - 1, j + 1) < maxContrast) &&
						 (nmsEdges.at<float>(i, j - 1) < maxContrast) &&
						 (nmsEdges.at<float>(i, j + 1) < maxContrast) &&
						 (nmsEdges.at<float>(i + 1, j - 1) < maxContrast) &&
						 (nmsEdges.at<float>(i + 1, j) < maxContrast) &&
						 (nmsEdges.at<float>(i + 1, j + 1) < maxContrast)
						)
					{
						nmsEdges.at<float>(i, j) = 0;
						flag = 0;
					}
				}
				
			}
			
			// save selected edge information
			curX = i;	curY = j;
			if(flag != 0)
			{
				if(fdx != 0 || fdy != 0)
				{		
					// Row sum and column sum for center of gravity
					RSum = RSum + curX;	
					CSum = CSum + curY; 
					
					cordinates[cordinates_num].x = curX;
					cordinates[cordinates_num].y = curY;
					edge_derivativeX[cordinates_num] = fdx;
					edge_derivativeY[cordinates_num] = fdy;
					
					//handle divide by zero
					if(MagG != 0)
						edge_magnitude[cordinates_num] = 1/MagG;  // gradient magnitude 
					else
						edge_magnitude[cordinates_num] = 0;
															
					cordinates_num++;
				}
			}
		}
	}

	center_gravity.x = RSum / cordinates_num; // center of gravity
	center_gravity.y = CSum / cordinates_num;	// center of gravity
		
	// change coordinates to reflect center of gravity
	for(int m = 0; m < cordinates_num; m++)
	{
		int temp;

		temp = cordinates[m].x;
		cordinates[m].x = temp - center_gravity.x;
		temp = cordinates[m].y;
		cordinates[m].y = temp - center_gravity.y;
	}

	
	modelDefined = true;
	return 1;
}

2. 查找模板

要在图像中查找指定的模板对象。先要从包含一组点的模板图像创建的模型:

P i τ = ( X i τ , Y i τ )   P_{i}^{\tau}=(X_{i}^{\tau},Y_{i}^{\tau})\, Piτ=(Xiτ,Yiτ)

获取它在 X 和 Y 方向上的梯度 :

G i T = ( G x i T , G y i T ) G_{i}^{T}=(G x_{i}^{T},G y_{i}^{T}) GiT=(GxiT,GyiT)

当i = 1…n,n是模板 (T) 数据集中的元素数。还可以在搜索图像 (S) 中找到梯度:

G a ˉ , ν S = ( G x a , ν S , G y a , ν S )   G_{\bar{a},\nu}^{S}=(G x_{a,\nu}^{S},G y_{a,\nu}^{S})\, Gaˉ,νS=(Gxa,νS,Gya,νS)

当u = 1…搜索图像中的行数,v = 1…搜索图像中的列数。

在匹配过程中,应使用相似性度量将模板模型与所有位置的搜索图像进行比较。相似性度量背后的思想是取模板图像梯度向量的所有归一化点积之和,并在模型数据集中的所有点上搜索图像。这会导致搜索图像中每个点的分数。

公式为:

S u , v = 1 n ∑ i = 1 n ( G x i T . G x ( u + X i , v + Y i ) S ) + ( G y i T . G y ( u + X i , v + Y i ) S ) G x i T + G y i T . G x ( u + X i , v + Y i ) T + G y ( u + X i , v + Y i ) T S_{u,v}=\frac{1}{n}\sum_{i=1}^{n}\frac{(G x_{i}^{T}.G x_{(u+X i,v+Y i)}^{S})+(G y_{i}^{T}.G y_{(u+X i,v+Y i)}^{S})}{\sqrt{G x_{i}^{T}+G y_{i}^{T}.\sqrt{G x_{(u+X i,v+Y i)}^{T}}+G y_{(u+X i,v+Y i)}^{T}}} Su,v=n1i=1nGxiT+GyiT.Gx(u+Xi,v+Yi)T +Gy(u+Xi,v+Yi)T (GxiT.Gx(u+Xi,v+Yi)S)+(GyiT.Gy(u+Xi,v+Yi)S)

如果模板模型和搜索图像之间存在完美匹配,则相似性度量函数将返回分数 1。该分数对应于搜索图像中可见的对象部分与模板模型的匹配程度。如果搜索图像中不存在对象或对象不可见,则分数将为 0。分数的范围在 0 到 1 之间,可用于衡量匹配的相似程度。

在实际情况下,处理的时候需要加快搜索过程。这可以使用各种方法来实现。第一种方法是使用平均的属性。在寻找相似性度量时,如果可以为相似性度量设置一个最小分数(S min),就不需要评估模板模型中的所有点。为了检查特定点 J 处的部分分数 S u,v,必须找到部分总和 Sm。点 m 处的 Sm 可以定义如下:
在这里插入图片描述
当剩余项小于或等于 1,可以停止评估 ,另一个标准可以是任何点的部分分数应大于最低分数。
使用硬标准和安全停止标准结合的方法是为了提高匹配的效率。这种方法允许在匹配的早期阶段更快速地排除那些不太可能是匹配项的位置,从而加速整个匹配过程。
在这个过程中,贪婪参数 (g) 起着关键的作用。通过调整 (g) 的值,用户可以灵活地控制使用硬标准检查的模板模型的部分。当 (g = 1) 时,模板模型中的所有点都用硬标准检查,这意味着在整个匹配过程中都使用硬标准。而当 (g = 0) 时,所有点只用安全标准检查,这意味着整个匹配过程都使用安全停止标准。

S m   < M I N ( ( S m i n − 1 + 1 − g   S m i n 1 − g  ⁣ ⋅  ⁣ m n ) , ( S m i n  ⁣ ⋅  ⁣ m n ) ) S_{m}\ \lt M I N\bigg(\Big(S^{m i n}-1+{\frac{1-g\,S^{m i n}}{1-g}}\!\cdot\!{\frac{m}{n}}\Big),(S^{m i n}\!\cdot\!{\frac{m}{n}})\bigg) Sm <MIN((Smin1+1g1gSminnm),(Sminnm))

这种方法的优势在于可以根据具体情况调整 (g) 的值,以在保持一定匹配准确性的同时实现更高的匹配速度。

模板查找代码:

double GeoMatch::find_match_model(Mat &cv_src, double minScore, double greediness, cv::Point &result_point)
{
	Mat Sdx, Sdy;
	
	double resultScore = 0;
	double partialSum = 0;
	double sumOfCoords = 0;
	double partialScore;
	const short* _Sdx;
	const short* _Sdy;
	int i,j,m ;			// count variables
	double iTx, iTy, iSx, iSy;
	double gradMag;    
	int curX,curY;

	std::vector<std::vector<double>> matGradMag;
	
	Mat src = cv_src.clone();
	// source image size
	Size Ssize;
	Ssize.width =  src.cols;
	Ssize.height= src.rows;
	
	create_double_matrix(matGradMag, Ssize); // 创建图像以保存梯度大小值
		
	Sdx = Mat( Ssize.height, Ssize.width, CV_16SC1 ); // X derivatives
	Sdy = Mat( Ssize.height, Ssize.width, CV_16SC1 ); // y derivatives
	
	Sobel( src, Sdx, CV_16S, 1, 0, 3 );  // find X derivatives
	Sobel( src, Sdy, CV_16S, 0, 1, 3 ); // find Y derivatives
		
	// stoping criterias to search for model
	double normMinScore = minScore / cordinates_num; // precompute minumum score 
	double normGreediness = ((1 - greediness * minScore)/(1 - greediness)) / cordinates_num; // precompute greedniness 

	for( i = 0; i < Ssize.height; i++ )
    {
		for( j = 0; j < Ssize.width; j++ )
		{

			iSx = Sdx.at<short>(i, j);
			iSy = Sdy.at<short>(i, j);

				gradMag = sqrt((iSx * iSx) + (iSy * iSy)); //Magnitude = Sqrt(dx^2 +dy^2)
							
				if(gradMag != 0) // hande divide by zero
					matGradMag[i][j] = 1 / gradMag;   // 1/Sqrt(dx^2 +dy^2)
				else
					matGradMag[i][j] = 0;
				
		}
	}
	for( i = 0; i < Ssize.height; i++ )
    {
		for( j = 0; j < Ssize.width; j++ )
            { 
				partialSum = 0; // initilize partialSum measure
				for(m = 0; m < cordinates_num; m++)
				{
					curX	= i + cordinates[m].x ;	// template X coordinate
					curY	= j + cordinates[m].y ; // template Y coordinate
					iTx	= edge_derivativeX[m];	// template X derivative
					iTy	= edge_derivativeY[m];    // template Y derivative

					if(curX < 0 || curY < 0 || curX > Ssize.height - 1 || curY > Ssize.width - 1)
						continue;
					

					iSx = Sdx.at<short>(curX, curY);  //CHECK IF curX AND curY NEED TO BE SWITCHED
					iSy = Sdy.at<short>(curX, curY);
					 	
				if((iSx != 0 || iSy != 0) && (iTx != 0 || iTy != 0))
				{
					//partial Sum  = Sum of(((Source X derivative* Template X drivative) + Source Y derivative * Template Y derivative)) / Edge magnitude of(Template)* edge magnitude of(Source))
					partialSum = partialSum + ((iSx * iTx) + (iSy * iTy)) * (edge_magnitude[m] * matGradMag[curX][curY]);			
				}

				sumOfCoords = m + 1;
				partialScore = partialSum / sumOfCoords ;
				// check termination criteria
				// if partial score score is less than the score than needed to make the required score at that position
				// break serching at that coordinate.
				if( partialScore < (MIN((minScore - 1) + normGreediness * sumOfCoords, normMinScore * sumOfCoords)))
					break;
									
			}
			if(partialScore > resultScore)
			{
				resultScore = partialScore; //  Match score
				result_point.x = i;
				result_point.y = j;
			}
		} 
	}
	
	Sdx.release();
	Sdy.release();
	
	return resultScore;
}

测试效果如下:

在这里插入图片描述

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

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

相关文章

Tg-5511cb: tcxo高稳定性+105℃高温

爱普生推的一款TG-5511CB是一种高稳定的TCXO温补晶体振荡器&#xff0c;频率范围十分广泛从 10mhz ~ 54mhz&#xff0c;它的电源电压只需要3.3V&#xff0c;无论是手机还是其他电子设备&#xff0c;都能轻松提供稳定的电力支持。频率/温度特性表现出色&#xff0c;0.28 10^6Ma…

sheng的学习笔记-【中】【吴恩达课后测验】Course 4 -卷积神经网络 - 第三周测验

课程4_第3周_测验题 目录 第一题 1.现在你要构建一个能够识别三个对象并定位位置的算法&#xff0c;这些对象分别是&#xff1a;行人&#xff08;c1&#xff09;&#xff0c;汽车&#xff08;c2&#xff09;&#xff0c;摩托车&#xff08;c3&#xff09;。下图中的标签哪个…

65内网安全-域环境工作组局域网探针

这篇分为三个部分&#xff0c;基本认知&#xff0c;信息收集&#xff0c;后续探针&#xff0c; 基本认知 分为&#xff0c;名词&#xff0c;域&#xff0c;认知&#xff1b; 完整架构图 名词 dwz称之为军事区&#xff0c;两个防火墙之间的区域称之为dwz&#xff0c;但安全性…

电脑显卡驱动停止响应该怎么办?为什么会出现这种情况

显卡驱动停止响应的原因 当你电脑突然弹框说显卡驱动停止响应&#xff0c;你知道是出现什么问题了吗&#xff0c;下面我们为大家总结了有可能造成显卡驱动停止响应的原因。1. 过热&#xff1a;显卡在长时间高负载下可能会过热&#xff0c;导致驱动停止响应。过高的温度可能…

【学习笔记】Java函数式编程03 Stream流-终结操作

书接上回 3.3.3 终结操作 3.3.3.1 forEach 对集合的每一个元素进行处理 接触很多了不赘述 3.3.3.2 count 用来获取当前流中的元素的个数 比如&#xff0c;打印出所有作家的作品的总数 System.out.println(authors.stream().flatMap(author -> author.getBooks().stre…

uniapp中uview的text组件

基本使用&#xff1a; 通过text参数设置文本内容。推荐您使用:textvalue的形式 <u--text text"我用十年青春,赴你最后之约"></u--text>设置主题&#xff1a; 通过type参数设置文本主题&#xff0c;我们提供了五类属性。primary error success warning…

STM32 cubeMX 人体红外模块实验

本文代码使用HAL库。 文章目录 前言一、人体红外模块介绍工作原理&#xff1a; 二、人体红外原理图解读三、STM32 cubeMX配置红外模块四、代码编写总结 前言 实验开发板&#xff1a;STM32F051K8。所需软件&#xff1a;keil5 &#xff0c; cubeMX 。实验目的&#xff1a;了解 人…

【流复制环境PostgreSQL-14.1到PostgreSQL-16.1大版本升级】

PostgreSQL大版本会定期添加新特性&#xff0c;这些新特性通常会改变系统表的布局&#xff0c;但内部数据存储格式很少改变。pg_upgrade通过创建新的系统表和重用旧的用户数据文件来执行快速升级。 pg_upgrade升级主要有三种用法&#xff1a; 1、使用pg_upgrade拷贝升级。 2、…

Shell三剑客:awk(awk编辑编程)三

一、For 循环 For 循环的语法 for(variable addignment; condition; iteration peocess) {statement1statement2... } #for 语句首先执行初始化动作( initialisation )&#xff0c;然后再检查条件( condition )。如果条件为真&#xff0c;则执行动作( action )&#xff0c;然后…

【论文笔记】Run, Don’t Walk: Chasing Higher FLOPS for Faster Neural Networks

论文地址&#xff1a;Run, Dont Walk: Chasing Higher FLOPS for Faster Neural Networks 代码地址&#xff1a;https://github.com/jierunchen/fasternet 该论文主要提出了PConv&#xff0c;通过优化FLOPS提出了快速推理模型FasterNet。 在设计神经网络结构的时候&#xff…

网络编程--网络基础

这里写目录标题 协议的概念什么是协议典型协议 分层模型OSI七层模型与TCP/TP四层模型 通信过程协议格式以太网帧协议&#xff08;主要作用与mac地址&#xff0c;也就是网卡&#xff09;mac地址格式ARP协议总结 IP协议&#xff08;主要作用于IP&#xff09;UDP与TCP协议&#xf…

one wire(单总线)FPGA代码篇

一.引言 单总线&#xff08;OneWire&#xff09;是一种串行通信协议&#xff0c;它允许多个设备通过一个单一的数据线进行通信。这个协议通常用于低速、短距离的数字通信&#xff0c;特别适用于嵌入式系统和传感器网络。 二.one wire通信优点缺点 优点&#xff1a; 单一数据线…

扫描全能王启动鸿蒙原生应用开发,系HarmonyOS NEXT智能扫描领域首批

近期&#xff0c;“鸿蒙合作签约暨扫描全能王鸿蒙原生应用开发启动仪式”&#xff08;简称“签约仪式”&#xff09;正式举行。合合信息与华为达成鸿蒙合作&#xff0c;旗下扫描全能王将基于HarmonyOS NEXT正式启动鸿蒙原生应用开发。据悉&#xff0c;扫描全能王是鸿蒙在智能扫…

TG7050CKN,TG7050SKN ,TG7050CMN,TG7050SMN

爱普生推出的温补晶振型号&#xff1a;TG7050CKN&#xff0c;TG7050SKN &#xff0c;TG7050CMN&#xff0c;TG7050SMN频率范围为 10mhz ~ 54mhz 适用于广泛的频率需求。这几款的特点就是耐高温&#xff0c;温度可达105℃高温&#xff0c;而且都是高稳定性温补晶振&#xff0c;&…

【C++】开源:fast-cpp-csv-parser数据解析库配置使用

&#x1f60f;★,:.☆(&#xffe3;▽&#xffe3;)/$:.★ &#x1f60f; 这篇文章主要介绍fast-cpp-csv-parser数据解析库配置使用。 无专精则不能成&#xff0c;无涉猎则不能通。——梁启超 欢迎来到我的博客&#xff0c;一起学习&#xff0c;共同进步。 喜欢的朋友可以关注一…

钦丰科技(安徽)股份有限公司携卫生级阀门管件盛装亮相2024发酵展

钦丰科技(安徽)股份有限公司携卫生级阀门管件盛装亮相2024济南生物发酵展&#xff01; 展位号&#xff1a;2号馆A65展位 2024第12届国际生物发酵产品与技术装备展览会&#xff08;济南&#xff09;于3月5-7日在山东国际会展中心盛大召开&#xff0c;展会同期将举办30余场高质…

ubuntu22.04搭建RTSP服务器

大致命令如下&#xff1a; git clone --depth 1 gitgithub.com:ZLMediaKit/ZLMediaKit.git sudo apt-get install build-essential sudo apt-get install cmake #除了openssl,其他其实都可以不安装 sudo apt-get install libssl-dev sudo apt-get install libsdl-dev sudo apt…

医院信息化-6 大模型与医疗

之前写了一系列跟医疗信息化相关的内容&#xff0c;其中有提到人工智能&#xff0c;但是写的都是原先的一些AI算法基础上的医疗应用。现在大模型出现的涌现推理能力确实让人惊讶&#xff0c;并且出现可商用化的可能性&#xff0c;因此最近一年关于大模型在医疗的应用也开始出现…

使用ffmpeg实现视频旋转并保持清晰度不变

1 原始视频信息 通过ffmpeg -i命令查看视频基本信息 ffmpeg -i source.mp4 ffmpeg version 6.1-essentials_build-www.gyan.dev Copyright (c) 2000-2023 the FFmpeg developersbuilt with gcc 12.2.0 (Rev10, Built by MSYS2 project)configuration: --enable-gpl --enable-…

智能三维数据虚拟现实电子沙盘

一、概述 易图讯科技&#xff08;www.3dgis.top&#xff09;以大数据、云计算、虚拟现实、物联网、AI等先进技术为支撑&#xff0c;支持高清卫星影像、DEM高程数据、矢量数据、无人机倾斜摄像、BIM模型、点云、城市白模、等高线、标高点等数据融合和切换&#xff0c;智能三维数…
最新文章