2018-05-15 18:25:43 u010005281 阅读数 792
  • Android开源项目实践之UI篇

    本课程主要展现了如何利用Android开源代码进行app的开发。例如: 1.异步网络请求(android-async-http); 2.百变圆形滚动条(ProgressWheel);3.滑动导航栏(PagerSlidingTabStrip);4.瀑布流与上拉刷新,下拉加载完美结合(PinterestLikeAdapterView)...

    21762 人正在学习 去看看 李忠义

题目描述

给定一个数组和滑动窗口的大小,找出所有滑动窗口里数值的最大值。例如,如果输入数组{2,3,4,2,6,2,5,1}及滑动窗口的大小3,那么一共存在6个滑动窗口,他们的最大值分别为{4,4,6,6,6,5}; 针对数组{2,3,4,2,6,2,5,1}的滑动窗口有以下6个: {[2,3,4],2,6,2,5,1}, {2,[3,4,2],6,2,5,1}, {2,3,[4,2,6],2,5,1}, {2,3,4,[2,6,2],5,1}, {2,3,4,2,[6,2,5],1}, {2,3,4,2,6,[2,5,1]}。

解题思路

没什么思路,就是滑动求解…
看到用Java和C++代码的长度,再看看python代码的长度,心里越发忐忑:在练题的时候,代码太方便以至于跳过了出题人本来要考察的意图,并不是什么好事。。。

def maxInWindows(self, num, size):
    res = []
    if len(num)>0 and size > 0:
        endP = size
        while endP <= len(num):
            res.append(max(num[endP-size:endP]))
            endP += 1
    return res
2020-02-24 19:53:39 weixin_45709330 阅读数 5
  • Android开源项目实践之UI篇

    本课程主要展现了如何利用Android开源代码进行app的开发。例如: 1.异步网络请求(android-async-http); 2.百变圆形滚动条(ProgressWheel);3.滑动导航栏(PagerSlidingTabStrip);4.瀑布流与上拉刷新,下拉加载完美结合(PinterestLikeAdapterView)...

    21762 人正在学习 去看看 李忠义

基于直方图的快速中值滤波算法

前言

以下内容引自:https://zhuanlan.zhihu.com/p/98092747

算法原理

传统的中值滤波是通过滑动窗口不断在图像上移动,求出窗口内的中值作为中心像素点的像素。在这个过程中显然存在大量的重复计算,所以效率很低。因此有人提出了一个利用直方图来做中值滤波的算法,如下图所示:
在这里插入图片描述

可以把整个图片看成滑动窗口,当我们从左边移动到右边时,中间的粉色部分是共享的,只有黄色部分变为了蓝色部分,所以就想了利用直方图来更新的方法。

算法步骤

  • 读取图像I,并且设定滤波窗口大小(winXwinY)(winX,winY),一般winX=winYwinX=winY,奇数。
  • 设定中值滤波直方图中的阈值,Thresh=(winXwinY)2+1Thresh=\frac{(winX*winY)}{2} +1;
  • 如果要考虑边界情况,可以先对原图像进行扩展,左、右边界分别扩展winX2\frac{winX}{2}个像素,上下边界分别扩展winY2\frac{winY}{2}个像素。
  • 逐行遍历图像像素,以第一行为例:先取第一行第一个要处理的像素(窗口中心像素),建立滤波窗口,提取窗口内所有像素值(N=winXwinY)(N=winX*winY个),获取NN个像素的直方图HistHist。从左到右累加直方图中每个灰度层级下像素点个数,记为sumCnt,直到sumCntThreshsumCnt\geqslant Thresh,这时的灰度值就是当前窗口内所有像素值的中值MediaValueMediaValue。将MediaValueMediaValue值赋值给窗口中心像素,表明第一个像素中值滤波完成。
  • 此时窗口需要向右移动一个像素,开始滤波第二个像素,并且更新直方图。以第二个像素为窗口中心建立滤波窗口,从前一个窗口的灰度直方图Hist中减去窗口中最左侧的一列像素值的灰度个数,然后加上窗口最右侧一列像素值的灰度个数。完成直方图的更新。
  • 直方图更新后,sumCntsumCnt值有三种变化可能:(1)(1)减小(2)(2)维持不变(3)(3)增大。这三种情况与减去与加入的像素值灰度有关。此时为了求得新的中值,需要不断调整sumCntsumCntThreshThresh之间的关系。
    (1)(1)如果sumCntsumCnt<Thresh<Thresh:说明中值在直方图当前灰度层级的右边,sumCntsumCnt就依次向右加上一个灰度层级中灰度值个数,直到满足sumCntThreshsumCnt\geqslant Thresh为止。记录此时的灰度层级代表的灰度值,更新MediaValueMediaValue,作为第二个像素的滤波后的值。
    (2)(2)维持不变:说明MediaValueMediaValue值不变,直接作为第二个像素滤波后的值。
    (3)(3)如果sumCntsumCnt值大于ThreshThresh:说明中值在直方图当前灰度层级的左边,sumCnt就依次向左减去一个灰度层级中灰度值个数,直到满足sumCntThreshsumCnt\leqslant Thresh为止。记录此时的灰度层级代表的灰度值,更新MediaValue值,作为第二个像素的滤波后的值。
  • 窗口逐行依次滑动,求得整幅图像的中值滤波结果。

