首页 > 其他分享 >[快速阅读八] Matlab中bwlookup的实现及其在计算二值图像的欧拉数、面积及其他morph变形中的应用。

[快速阅读八] Matlab中bwlookup的实现及其在计算二值图像的欧拉数、面积及其他morph变形中的应用。

时间:2024-10-24 08:50:11浏览次数:1  
标签:epi8 Index morph mm bwlookup 像素 Matlab 255 set1

  以前看过matlab的bwlookup函数,但是总感觉有点神秘,一直没有去仔细分析,最近在分析计算二值图像的欧拉数时,发现自己写的代码和matlab的总是对不少,于是又去翻了下matlab的源代码,看到了matlab里实现欧拉数的代码非常简单,如下所示:

if n==4
    lut = 4*[0 0.25 0.25 0 0.25 0  .5 -0.25 0.25  0.5  0 -0.25 0 ...
             -0.25 -0.25 0] + 2;
else
    lut = 4*[0 0.25 0.25 0 0.25 0 -.5 -0.25 0.25 -0.5  0 -0.25 0 ...
             -0.25 -0.25 0] + 2;
end
% Need to zero-pad the input
b = padarray(a,[1 1],'both');

weights = bwlookup(b,lut);
if coder.isColumnMajor
    e = (sum(weights(:),'double') - 2*numel(b)) / 4;
else
    e = (sum(sum(weights,2,'double'),1,'double') - 2*numel(b)) / 4;
end

  在内部就是调用了bwlookup函数,那没办法了,就仔细看了M的帮助文档,发现原来这个函数真的非常简单,我们贴下M的帮助文档:

  A = bwlookup(BW,lut)

  The bwlookup function performs these steps to determine the value of each pixel in the processed image A:

  • Locate the pixel neighborhood in input image BW based on the coordinates of the target pixel in A. The function zero-pads border pixels of image BW when the neighborhood extends past the edge of BW.

  • Calculate an index, idx, based on the binary pixel pattern of the neighborhood.

  • Set the target pixel in A as the value of the lookup table at the index idx, in other words, the value of lut(idx).

2-by-2 Neighborhood Lookup

For 2-by-2 neighborhoods, there are four pixels in each neighborhood. Each binary pixel has two possible states, therefore the total number of permutations is 24 and the length of the lookup table lut is 16.

To find the value of the target output pixel at (row, column) coordinate (r,c), bwlookup uses the 2-by-2 pixel neighborhood in the input binary image BW whose top left pixel is at coordinate (r,c). The index idx into the lookup table is the weighted sum of the four pixels in the neighborhood, plus 1.

Pixel weights start with a weight of 1 for the top left pixel in the neighborhood, and increase as powers of two along rows then along columns, with a final weight of 8 for the bottom right pixel.

3-by-3 Neighborhood Lookup

For 3-by-3 neighborhoods, there are nine pixels in each neighborhood. Each binary pixel has two possible states, therefore the total number of permutations is 29 and the length of the lookup table lut is 256.

To find the value of the target output pixel at (row, column) coordinate (r,c), bwlookup uses the 3-by-3 pixel neighborhood in the input binary image BW centered on coordinate (r,c). The index idx into the lookup table is the weighted sum of the nine pixels in the neighborhood, plus 1.

Pixel weights start with a weight of 1 for the top left pixel in the neighborhood, and increase as powers of two along rows then along columns, with a final weight of 256 for the bottom right pixel.

  简单的说,这个查找表只有2种可能,一个是表的元素是16个,一种是由512个元素,其中16个元素时,查表的依据是以当前点为起点,向右向下各扩展一个像素范围,得到一个2*2领域4个像素,4个像素,每个像素也就只有2种可能的取值,这样就有16种可能的组合,对应16个元素。

  当表的元素是512个时,查表的依据是以当前点为中心点,向左向右,向上向下各扩展一个像素,得到一个3*3的领域9个像素,同样每个像素也只有2种可能取值,所以共有1<<9即512种取值。

  因为涉及到领域,所有在原图边缘的地方会有越界的图像,这个时候的原则是填充0,而不是复制最近的像素。

  这样,当表中有不同的内容时,就可以实现不同的结果。

  我们以16个元素的表为例,假定 lut[16] ={ 6 3 16 11 7 14 8 5 15 1 2 4 13 9 10 12},

  对于下面的一个4*4的二值数据:

   0   0   1   1
   0   0   1   1
   1   1   0   0
   1   1   0   0

  如果当前的点位于第二行第一列,则其2*2的领域范围的像素信息为:

   0   0
   1   1
  按照位置信息即对应的值计算索引为: 1 + 0 * 1 + 1 * 2 + 0 * 4 + 1 * 8 = 11, 对用的查表结果为 lut[11] = 2。
  使用类似的计算方法可以得到其他位置经过查表后的结果为:
    6    13    12    11
     2     8    14     3
    12    11     6     6
    14     3     6     6

 注意,以上所有的下标起点都是1(matlab内部的约定),而且行列顺序和我们常用的C语言的二维数组也是掉了边的。
