连续多帧图像光流对齐和光流运动检测

14 篇文章 0 订阅
8 篇文章 0 订阅

光流运动检测

本方案适用于手持设备拍摄连续多帧拍摄,  使用光流检测场景运动信息.
1. 由于设备或者手抖动, 产生画面的整体偏移--需要先对齐不同的帧.
2. 为了计算性能, 将原始输入图, resize到较小尺寸(此处采用320x200).
3. 从大图直接resize到小图容易产生明显的摩尔纹, 此处采用三级下采样, 有效避免摩尔纹的干扰.

主要流程

  1. 预测多帧图像的相对清晰度
    通过sobel算子,计算不同帧的图像平均梯度值sobelValue, 代表相对清晰度.sobelValue最大的作为Reference, 并根据sobelValue利用统计分析, 标出不可用的BlurImg.

  2. 光流对齐
    Reference下采样到目标Size(OPT_W, OPT_H).
    遍历其他帧(除了Ref img和Blur img), 同样下采样到Size(OPT_W, OPT_H). 进行光流对齐

	//Read Reference frame from pBuffers[refIdx], and resize to target Size
	cv::Mat smallRef, resize1, resize2;
	cv::Mat inputRef(cropheight, cropwidth, CV_8UC1, (uint8_t*)pBuffers[refIdx]);

	cv::resize(inputRef, resize1, cv::Size(OPT_W*4, OPT_H*4), cv::INTER_CUBIC);
	cv::resize(resize1, resize2, cv::Size(OPT_W*2, OPT_H*2), cv::INTER_CUBIC);
	cv::resize(resize2, smallRef, cv::Size(OPT_W, OPT_H), cv::INTER_CUBIC);
	
	//Buffers for storing aligned small image
	uint8_t* pSmallBuffers[7];
	for(int k=0;k<burstImgNum;k++) {
		pSmallBuffers[k] = (uint8_t*)malloc(OPT_W*OPT_H);
		if(!pSmallBuffers[k] )
			return;
	}

	//Align the smallNoise to the smallRef, and store it in the alignSmall.
	for(int imgId = 0; imgId < burstImgNum; imgId++) {
		if(imgId == refIdx || imgIsBlur[imgId] == true) continue;
		
		cv::Mat smallNoise;
		cv::Mat inputNoise(cropheight, cropwidth, CV_8UC1, (uint8_t*)pBuffers[imgId]);
		cv::Mat alignSmall(OPT_H, OPT_W, CV_8UC1, (uint8_t*)pSmallBuffers[imgId]);
		
		cv::resize(inputNoise, resize1, cv::Size(OPT_W*4, OPT_H*4), cv::INTER_CUBIC);
		cv::resize(resize1, resize2, cv::Size(OPT_W*2, OPT_H*2), cv::INTER_CUBIC);
		cv::resize(resize2, smallNoise, cv::Size(OPT_W, OPT_H), cv::INTER_CUBIC);

		alignWithDenseOpticalFlow(smallNoise, smallRef, alignSmall);
		}
		
	光流对齐部分代码alignWithDenseOpticalFlow
void alignWithDenseOpticalFlow(cv::Mat& imMov, cv::Mat& imRef, cv::Mat& imAli)
{
	Mat matFlow; 
	//cv::Mat visFlow(cv::Size(imRef.cols, imRef.rows),CV_8UC1);
	
	//Get matFlow from openCV api.
	calcOpticalFlowFarneback(imRef, imMov, matFlow, 0.5, 3, 15, 3, 5, 1.2, 0);
	
	//Sample 100 points uniformly from the middle part of the optical matFlow.
	std::vector<float> sort_x;
	std::vector<float> sort_y;
	for(int y=30; y<150; y+=12){
		for(int x=40; x<280; x+=24){
			sort_x.push_back(matFlow.at<cv::Vec2f>(y, x)[0]);
			sort_y.push_back(matFlow.at<cv::Vec2f>(y, x)[1]);
			//visFlow.at<uchar>(y, x) = 255;
		}
	}
	
	//Sort the sampling points, select the middle 20 points, calculate the average value, use it as the offset vector of the whole image.
	std::sort(sort_x.begin(), sort_x.end());
	std::sort(sort_y.begin(), sort_y.end());

	float sum_x = 0.0, sum_y = 0.0;
	for(int i=40; i<60; i++){
		sum_x += sort_x.at(i);
		sum_y += sort_y.at(i);
	}
	sum_x /= 20.0;
	sum_y /= 20.0;
	
	//Map imMov to imAli according to the offset vector.
	Mat map(matFlow.size(), CV_32FC2);
	for (int y = 0; y < map.rows; ++y)
	{
	    for (int x = 0; x < map.cols; ++x)
	    {
	        map.at<Point2f>(y, x) = Point2f(x + sum_x, y + sum_y);
	    }
	}

	Mat mapping[2];
	split(map, mapping);
	remap(imMov, imAli, mapping[0], mapping[1], INTER_CUBIC);//INTER_LINEAR

	// static int save_id = 0;
	// std::string save_name = "/sdcard/sr/sr_results/vismat_" + std::to_string(save_id) + ".jpg";
	// cv::imwrite(save_name, visFlow);
	// save_id++;
}

  1. 光流运动场检测.
    使用opencv api --calcOpticalFlowFarneback计算稠密光流(高频场景有噪声, 感觉稀疏光流可以一试, 后话了, 先交作业…),
    由于上面的alignment, 引起图像的平移, 边缘应舍弃, 对光流图裁掉边缘.
    根据光流场, 计算每个像素位置的光流强度并缩放到可视化的范围.
    统计光流强度, 通过阈值判断运动场景.