C++代码

#include <iostream>
#include <opencv2/opencv.hpp>

using namespace cv;
using namespace std;

//计算中值
int getMediaValue(const int hist[], int thresh) 
{
	int sum = 0;
	for (int i = 0; i < 256; i++) 
	{
		sum += hist[i];
		if (sum >= thresh) 
		{
			return i;
		}
	}
	return 255;
}
//快速中值滤波,灰度图
Mat fastMedianBlur(Mat src, int diameter) 
{
	int row = src.rows;
	int col = src.cols;
	Mat dst(row, col, CV_8UC1);
	int Hist[256] = { 0 };
	int radius = (diameter - 1) / 2;
	int windowSize = diameter * diameter;
	int threshold = windowSize / 2 + 1;
	uchar* srcData = src.data;
	uchar* dstData = dst.data;
	int right = col - radius;
	int bot = row - radius;
	for (int j = radius; j < bot; j++) 
	{
		for (int i = radius; i < right; i++) 
		{
			//每一行第一个待滤波元素建立直方图
			if (i == radius) {
				memset(Hist, 0, sizeof(Hist));
				for (int y = j - radius; y <= min(j + radius, row); y++) 
				{
					for (int x = i - radius; x <= min(i + radius, col); x++) 
					{
						uchar val = srcData[y * col + x];
						Hist[val]++;
					}
				}
			}
			else 
			{
				int L = i - radius - 1;
				int R = i + radius;
				for (int y = j - radius; y <= min(j + radius, row); y++) 
				{
					//更新左边一列
					Hist[srcData[y * col + L]]--;
					//更新右边一列
					Hist[srcData[y * col + R]]++;
				}
			}
			uchar medianVal = getMediaValue(Hist, threshold);
			dstData[j * col + i] = medianVal;
		}
	}
	//边界直接赋值
	for (int i = 0; i < row; i++) 
	{
		for (int j = 0; j < radius; j++) 
		{
			int id1 = i * col + j;
			int id2 = i * col + col - j - 1;
			dstData[id1] = srcData[id1];
			dstData[id2] = srcData[id2];
		}
	}

	for (int i = 0; i < col; i++) 
	{
		for (int j = 0; j < radius; j++) 
		{
			int id1 = j * col + i;
			int id2 = (row - j - 1) * col + i;
			dstData[id1] = srcData[id1];
			dstData[id2] = srcData[id2];
		}
	}
	return dst;
}

void MedianFilter(int height, int width, unsigned char* __restrict src, unsigned char* __restrict dst) 
{
	for (int i = 1; i < height - 1; i++) 
	{
		for (int j = 1; j < width - 1; j++) 
		{
			unsigned char a[9];
			a[0] = src[i * width + j];
			a[1] = src[i * width + j + 1];
			a[2] = src[i * width + j - 1];

			a[3] = src[(i + 1) * width + j];
			a[4] = src[(i + 1) * width + j + 1];
			a[5] = src[(i + 1) * width + j - 1];

			a[6] = src[(i - 1) * width + j];
			a[7] = src[(i - 1) * width + j + 1];
			a[8] = src[(i - 1) * width + j - 1];
			for (int ii = 0; ii < 5; ii++) 
			{
				for (int jj = ii + 1; jj < 9; jj++) 
				{
					if (a[ii] > a[jj]) 
					{
						unsigned char temp = a[ii];
						a[ii] = a[jj];
						a[jj] = temp;
					}
				}
			}
			dst[i * width + j] = a[4];
		}
	}
	for (int i = 0; i < width; i++) 
	{
		dst[i] = src[i];
		dst[(height - 1) * width + i] = src[(height - 1) * width + i];
	}
	for (int i = 0; i < height; i++) 
	{
		dst[i * width] = src[i * width];
		dst[i * width + width - 1] = src[i * width + width - 1];
	}
}
Mat speed_MedianFilter(Mat src) 
{
	int row = src.rows;
	int col = src.cols;
	unsigned char* data = (unsigned char*)src.data;
	unsigned char* dst = new unsigned char[row * col];
	MedianFilter(row, col, data, dst);
	Mat res(row, col, CV_8UC1, dst);
	return res;
}