除了刚刚说的欧拉数的计算(其实没有搞懂欧拉数里的那个查找表是如何设计的,且8领域的欧拉数也是用的2*2的表,而不是3*3的),我们以matlab的bwarea函数为例,说明这个函数的价值。
  matlab中关于bwarea函数的定义为:

  bwarea 通过对图像中每个像素的面积求和来估计图像中所有 on 像素的面积。单个像素的面积是通过观察其 2×2 邻域来确定的。有六种不同的情形,每种情形表示一个不同面积:

  • 具有零个 on 像素的情形(面积 = 0)

  • 具有一个 on 像素的情形(面积 = 1/4)

  • 具有两个相邻 on 像素的情形(面积 = 1/2)

  • 具有两个对角 on 像素的情形(面积 = 3/4)

  • 具有三个 on 像素的情形(面积 = 7/8)

  • 具有所有四个 on 像素的情形(面积 = 1)

  用图形化的表达方式上述六种情况对应如下(为了显示,使用绿色表示黑色值,红色表示白色值):

                                                          

索引值              0     1      2    3      4    5    6       7     8       9       10     11      12     13      14      15  

面积值      0    1/4     1/4   1/2    1/4   1/2   3/4     7/8   1/4     3/4     1/2    7/8     1/2      7/8      7/8      1

把面积值放大八倍得到面积值依次为:  0 2 2 4 2 4 6 7 2 6 4 7 4 7 7 8,我们翻看matlab的bwarea函数,可以得到其代码如下:

lut = [0     2     2     4     2     4     6     7     2     6     4     7 ...
       4     7     7     8];

% Need to zero-pad the input.
bb = false(size(b)+2);
bb(2:end-1,2:end-1) = b;

weights = bwlookup(bb,lut);
a = sum(weights(:),[],'double')/8;

  查找表和这里的一摸一样。

  使用查找表的优点是把领域所有的组合提前计算出结果来,而不用到程序里进行一些列复杂的判断,这样在很多情况下是可以提高速度的,比如刚刚这个面积计算的东西,如果在循环内部做,则要做N种判断,那如果是涉及到3*3的领域,那就更为复杂了。 

  朴素的来说,有了bwlookup的原理,要把这个算法移植到C++中,就是一个比较简单的过程了,假如是处理16元素的表,也就是涉及到了4个像素,假定他们的值分别为P0\P1\P2\P3,则最基本的C代码如下所示:

int Index = 0;
if (P0 == 255)    Index += 1;
if (P1 == 255)    Index += 2;
if (P2 == 255)    Index += 4;
if (P3 == 255)    Index += 8;
LinePD[X] = Lut[Index];

  处理512个元素的过程也是类似的,只不过索引值多加了几个(注意,这里用255表示白色,而不是1)。

  如果考虑指令集优化,一个自然的翻译过程如下所示:

    __m128i Index = _mm_setzero_si128();
    Index = _mm_blendv_epi8(Index, _mm_add_epi8(Index, _mm_set1_epi8(1)), _mm_cmpeq_epi8(P0, _mm_set1_epi8(255)));
    Index = _mm_blendv_epi8(Index, _mm_add_epi8(Index, _mm_set1_epi8(2)), _mm_cmpeq_epi8(P1, _mm_set1_epi8(255)));
    Index = _mm_blendv_epi8(Index, _mm_add_epi8(Index, _mm_set1_epi8(4)), _mm_cmpeq_epi8(P2, _mm_set1_epi8(255)));
    Index = _mm_blendv_epi8(Index, _mm_add_epi8(Index, _mm_set1_epi8(8)), _mm_cmpeq_epi8(P3, _mm_set1_epi8(255)));
    _mm_storeu_si128((__m128i*)(LinePD + X), _mm_shuffle_epi8(Lut128, Index));

  一次性处理16个字节,最为关键的一点是,这个查表可以方便的直接使用_mm_shuffle_epi8非常高效的实现,这个在我前期的文章里多次提到(16个字节表的查找,我们假定lut的类型为字节类型,这个在实际的应用中也足够了),

   这个代码的性能提升相对于C++来说是非常可观的,大概又4倍多的速度提升。   仔细上述代码其实还有提高的地方,我们使用_mm_blendv_epi8来进行结果的混合, 而混合的标志是值是否等于255,这里用_mm_cmpeq_epi8做判断,而_mm_cmpeq_epi8的结果就是如果P等于255,则返回255,否则返回0,那这个不就是P本省吗,所以根本就不用做这个判断,这样代码就变为:
    __m128i Index = _mm_setzero_si128();
    Index = _mm_blendv_epi8(Index, _mm_add_epi8(Index, _mm_set1_epi8(1)), P0, _mm_set1_epi8(255));
    Index = _mm_blendv_epi8(Index, _mm_add_epi8(Index, _mm_set1_epi8(2)), P1, _mm_set1_epi8(255));
    Index = _mm_blendv_epi8(Index, _mm_add_epi8(Index, _mm_set1_epi8(4)), P2, _mm_set1_epi8(255));
    Index = _mm_blendv_epi8(Index, _mm_add_epi8(Index, _mm_set1_epi8(8)), P3, _mm_set1_epi8(255));
    _mm_storeu_si128((__m128i*)(LinePD + X), _mm_shuffle_epi8(Lut128, Index));

  另外,再观察这个C语言的特殊性,我们可以把上述C代码修改为:

    int Index = 0;
    Index += (1 & P0);
    Index += (2 & P1);
    Index += (4 & P2);
    Index += (8 & P3);
    LinePD[X] = Lut[Index];

  即直接使用位运算替换掉那个判断,这样对应的SSE指令也可以进行修改为如下代码:

    __m128i Index = _mm_setzero_si128();
    Index = _mm_add_epi8(Index, _mm_and_si128(_mm_set1_epi8(1), P0));
    Index = _mm_add_epi8(Index, _mm_and_si128(_mm_set1_epi8(2), P1));
    Index = _mm_add_epi8(Index, _mm_and_si128(_mm_set1_epi8(4), P2));
    Index = _mm_add_epi8(Index, _mm_and_si128(_mm_set1_epi8(8), P3));
    //    可以直接接用shuffle实现查找表
    _mm_storeu_si128((__m128i*)(LinePD + X), _mm_shuffle_epi8(Lut128, Index));

  相比于原始的SSE代码,这个改动也约有30%的性能提升的。

  对于512个元素的表,情况会有所不同,因为索引值大于了255,所以已经无法用字节类型来保存累加后得到的索引了,至少的用ushort类型,但是呢,前8个位置的索引相加还是不会超出字节类型的,所以前8个位置还是可以一次性处理16个像素,到最后一个位置时,单独转换为16位,然后再接用2次16数据的处理指令,就可以一次性得到16个字节对应的查找表索引。

  但是这个时候,由于表的大小时512,所以已经无法使用SSE去优化这个查表功能了,只能把索引值单个的提取出来,然后用普通的C语言去执行代码。如果CPU能支持AVX2,那么AVX2倒是又有关指令能直接查表,不过也要做很多的改造工程。

  我们测试,对于512个元素的表,优化后的SSE指令处理一副 3000*2000的二值图,大概耗时在2ms不到,速度还是相当的快的。

  搜索matlab的代码,除了bweuler及bwarea内使用了bwlookup,另外就是bwmorph里也大量的使用了bwlookup,而且都是使用的512个元素的表,也就是说使用3*3的领域,我们看bwmorph的帮助文档,有很多相关内容:

   这些对应的查找表在 MATLAB\R2023b\toolbox\images\images\+images\+internal 目录下以lut开头的文件中可以找到,比如说,这个clean的表内容如下:

  

   这些表的内容都是提前设计好的,然后可以多次重复的调用,从而得到某种效果。

  一般来说,这种3*3的领域算子,在迭代了一定的次数后效果就不会有变化了。

   我们在morph中也能看到一些常用的算子,比如这个remove就可以得到二值图像的边界,这个majority可以平滑噪音(和我博客里专门讲的那个majority效果还是有差异的)。

               

        原图                  remove              majority  

             

        fatten                       skeleton                 thin

   个人感觉这个方法在处理一些比较复杂的领域信息时还是很有帮助的,特别是不方便用判断条件一个一个的写的,如果能提前把这个表弄出来,那就效率能得到很大的提升的。

   在个人的SSE Demo里,也集成了这个算法,其内容在Binary(二值处理)--》Processing(后处理)-->> Morph(形态学)中,有兴趣的朋友可以看看。

   SSE Demo下载地址: https://files.cnblogs.com/files/Imageshop/SSE_Optimization_Demo.rar

