任意半径局部直方图类算法在PC中快速实现的框架。
在图像处理中,局部算法一般来说,在很大程度上会获得比全局算法更为好的效果,因为他考虑到了图像领域像素的信息,而很多局部算法可以借助于直方图获得加速。同时,一些常规的算法,比如中值滤波、最大值滤波、最小值滤波、表面模糊等等都可以通过局部直方图进行加速。而传统的获取局部直方图计算量很大,特别是半径增加时,耗时会成平方关系增加。一些局部算法只有在半径较大时才会获得很好的效果,因此,必须找到一种合适的加速计算局部直方图的方式。
在参考Median Filter in Constant Time.pdf一文附带的C的代码的基础上,本文提出了基于SSE加速的恒长任意半径局部直方图获取技术,可以大大加速算法的计算时间,特别是大半径时的提速更为明显。
主要的优化思路是,沿着列方向一行一行的更行整行的列直方图,新的一行对应的列直方图更新时只需要减去已经不再范围内的那个像素同时加入新进入的像素的直方图信息。之后,对于一行中的第一个像素点,累加半径辐射范围内的列直方图,得到改点的局部直方图,对于行中的其他的像素,则类似于更新行直方图,先减去不在范围内那列的列直方图,然后加上移入范围内的列直方图。由于采用了基于SSE函数的加速过程,直方图想加和相减的速度较普通的加减法有了10倍以上的提速,因此大大的提高了整体的实用性。
具体的过程我用代码加以说明:
1、一些公用的内存分配过程
TMatrix *Row = NULL, *Col = NULL;
unsigned char *LinePS, *LinePD;
int X, Y, K, Width = Src->Width, Height = Src->Height;
int *RowOffset, *ColOffSet; unsigned short *ColHist = (unsigned short *)IS_AllocMemory( * (Width + * Radius) * sizeof(unsigned short), true);
if (ColHist == NULL) {Ret = IS_RET_ERR_OUTOFMEMORY; goto Done8;}
unsigned short *Hist = (unsigned short *)IS_AllocMemory( * sizeof(unsigned short), true);
if (Hist == NULL) {Ret = IS_RET_ERR_OUTOFMEMORY; goto Done8;}
Ret = GetValidCoordinate(Width, Height, Radius, Radius, Radius, Radius, Edge, &Row, &Col); // 获取坐标偏移量
if (Ret != IS_RET_OK) goto Done8;
其中的ColHist用于保存一行像素对应的列直方图 ,注意这里的行是用的扩展后的行的大小即:Width + 2 * Radius。IS_AllocMemory是个内部使用了_mm_malloc定义的内存分配函数,主要是考虑SSE函数的16字节对齐问题。
Hist变量用于保存每个像素点的局部直方图数据,任何基于局部直方图技术的函数最终都演变为对于该函数进行各种各样的计算。
GetValidCoordinate是一个用于辅助边界处像素点处理的函数,具体可详见附件中给出的代码。
2、更新一行像素的列直方图
for (Y = ; Y < Height; Y++)
{
if (Y == ) // 第一行的列直方图,要重头计算
{
for (K = -Radius; K <= Radius; K++)
{
LinePS = Src->Data + ColOffSet[K] * Src->WidthStep;
for (X = -Radius; X < Width + Radius; X++)
{
ColHist[X * + LinePS[RowOffset[X]]]++;
}
}
}
else // 其他行的列直方图,更新就可以了
{
LinePS = Src->Data + ColOffSet[Y - Radius - ] * Src->WidthStep;
for (X = -Radius; X < Width + Radius; X++) // 删除移出范围内的那一行的直方图数据
{
ColHist[X * + LinePS[RowOffset[X]]]--;
} LinePS = Src->Data + ColOffSet[Y + Radius] * Src->WidthStep;
for (X = -Radius; X < Width + Radius; X++) // 增加进入范围内的那一行的直方图数据
{
ColHist[X * + LinePS[RowOffset[X]]]++;
}
}
// 依次获取一行每个像素的局部直方图
// 根据局部直方图获的结果
}
可见,这部分和普通的局部优化方式类似,没有什么特殊的地方。
3、依次获取一行每个像素的局部直方图
for (Y = ; Y < Height; Y++)
{
// 更新一行像素的列直方图 memset(Hist, , * sizeof(unsigned short)); // 每一行直方图数据清零先
LinePS = Src->Data + Y * Src->WidthStep;
LinePD = Dest->Data + Y * Dest->WidthStep;
for (X = ; X < Width; X++)
{
if (X == )
{
for (K = -Radius; K <= Radius; K++) // 行第一个像素,需要重新计算
HistgramAddShort(ColHist + K * , Hist);
}
else
{
/* HistgramAddShort(ColHist + RowOffset[X + Radius] * 256, Hist);
HistgramSubShort(ColHist + RowOffset[X - Radius - 1] * 256, Hist);
*/
HistgramSubAddShort(ColHist + RowOffset[X - Radius - ] * , ColHist + RowOffset[X + Radius] * , Hist); // 行内其他像素,依次删除和增加就可以了
}
// 根据局部直方图获的结果
LinePS++;
LinePD++;
}
}
上面处理的过程其实和2的过程的优化道理是类似的,只不过一个是行方向,一个是列方向,聪明者自然能明白,稍微愚钝者请自己多多斟酌,自然有豁然开朗的时刻。
4、 根据局部直方图获的结果
根据不同的算法需求,结合局部直方图信息来获取结果,比如最大值算法可以用如下方式获得:
for (K = ; K >= ; K--)
{
if (Hist[K] != )
{
LinePD[X] = K;
break;
}
}
关于直方图累加的代码如下:
/// <summary>
/// 无符号短整形直方图数据相加,Y = X + Y, 整理时间2014.12.28;
/// </summary>
/// <param name="X">加数。</param>
/// <param name="Y">被加数,结果保存于该数中。</param>
/// <remarks>使用了SSE优化。</remarks>
void HistgramAddShort(unsigned short *X, unsigned short *Y)
{
*(__m128i*)(Y + ) = _mm_add_epi16(*(__m128i*)&Y[], *(__m128i*)&X[]); // 不要想着用自己写的汇编超过他的速度了,已经试过了
*(__m128i*)(Y + ) = _mm_add_epi16(*(__m128i*)&Y[], *(__m128i*)&X[]);
*(__m128i*)(Y + ) = _mm_add_epi16(*(__m128i*)&Y[], *(__m128i*)&X[]);
*(__m128i*)(Y + ) = _mm_add_epi16(*(__m128i*)&Y[], *(__m128i*)&X[]);
*(__m128i*)(Y + ) = _mm_add_epi16(*(__m128i*)&Y[], *(__m128i*)&X[]);
*(__m128i*)(Y + ) = _mm_add_epi16(*(__m128i*)&Y[], *(__m128i*)&X[]);
*(__m128i*)(Y + ) = _mm_add_epi16(*(__m128i*)&Y[], *(__m128i*)&X[]);
*(__m128i*)(Y + ) = _mm_add_epi16(*(__m128i*)&Y[], *(__m128i*)&X[]);
*(__m128i*)(Y + ) = _mm_add_epi16(*(__m128i*)&Y[], *(__m128i*)&X[]);
*(__m128i*)(Y + ) = _mm_add_epi16(*(__m128i*)&Y[], *(__m128i*)&X[]);
*(__m128i*)(Y + ) = _mm_add_epi16(*(__m128i*)&Y[], *(__m128i*)&X[]);
*(__m128i*)(Y + ) = _mm_add_epi16(*(__m128i*)&Y[], *(__m128i*)&X[]);
*(__m128i*)(Y + ) = _mm_add_epi16(*(__m128i*)&Y[], *(__m128i*)&X[]);
*(__m128i*)(Y + ) = _mm_add_epi16(*(__m128i*)&Y[], *(__m128i*)&X[]);
*(__m128i*)(Y + ) = _mm_add_epi16(*(__m128i*)&Y[], *(__m128i*)&X[]);
*(__m128i*)(Y + ) = _mm_add_epi16(*(__m128i*)&Y[], *(__m128i*)&X[]);
*(__m128i*)(Y + ) = _mm_add_epi16(*(__m128i*)&Y[], *(__m128i*)&X[]);
*(__m128i*)(Y + ) = _mm_add_epi16(*(__m128i*)&Y[], *(__m128i*)&X[]);
*(__m128i*)(Y + ) = _mm_add_epi16(*(__m128i*)&Y[], *(__m128i*)&X[]);
*(__m128i*)(Y + ) = _mm_add_epi16(*(__m128i*)&Y[], *(__m128i*)&X[]);
*(__m128i*)(Y + ) = _mm_add_epi16(*(__m128i*)&Y[], *(__m128i*)&X[]);
*(__m128i*)(Y + ) = _mm_add_epi16(*(__m128i*)&Y[], *(__m128i*)&X[]);
*(__m128i*)(Y + ) = _mm_add_epi16(*(__m128i*)&Y[], *(__m128i*)&X[]);
*(__m128i*)(Y + ) = _mm_add_epi16(*(__m128i*)&Y[], *(__m128i*)&X[]);
*(__m128i*)(Y + ) = _mm_add_epi16(*(__m128i*)&Y[], *(__m128i*)&X[]);
*(__m128i*)(Y + ) = _mm_add_epi16(*(__m128i*)&Y[], *(__m128i*)&X[]);
*(__m128i*)(Y + ) = _mm_add_epi16(*(__m128i*)&Y[], *(__m128i*)&X[]);
*(__m128i*)(Y + ) = _mm_add_epi16(*(__m128i*)&Y[], *(__m128i*)&X[]);
*(__m128i*)(Y + ) = _mm_add_epi16(*(__m128i*)&Y[], *(__m128i*)&X[]);
*(__m128i*)(Y + ) = _mm_add_epi16(*(__m128i*)&Y[], *(__m128i*)&X[]);
*(__m128i*)(Y + ) = _mm_add_epi16(*(__m128i*)&Y[], *(__m128i*)&X[]);
*(__m128i*)(Y + ) = _mm_add_epi16(*(__m128i*)&Y[], *(__m128i*)&X[]);
}
_mm_add_epi16可以一次性完成16个short类型的数据的加法,比传统的add指令快了很多倍。
由于_mm_add_epi16是这对短整形数据进行的处理,因此,一般情况下改指令所能处理的半径不能大于127,如果需要大于127,则需要修改过程序中的short类型为int,同时需要使用_mm_add_epi32指令,这样程序的速度会有所下降。
经过测试,在我的I5的台式机中,1024*768图像在直方图更新上所需要的平均之间约为30ms,相比局部算法的核心就算部分时间(比如上述的求最大值),可能大部分耗时并不在这里。
附件的代码中有个完整的测试工程,并有我目前所有的TMatrix结构的完整代码,我以后的文章都将以改结构为依托进行处理。
代码还共享了很多处理的函数,我很自信一定值得朋友去学习的。
这种前后依赖的算法都有一个很致命的缺点,就是不可以并行,把图像分段处理,也会造成过多初始化耗时。
代码下载地址:http://files.cnblogs.com/files/Imageshop/BaseFile.rar
****************************作者: laviewpbt 时间: 2015.4.20 联系QQ: 33184777 转载请保留本行信息**********************
任意半径局部直方图类算法在PC中快速实现的框架。的更多相关文章
- 图像处理之优化---任意半径局部直方图类算法在PC中快速实现的框架
在图像处理中,局部算法一般来说,在很大程度上会获得比全局算法更为好的效果,因为他考虑到了图像领域像素的信息,而很多局部算法可以借助于直方图获得加速.同时,一些常规的算法,比如中值滤波.最大值滤波.最小 ...
- 基于FPGA的HDTV视频图像灰度直方图统计算法设计
随着HDTV的普及,以LCD-TV为主的高清数字电视逐渐进入蓬勃发展时期.与传统CRT电视不同的是,这些高清数字电视需要较复杂的视频处理电路来驱动,比如:模数转换(A/D Converter).去隔行 ...
- 迭代硬阈值类算法总结||IHT/NIHT/CGIHT/HTP
迭代硬阈值类(IHT)算法总结 斜风细雨作小寒,淡烟疏柳媚晴滩.入淮清洛渐漫漫. 雪沫乳花浮午盏,蓼茸蒿笋试春盘.人间有味是清欢. ---- 苏轼 更多精彩内容请关注微信公众号 "优化与算法 ...
- DWA局部路径规划算法论文阅读:The Dynamic Window Approach to Collision Avoidance。
DWA(动态窗口)算法是用于局部路径规划的算法,已经在ROS中实现,在move_base堆栈中:http://wiki.ros.org/dwa_local_planner DWA算法第一次提出应该是1 ...
- Python实现图像直方图均衡化算法
title: "Python实现图像直方图均衡化算法" date: 2018-06-12T17:10:48+08:00 tags: [""] categorie ...
- LLE局部线性嵌入算法
非线性降维 流形学习 算法思想有些类似于NLM,但是是进行的降维操作. [转载自] 局部线性嵌入(LLE)原理总结 - yukgwy60648的博客 - CSDN博客 https://blog.csd ...
- 关于Python类的多继承中的__mro__属性使用的C3算法以及继承顺序解释
刚刚学到类的多继承这个环节,当子类继承多个父类时,调用的父类中的方法具体是哪一个我们无从得知,为此,在Python中有函数__mro__来表示方法解析顺序. 当前Python3.x的类多重继承算法用的 ...
- 记录我对'我们有成熟的时间复杂度为O(n)的算法得到数组中任意第k大的数'的误解
这篇博客记录我对剑指offer第2版"面试题39:数组中出现次数超过一半的数字"题解1的一句话的一个小误解,以及汇总一下涉及partition算法的相关题目. 在剑指offer第2 ...
- 任意promise串行执行算法 - 童彪
// 任意promise串行执行算法 - 童彪 function runAllPromise() { var p1 = new Promise((resove, reject) => { s ...
随机推荐
- Android消息传递之EventBus 3.0使用详解
前言: 前面两篇不仅学习了子线程与UI主线程之间的通信方式,也学习了如何实现组件之间通信,基于前面的知识我们今天来分析一下EventBus是如何管理事件总线的,EventBus到底是不是最佳方案?学习 ...
- C++服务器开发之笔记三
为什么需要原子性操作? 我们考虑一个例子:(1)x++这个常见的运算符在内存中是怎样操作的?从内存中读x的值到寄存器中,对寄存器加1,再把新值写回x所处的内存地址 若是有两个线程同时对同一个变量++, ...
- 不太被人提起的%%lockres%%的妙用
%%lockres%% 这个值似乎很少被大家提到,甚至微软在官方文档中. 它返回是一个Hash Value,看乎这个值没什么用. 后来在实践也有它的妙用之处,比如在出现性能问题如LOCK时,一般先通过 ...
- Linux下的磁盘分割和文件系统
一.各硬件装置在Linux下的文件名 1.IDE硬盘机 在Linux内的文件名: /dev/hd[a-d] (a-d 刚好是四个这个是有原因的具体如下) 解释:以 IDE 接口来说,由于一个 IDE ...
- javascript的变量作用域--对比js、php和c的for循环
为什么要写这篇文章呢?主要是给自己提个醒,js的水很深,需要小心点儿才能趟过去,更何况自己不是专业人士,那就得更加小心了. 看下面的js代码: <!DOCTYPE html> <ht ...
- 单链表的C++实现(采用模板类)
采用模板类实现的好处是,不用拘泥于特定的数据类型.就像活字印刷术,制定好模板,就可以批量印刷,比手抄要强多少倍! 此处不具体介绍泛型编程,还是着重叙述链表的定义和相关操作. 链表结构定义 定义单链表 ...
- 在mongoose中使用$match对id失效的解决方法
Topic.aggregate( //{$match:{_id:"5576b59e192868d01f75486c"}}, //not work //{$match:{title: ...
- ubuntu 入门
ubuntu 系统设置不全sudo apt-get install ubuntu-desktop uget aria2:下载工具http://www.xitongzhijia.net/xtjc/201 ...
- php调试工具——XDebug使用
下面以windows平台和Aptana Studio为例,介绍XDdebug的使用. 1.安装XDebug 1)下载php的XDebug扩展.dll文件,官网下载地址是https://xdebug.o ...
- JSP简单记录
JSP,全称是Java Server Page,是运行在服务器端的页面,是建立在Servlet规范的动态网页技术,JSP文件在第一次请求时,会被编译成Servlet,所以JSP也可以看成是运行中的Se ...