int getMotionPixels(cv::Mat& iRefFrame, cv::Mat& iCurrFrame, float optThresh)
{
	int iCount = 0;
	cv::Mat matFlow;
	
	//Get matFlow from openCV api.
	calcOpticalFlowFarneback(iRefFrame, iCurrFrame, matFlow, 0.5, 3, 15, 3, 5, 1.2, 0);
	
	//Crop matFlow 0.1 edges
	matFlow = matFlow(Rect(iRefFrame.cols*0.1, iRefFrame.rows*0.1,  iRefFrame.cols*0.8, iRefFrame.rows*0.8));
	
	//cv::Mat visFlow(cv::Size(matFlow.cols, matFlow.rows),CV_8UC1);

	
	for (int i = 0; i < matFlow.rows; i++)
	{
		for (int j = 0; j < matFlow.cols; j++)
		{
			float tmp0 = matFlow.at<cv::Vec2f>(i, j)[0];
            float tmp1 = matFlow.at<cv::Vec2f>(i, j)[1];
            float fImp = sqrt(tmp0 * tmp0 + tmp1 * tmp1);

			//unsigned char elem = (uchar)(fImp * 51);
			//visFlow.at<uchar>(i, j) = elem < 255 ? elem : 255;

			if( fImp*51 > optThresh) iCount++;
		}
	}
	
	// static int save_id = 0;
	// std::string save_name = "/sdcard/sr/sr_results/smallFlow_" + std::to_string(save_id) + ".jpg";
	// cv::imwrite(save_name, visFlow);
	// save_id++;
	std::cout<< "***********************Motion pixels     >       "<< optThresh << " iCount: "<< iCount << endl;
	return iCount;
}

效果对比

1, 输入图4k原图, 连续4帧
原始图
2. 对齐之前

beAli

3, 去掉摩尔纹, 对齐后
afAli

  1. 高频场景存在得问题. 感觉可以结合稀疏光流试试

todo

  1. 静态场景光流图

在这里插入图片描述

  1. 动态场景, 光流图
    在这里插入图片描述在这里插入图片描述
  • 5
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
下面是一个使用三差法(Three-frame differencing)处理连续图像的代码示例,使用Python和OpenCV库: ```python import cv2 def three_frame_difference(frame1, frame2, frame3, threshold): # 将图像转换为灰度图像 gray1 = cv2.cvtColor(frame1, cv2.COLOR_BGR2GRAY) gray2 = cv2.cvtColor(frame2, cv2.COLOR_BGR2GRAY) gray3 = cv2.cvtColor(frame3, cv2.COLOR_BGR2GRAY) # 计算前两之间的差异 diff1 = cv2.absdiff(gray1, gray2) # 计算后两之间的差异 diff2 = cv2.absdiff(gray2, gray3) # 使用阈值进行二值化 _, binary1 = cv2.threshold(diff1, threshold, 255, cv2.THRESH_BINARY) _, binary2 = cv2.threshold(diff2, threshold, 255, cv2.THRESH_BINARY) # 对二值化后的差异图像进行按位与操作 motion_mask = cv2.bitwise_and(binary1, binary2) return motion_mask # 使用示例 video_path = 'path/to/video.mp4' threshold = 30 # 打开视频文件 cap = cv2.VideoCapture(video_path) # 读取前三 ret, frame1 = cap.read() ret, frame2 = cap.read() ret, frame3 = cap.read() while cap.isOpened(): if not ret: break # 使用三差法处理当前 motion_mask = three_frame_difference(frame1, frame2, frame3, threshold) # 显示当前运动区域 cv2.imshow("Frame", frame2) cv2.imshow("Motion Mask", motion_mask) # 读取下一 frame1 = frame2 frame2 = frame3 ret, frame3 = cap.read() # 按下 'q' 键退出循环 if cv2.waitKey(1) & 0xFF == ord('q'): break # 释放资源 cap.release() cv2.destroyAllWindows() ``` 请将`video_path`替换为你实际的视频文件路径,并根据需要调整`threshold`参数来设置差异阈值。这段代码使用三差法计算前两和后两之间的差异,并通过阈值二值化得到运动区域的掩膜图像。然后,代码会逐地处理视频,并显示当前运动区域,直到按下 'q' 键退出循环。你可以根据实际需求对代码进行修改和优化。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值