翻译

搜索

复制

<iframe height="240" width="320"></iframe>

标签:epi8,Index,morph,mm,bwlookup,像素,Matlab,255,set1
From: https://www.cnblogs.com/Imageshop/p/18496651

相关文章

  • 基于毕奥-萨伐尔定律的交流电机的4极旋转磁场matlab模拟与仿真
    1.课题概述基于毕奥-萨伐尔定律的交流电机的4极旋转磁场,对比不同定子半径,对比2级旋转磁场。 2.系统仿真结果 3.核心程序与模型版本:MATLAB2022a%合并位置和电流P=[xaxa_xbxb_xcxc_];I=[IaIa_IbIb_IcIc_];index=1;%初始化索引%......
  • 基于三帧差算法的运动目标检测系统FPGA实现,包含testbench和MATLAB辅助验证程序
    1.算法运行效果图预览(完整程序运行后无水印)   将FPGA的仿真结果导入到MATLAB中,分别得到MATLAB的结果和FPGA的结果:   2.算法运行软件版本vivado2019.2 matlab2022a 3.部分程序(完整版代码包含详细中文注释和操作步骤视频)`timescale1ns/1ps////C......
  • 【旧文重发】MATLAB 通过函数封装一劳永逸地解决线性规划与运输问题的linprog的标准化
    这篇随笔原本是我上实验课时候的笔记,2023年7月曾经在CSDN平台上发布过。今天恰好有朋友跟我问起MATLAB自带的求解器输入很不直观的问题,我打开这个文章发给他的时候发现自己一年前写的LaTeX公式依托答辩,所以重打了一遍。再加上由于CSDN平台的持续摆烂,终于是用不下去......
  • 基于人脸识别的Matlab代码实现
    人脸识别的Matlab代码实现 1概述人脸检测(FaceDetection)是在输入图像中确定所有人脸(如果存在)的位置,大小,位姿的过程。人脸检测作为人脸信息处理中的一项关键技术,近年来成为模式识别与计算机视觉领域一项受到普遍重视,研究十分活跃的课题﹐人脸检测问题最初来源于人脸......
  • 基于MATLAB车道检测与跟踪
    MATLAB车道检测与跟踪读了车道检测这个论文,我理解了利用matlab对车道识别算法进行仿真研究,从仿真的结果中提出具有一定实时性鲁棒性的识别方法。车道检测是智能车辆发展的智能因素。近年来对这项目的研究都是针对特定的环境和道路状况给出了不同的解决方案。近年来,自主驾驶技......
  • 基于MATLAB(DCT DWT)
    第三章图像数字水印的方案3.1图像数字水印的技术方案在数据库中存储在国际互联网上传输的水印图像一般会被压缩,有时达到很高的压缩比。因此,数字水印算法所面临的第一个考验就是压缩。JPEG和EZW(EmbeddedZero-TreeWavelet)压缩是最常见的两种压缩方法。JPEG是基于离散余弦变......
  • matlab求冲击输入拉普拉斯变换,和响应
    在MATLAB中,求解冲击输入的拉普拉斯变换以及系统的冲击响应通常涉及以下几个步骤:定义冲击输入:在MATLAB中,冲击输入通常用狄拉克δ函数(Diracdeltafunction)表示,可以使用dirac函数来创建。计算拉普拉斯变换:使用laplace函数计算冲击输入的拉普拉斯变换。计算系统冲击响应:如果......
  • 5G NR GSCN计算SSB中心频率MATLAB实现
    本期给大家带来5GNR中已知GSCN如何计算SSB的中心频率,用MATLAB实现,参考3GPP38.104下图是GSCN与SSB中心频率换算关系。函数说明:函数的入参是GSCN号函数的输出是对应的SSB中心频率,单位MHZfunction freqency =nr_5g_gscn2freq(gscn)%%%%author:老牛% codingti......
  • 【论文复现】输电线路故障判别与测距研究(Matlab代码、Simulink仿真实现)
       ......
  • 【论文复现】输电线路故障判别与测距研究(Matlab代码、Simulink仿真实现)
       ......