int main() 
{
	Mat src = cv::imread("F:\\t1.jpg", 0);
	Mat dst1 = fastMedianBlur(src, 3);
	Mat dst2 = speed_MedianFilter(src);
	imshow("dst1", dst1);
	imshow("dst2", dst2);
	int row = src.rows;
	int col = src.cols;
	for (int i = 0; i < row; i++) 
	{
		for (int j = 0; j < col; j++) 
		{
			if (dst1.at<uchar>(i, j) != dst2.at<uchar>(i, j)) 
			{
				printf("%d %d\n", i, j);
			}
		}
	}
	system("pause");
	cv::waitKey(0);
	return 0;
}

效果

在这里插入图片描述

2019-02-27 22:26:03 just_sort 阅读数 1392
  • Android开源项目实践之UI篇

    本课程主要展现了如何利用Android开源代码进行app的开发。例如: 1.异步网络请求(android-async-http); 2.百变圆形滚动条(ProgressWheel);3.滑动导航栏(PagerSlidingTabStrip);4.瀑布流与上拉刷新,下拉加载完美结合(PinterestLikeAdapterView)...

    21762 人正在学习 去看看 李忠义

前言

这是OpenCV图像处理专栏的第9篇文章,主要介绍一个基于直方图的快速中值滤波算法,希望对大家有帮助。

算法原理

传统的中值滤波是通过滑动窗口不断在图像上移动,求出窗口内的中值作为中心像素点的像素。在这个过程中显然存在大量的重复计算,所以效率很低。因此有人提出了一个利用直方图来做中值滤波的算法,如下图所示:
在这里插入图片描述
可以把整个图片看成滑动窗口,当我们从左边移动到右边时,中间的粉色部分是共享的,只有黄色部分变为了蓝色部分,所以就想了利用直方图来更新的方法。

算法步骤

  • 读取图像I,并且设定滤波窗口大小(winX,winY),一般winX=winY,奇数。

  • 设定中值滤波直方图中的阈值,Thresh=(winX*winY)/2 +1;

  • 如果要考虑边界情况,可以先对原图像进行扩展,左、右边界分别扩展winX/2个像素,上下边界分别扩展winY/2个像素。

  • 逐行遍历图像像素,以第一行为例:先取第一行第一个要处理的像素(窗口中心像素),建立滤波窗口,提取窗口内所有像素值(N=winX*winY个),获取N个像素的直方图Hist。从左到右累加直方图中每个灰度层级下像素点个数,记为sumCnt,直到sumCnt>=Thresh,这时的灰度值就是当前窗口内所有像素值的中值MediaValue。将MediaValue值赋值给窗口中心像素,表明第一个像素中值滤波完成。

  • 此时窗口需要向右移动一个像素,开始滤波第二个像素,并且更新直方图。以第二个像素为窗口中心建立滤波窗口,从前一个窗口的灰度直方图Hist中减去窗口中最左侧的一列像素值的灰度个数,然后加上窗口最右侧一列像素值的灰度个数。完成直方图的更新。

  • 直方图更新后,sumCnt值有三种变化可能:(1)减小(2)维持不变(3)增大。这三种情况与减去与加入的像素值灰度有关。此时为了求得新的中值,需要不断调整sumCnt与Thresh之间的关系。
    (1)如果sumCnt值小于Thresh:说明中值在直方图当前灰度层级的右边,sumCnt就依次向右加上一个灰度层级中灰度值个数,直到满足sumCnt>=Thresh为止。记录此时的灰度层级代表的灰度值,更新MediaValue,作为第二个像素的滤波后的值。
    (2)维持不变:说明MediaValue值不变,直接作为第二个像素滤波后的值。
    (3)如果sumCnt值大于Thresh:说明中值在直方图当前灰度层级的左边,sumCnt就依次向左减去一个灰度层级中灰度值个数,直到满足sumCnt<=Thresh为止。记录此时的灰度层级代表的灰度值,更新MediaValue值,作为第二个像素的滤波后的值。

  • 窗口逐行依次滑动,求得整幅图像的中值滤波结果。

