在图像处理中,局部算法一般来说,在很大程度上会获得比全局算法更为好的效果,因为他考虑到了图像领域像素的信息,而很多局部算法可以借助于直方图获得加速。同时,一些常规的算法,比如中值滤波、最大值滤波、最小值滤波、表面模糊等等都可以通过局部直方图进行加速。而传统的获取局部直方图计算量很大,特别是半径增加时,耗时会成平方关系增加。一些局部算法只有在半径较大时才会获得很好的效果,因此,必须找到一种合适的加速计算局部直方图的方式。

在参考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中快速实现的框架。的更多相关文章

  1. 图像处理之优化---任意半径局部直方图类算法在PC中快速实现的框架

    在图像处理中,局部算法一般来说,在很大程度上会获得比全局算法更为好的效果,因为他考虑到了图像领域像素的信息,而很多局部算法可以借助于直方图获得加速.同时,一些常规的算法,比如中值滤波.最大值滤波.最小 ...

  2. 基于FPGA的HDTV视频图像灰度直方图统计算法设计

    随着HDTV的普及,以LCD-TV为主的高清数字电视逐渐进入蓬勃发展时期.与传统CRT电视不同的是,这些高清数字电视需要较复杂的视频处理电路来驱动,比如:模数转换(A/D Converter).去隔行 ...

  3. 迭代硬阈值类算法总结||IHT/NIHT/CGIHT/HTP

    迭代硬阈值类(IHT)算法总结 斜风细雨作小寒,淡烟疏柳媚晴滩.入淮清洛渐漫漫. 雪沫乳花浮午盏,蓼茸蒿笋试春盘.人间有味是清欢. ---- 苏轼 更多精彩内容请关注微信公众号 "优化与算法 ...

  4. DWA局部路径规划算法论文阅读:The Dynamic Window Approach to Collision Avoidance。

    DWA(动态窗口)算法是用于局部路径规划的算法,已经在ROS中实现,在move_base堆栈中:http://wiki.ros.org/dwa_local_planner DWA算法第一次提出应该是1 ...

  5. Python实现图像直方图均衡化算法

    title: "Python实现图像直方图均衡化算法" date: 2018-06-12T17:10:48+08:00 tags: [""] categorie ...

  6. LLE局部线性嵌入算法

    非线性降维 流形学习 算法思想有些类似于NLM,但是是进行的降维操作. [转载自] 局部线性嵌入(LLE)原理总结 - yukgwy60648的博客 - CSDN博客 https://blog.csd ...

  7. 关于Python类的多继承中的__mro__属性使用的C3算法以及继承顺序解释

    刚刚学到类的多继承这个环节,当子类继承多个父类时,调用的父类中的方法具体是哪一个我们无从得知,为此,在Python中有函数__mro__来表示方法解析顺序. 当前Python3.x的类多重继承算法用的 ...

  8. 记录我对'我们有成熟的时间复杂度为O(n)的算法得到数组中任意第k大的数'的误解

    这篇博客记录我对剑指offer第2版"面试题39:数组中出现次数超过一半的数字"题解1的一句话的一个小误解,以及汇总一下涉及partition算法的相关题目. 在剑指offer第2 ...

  9. 任意promise串行执行算法 - 童彪

      // 任意promise串行执行算法 - 童彪 function runAllPromise() { var p1 = new Promise((resove, reject) => { s ...

随机推荐

  1. Compile FreeCAD on Windows

    Compile FreeCAD on Windows eryar@163.com 1.Introduction FreeCAD是一个参数化的三维造型软件,主要用于任意大小的实际模型的设计.参数化的建模 ...

  2. three.js笔记

    /*** 场景(scene) ***/ var scene = new THREE.Scene(); // 创建场景 scene.add(x); // 插入场景 /*** 相机(camera) *** ...

  3. Vertica license导入最佳实践

    常用的方法,我们可以通过admintools字符图形工具来导入license, 7 -> 5 -> 选择库 -> 输入license文件全路径 -> Accept -> ...

  4. 《HelloGitHub月刊》第09期

    <HelloGitHub>第09期 兴趣是最好的老师,<HelloGitHub>就是帮你找到兴趣! 前言 转眼就到年底了,月刊做到了第09期,感谢大家一路的支持和帮助

  5. 4.C#WinForm基础图片(显示和隐藏)

    要求: 软件上有一张图片,默认是隐藏的.用户在文本框中输入身份证号(131226198105223452),点击按钮,如果年龄大于18岁,则显示图片. 知识点: 取当前年份,Date Time Now ...

  6. Docker容器环境下ASP.NET Core Web API应用程序的调试

    本文主要介绍通过Visual Studio 2015 Tools for Docker – Preview插件,在Docker容器环境下,对ASP.NET Core Web API应用程序进行调试.在 ...

  7. ASP.NET使用HttpModule压缩并删除空白Html请求

    当我们压缩我的Response后再传到Client端时,可以明显节省宽带. 提升Site的性能. 现在的浏览器大部分都支持Gzip,Deflate压缩. 同时我们还可以删除一些空白段,空行,注释等以使 ...

  8. Spark 入门

    Spark 入门 目录 一. 1. 2. 3. 二. 三. 1. 2. 3. (1) (2) (3) 4. 5. 四. 1. 2. 3. 4. 5. 五.         Spark Shell使用 ...

  9. Contents

    Contents 占位 ---------------------------------- Python3中的字符串函数学习总结

  10. JDBC 详解(转载)

    原文链接:http://blog.csdn.net/cai_xingyun/article/details/41482835 什么是JDBC? Java语言访问数据库的一种规范,是一套API JDBC ...