代码实现

#include "stdafx.h"
#include <stdio.h>
#include <iostream>
#include <immintrin.h>
#include <opencv2/opencv.hpp>
using namespace cv;
using namespace std;

//计算中值
int getMediaValue(const int hist[], int thresh) {
	int sum = 0;
	for (int i = 0; i < 256; i++) {
		sum += hist[i];
		if (sum >= thresh) {
			return i;
		}
	}
	return 255;
}
//快速中值滤波,灰度图
Mat fastMedianBlur(Mat src, int diameter) {
	int row = src.rows;
	int col = src.cols;
	Mat dst(row, col, CV_8UC1);
	int Hist[256] = { 0 };
	int radius = (diameter - 1) / 2;
	int windowSize = diameter * diameter;
	int threshold = windowSize / 2 + 1;
	uchar *srcData = src.data;
	uchar *dstData = dst.data;
	int right = col - radius;
	int bot = row - radius;
	for (int j = radius; j < bot; j++) {
		for (int i = radius; i < right; i++) {
			//每一行第一个待滤波元素建立直方图
			if (i == radius) {
				memset(Hist, 0, sizeof(Hist));
				for (int y = j - radius; y <= min(j + radius, row); y++) {
					for (int x = i - radius; x <= min(i + radius, col); x++) {
						uchar val = srcData[y * col + x];
						Hist[val]++;
					}
				}
			}
			else {
				int L = i - radius - 1;
				int R = i + radius;
				for (int y = j - radius; y <= min(j + radius, row); y++) {
					//更新左边一列
					Hist[srcData[y * col + L]]--;
					//更新右边一列
					Hist[srcData[y * col + R]]++;
				}
			}
			uchar medianVal = getMediaValue(Hist, threshold);
			dstData[j * col + i] = medianVal;
		}
	}
	//边界直接赋值
	for (int i = 0; i < row; i++) {
		for (int j = 0; j < radius; j++) {
			int id1 = i * col + j;
			int id2 = i * col + col - j - 1;
			dstData[id1] = srcData[id1];
			dstData[id2] = srcData[id2];
		}
	}

	for (int i = 0; i < col; i++) {
		for (int j = 0; j < radius; j++) {
			int id1 = j * col + i;
			int id2 = (row - j - 1) * col + i;
			dstData[id1] = srcData[id1];
			dstData[id2] = srcData[id2];
		}
	}
	
	return dst;
}

void MedianFilter(int height, int width, unsigned char * __restrict src, unsigned char * __restrict dst) {
	for (int i = 1; i < height - 1; i++) {
		for (int j = 1; j < width - 1; j++) {
			unsigned char a[9];
			a[0] = src[i * width + j];
			a[1] = src[i * width + j + 1];
			a[2] = src[i * width + j - 1];

			a[3] = src[(i + 1) * width + j];
			a[4] = src[(i + 1) * width + j + 1];
			a[5] = src[(i + 1) * width + j - 1];

			a[6] = src[(i - 1) * width + j];
			a[7] = src[(i - 1) * width + j + 1];
			a[8] = src[(i - 1) * width + j - 1];
			for (int ii = 0; ii < 5; ii++) {
				for (int jj = ii + 1; jj < 9; jj++) {
					if (a[ii] > a[jj]) {
						unsigned char temp = a[ii];
						a[ii] = a[jj];
						a[jj] = temp;
					}
				}
			}
			dst[i * width + j] = a[4];
		}
	}
	for (int i = 0; i < width; i++) {
		dst[i] = src[i];
		dst[(height - 1) * width + i] = src[(height - 1) * width + i];
	}
	for (int i = 0; i < height; i++) {
		dst[i * width] = src[i * width];
		dst[i * width + width - 1] = src[i * width + width - 1];
	}
}
Mat speed_MedianFilter(Mat src) {
	int row = src.rows;
	int col = src.cols;
	unsigned char * data = (unsigned char *)src.data;
	unsigned char *dst = new unsigned char[row * col];
	MedianFilter(row, col, data, dst);
	Mat res(row, col, CV_8UC1, dst);
	return res;
}


int main() {
	Mat src = cv::imread("F:\\t1.jpg", 0);
	Mat dst1 = fastMedianBlur(src, 3);
	Mat dst2 = speed_MedianFilter(src);
	cv::imshow("dst1", dst1);
	cv::imshow("dst2", dst2);
	int row = src.rows;
	int col = src.cols;
	for (int i = 0; i < row; i++) {
		for (int j = 0; j < col; j++) {
			if (dst1.at<uchar>(i, j) != dst2.at<uchar>(i, j)) {
				printf("%d %d\n", i, j);
			}
		}
	}
	system("pause");
	cv::waitKey(0);
	return 0;
}

代码已经测试过,验证无误。在分辨率比较大的图像上执行中值滤波可以考虑一下这个算法,而且这个算法使用SSE指令可以进一步加速,后续会继续分享,欢迎大家关注阅读哦。

后记

今天为大家介绍了一个基于直方图的快速中值滤波算法,希望对大家有帮助。


欢迎关注我的微信公众号GiantPandaCV,期待和你一起交流机器学习,深度学习,图像算法,优化技术,比赛及日常生活等。
图片.png

2018-02-03 11:48:44 guojunxiu 阅读数 388
  • Android开源项目实践之UI篇

    本课程主要展现了如何利用Android开源代码进行app的开发。例如: 1.异步网络请求(android-async-http); 2.百变圆形滚动条(ProgressWheel);3.滑动导航栏(PagerSlidingTabStrip);4.瀑布流与上拉刷新,下拉加载完美结合(PinterestLikeAdapterView)...

    21762 人正在学习 去看看 李忠义

3.4 滑动窗口的卷积实现(Convolutional implementation of sliding windows)

滑动窗口算法计算成本高的主要原因是有许多重复计算,如下图所示:
pandas
在滑动过程中,红框和绿框重叠区域会被计算多次,这浪费了很多计算时间,因此要改进滑动窗口就要避免重复计算。
先看一下滑动窗口算法的结构:
sliding windows
由于使用全连接层,每次处理只能输出一个结果。而如果我们想共享重叠部分的计算,很自然想到一次处理得到所有窗口的结果,全连接层难以胜任,所以我们要使用卷积层代替全连接层。如下图所示:
conv
注意,这里是以四分类为例,所以最后得到的是一个1*1*4的结果,分别代表每种分类的概率。
这样便完成了全连接层向卷积层的转换,接下来要实现一次处理得到所有窗口:
one
如上图所示,假设我们对28*28大小的图片截取8*8个窗口,最终的分类结果有四种。由于图像位置关系都是一一对应的,所以原图中(1,1)处的窗口对应处理后(1,1)处的结果,原图中(i,j)处的窗口对应处理后(i,j)处的结果。
这样便实现了一次处理得到所有窗口的结果。
滑动窗口的卷积实现已经很高效,但缺点是边界框不够准确。 因为边界框完全由窗口大小及步长决定,很难使所有的目标恰好在窗口中。

3.5 Bounding Box Prediction

主要描述了YOLO(You Only Look Once)算法:

  1. 将输入图像划分成N*N 的网格。
  2. 对N*N个网格使用定位分类

image

需要注意一下几点:

  • YOLO使用的是卷积实现,可以一次处理N*N个网格。
  • 目标的中点位于哪个网格,那么就认为目标位于该网格。
  • 每个网格都会产生一个定位分类中的Y(维度为8),所以算法最后的输出是一个N*N*8的结果。
  • 对于目标中心的指定,以目标所在网格的左上为(0,0),右下为(1,1)来进行表示。当物体跨越多个网格时,bh,bw 会大于1。

当网格内的目标不超过一个时,YOLO算法表现很好,可以达到实时检测。当一个网格出现多个目标时,可以考虑使用更稠密的网格。

3.6 交并比(intersection over union)

为了衡量定位精确度,引入了交并比的概念。
顾名思义,交并比是两个集合交集 比 它们的并集。以下图为例
IoU
紫色框区域为预测的目标位置,红色框区域为目标的真实位置,二者的交并比为黄色区域比绿色区域。
一般交并比≥0.5时,我们就认为定位结果是可以接受的。IoU越大,说明定位精度越高。

2017-03-27 16:03:09 AthlonSe7en 阅读数 1857
  • Android开源项目实践之UI篇

    本课程主要展现了如何利用Android开源代码进行app的开发。例如: 1.异步网络请求(android-async-http); 2.百变圆形滚动条(ProgressWheel);3.滑动导航栏(PagerSlidingTabStrip);4.瀑布流与上拉刷新,下拉加载完美结合(PinterestLikeAdapterView)...

    21762 人正在学习 去看看 李忠义

一种均值滤波的优化算法

做毕设的时候需要用到滤波算法的优化,刚好查到这么一篇论文,感觉对自己有用,特来实现一下。
论文先摆出来:何石, 潘晓璐, 李一民. 一种均值滤波的优化算法[J]. 信息技术, 2012(3):133-134.

均值滤波优化原理

非常非常常见的数字图像处理手段,不用过多介绍,就是通过目标像素邻域内的像素点均值来代替目标像素值,一般用一个卷积核窗口在图像上滑动的方式来进行运算。
均值滤波公式
论文中的理论也比较简单,就是通过减少数据的读取次数和均值计算来实现的。
滑窗每在图像上水平移动一个像素,就相当于去掉了最左侧一列的像素,而后又加上了最右侧一列的像素。利用这个特点,每次从窗内像素的总和中剔除最左列像素的和,再加上新进最右列像素的和,最后除以窗内总点数即可得到均值滤波的结果。

实现

这里需要建立一个和滤波窗口大小同样长度的循环链表,将窗口中每一列的和保存在链表中,这样在窗口滑动的时候最左侧的列才不用再次计算,右边新进来的列的和在计算完成后代替链表中左侧的节点,更新了链表。
窗口中的链表分布

链表则按照顺序向后链,这样在窗口滑动的时候每移动一个像素,链表指针刚好配合向后走。
链表

链表定义

typedef struct _Node
{
    int sum;
    struct _Node* next;
}Node;

链表创建/初始化
由于滤波函数是要循环使用的,在函数内创建链表的话每次调用函数都会重复创建释放的过程,应该用什么更好的办法在下也不大懂。所以就单独写一个函数创建循环链表,滤波的时候直接使用即可,不会重复创建。

void initchain(Node* head, int length)
{
    Node *p1, *p2;

    head = (Node*)malloc(sizeof(Node));
    head->sum = 0;
    p1 = head;
    for(int i = 0;i < length - 1;++i)
    {
        p2 = (Node*)malloc(sizeof(Node));
        p2->sum = 0;
        p1->next = p2;
        p1 = p2;
    }
    p2->next = head;
}

滤波函数

void avgfilter(uint16_t* src, uint16_t* dst, Node* head, int Height, int Width, int size)
{
    int sum = 0;
    int semi_size = size / 2;
    int num = size * size;
    Node* ptr = head;

    for(int x = 0;x < Height;++x)
        for(int y = 0;y < Width;++y)
        {
            // 如果越界,则直接将原始值复制给目标
            if((x < semi_size) || (x >= Height - semi_size) || (y < semi_size)
                || (y >= Width - semi_size))
            {
                *(dst + x * Width + y) = *(src + x * Width + y);
                continue;
            }
            // 最左侧的窗口需要完整计算
            if(y == semi_size)
            {
                for(int j = y - semi_size;j < y + semi_size;++j)
                {
                    int temp = 0;
                    for(int i = x - semi_size;i < x + semi_size;++i)
                        temp += *(src + i * Width + j);
                    sum += temp;
                    ptr->sum = temp;
                    ptr = ptr->next;
                }
                *(dst + x * Width + y) = (uint16_t)(sum / num);
            }
            // 正常循环左侧列从sum中剔除,右列进入sum
            else
            {
                sum -= ptr->sum;
                int temp = 0;
                for(int i = x - semi_size;i < x + semi_size + 1;++i)
                {
                    temp += *(src + i * Width + y + semi_size);
                }
                sum += temp;
                ptr->sum = temp;
                ptr = ptr->next;
                *(dst + x * Width + y) = (uint16_t)(sum / num);
            }
        }
}

结果测试

由于所用图像特殊的缘故就不放图了,经过我的测试,和普通方式的均值滤波效果完全相同,且速度上有明显的提升。
我的测试图像分辨率是640x480的,两种滤波分别各运行1000次,在滤波窗口为5的时候时间如下:
时间对比
可见优化过的算法与原始算法相比有不俗的提升,再将窗口放大,使用大小为11的滤波器再进行测试:
时间对比
提升更加明显了,可见滤波窗口越大,提升越是显著。

均值漂移聚类

阅读数 4512

没有更多推荐了,返回首页