SSE图像算法优化系列二十四: 基于形态学的图像后期抗锯齿算法--MLAA优化研究。

  偶尔看到这样的一个算法,觉得还是蛮有意思的,花了将近10天多的时间研究了下相关代码。

       以下为百度的结果:MLAA全称Morphological Antialiasing,意为形态   

   如上面两图,左侧图中树叶的边缘有明显的锯齿状图像,而右侧为经过MLAA算法处理后的图,边缘光滑了许多,而且其他部位未受任何的画质影响。

  关于这方面的论文和资料主要有Morphological Antialiasing.pdf ,Intel官方还对改算法进行了一些额外的说明,详见:https://software.intel.com/sites/default/files/m/d/4/1/d/8/MLAA.pdf,并且附带了相关源代码,代码可从https://software.intel.com/zh-cn/articles/morphological-antialiasing-mlaa-sample处下载。我们重点来研读和改进下这部分代码。

  下载代码后,找到\SAA-samples\SAA文件夹下的 MLAAPostProcess.h和MLAAPostProcess.cpp文件,这就是我们最关心的CPU实现的代码。

  根据论文的描述,MLAA算法共包含3个步骤:

  1、寻找在特定的图像像素之间的不连续性,在有些图像中梯度幅值较大的并不是边缘点。

  2、确定预定模式,确定渲染的图像。

  3、在预定模式中进行领里边缘色彩混合处理。确定模式中的相应模板。

    下面的文章如果你没有看过代码或者没有看过论文,你根本不知道我在说什么。

  第一步的计算连续性,在实现上实际是计算一个像素点和其右侧及下方一个像素的颜色差异绝对值的大小,如果某个点和其下方像素的 AbsDiff大于某个指定的阈值,则设置这个点为水平方向边缘的标记(EdgeFlagH),如果和其右侧像素的颜色差异大于阈值,则设置这个点为为垂直方向边缘的标记(EdgeFlagV)。一个点可以只是水平边缘或垂直边缘,也可以只是其中一个,或者两者都不是。

  在获得连续性的基础上,第二步是沿着图像的某一个方向,比如宽度方向分析边缘的形状,这里可以有Z型,U形和L型,针对不同的形状我们有不同的处理方式。

  最后,就是在获得形状后,按照一定的规则对这些比较硬性的拐角处(L和Z型都是直角的弯),进行融合和柔化。

  由于MLAA算法的初衷是处理GPU初处理的图,因此其主要针对的图像必然是32位图像,而且图像的宽度和高度一般来说都是4的倍数,因此,在Intel给出的代码中我们可以看到都是处理BGRA格式的图像的。

  具体到代码实时上,因为是和显示打交道的,因此 ,算法的实时性必须有可靠的保证,不然这个算法粗在的意义就会大打折扣,网络上说人有提供了改算法的GPU实现,但是同时又提到那个GPU代码还不如没有。我想也是,Intel作为改算法的提出者,所分享的代码确实还是相当又学习意义的,我这里来稍微分析下。

  首先是不连续性的相关代码,对于32位图形,Intel只计算其BGR三通道和周边像素的差异,当三通道其中一个通道的差异绝对值大于16时,我们就认为这个点是某个方位的边缘处。另外,在Intel的代码中,其将这些标志信息直接隐藏到了BGRA的A通道中,这个可能于DDS格式有关,DDS格式有一些并没有用到所有的Alpha的8位信息。这样做的一个好处是不需要额外的内存用来保存边缘标志。

  为了速度起见,同时考虑在CPU端运行,这部分可充分利用SSE进行优化。我们先贴出Intel的相关代码进行分析。

复制代码
//------------------------------------------------------------------------------------------------------------------- // This task analyzes the color buffer for discontinuities between pixels to set the edge flags in the work buffer.////-------------------------------------------------------------------------------------------------------------------void MLAAFindDiscontinuitiesTask(unsigned char *Src, int Width, int Height, int Stride) {     for (unsigned int Y = 0; Y < Height; Y += 4)     {         unsigned char *LinePS = Src + Y * Stride;         for (unsigned int X = 0; X < Width; X += 4)         {             // Load pixel block from color buffer. vPixels0 contains the 4 pixels indexed by iRow and iCol,              // vPixels1, the 4 pixels just below the pixels in vPixels0, and so on and so forth.            __m128i vPixels0 = _mm_loadu_si128((__m128i *)LinePS);             __m128i vPixels1 = _mm_loadu_si128((__m128i *)(LinePS + Stride));             __m128i vPixels2 = _mm_loadu_si128((__m128i *)(LinePS + 2 * Stride));             __m128i vPixels3 = _mm_loadu_si128((__m128i *)(LinePS + 3 * Stride));             __m128i vPixels4 = (Y == Height - 4) ?                 vPixels3 // For the last block (vertically), add a virtual row by duplicating the last real block row.                : _mm_loadu_si128((__m128i *)(LinePS + 4 * Stride));                  // zero alpha, we are using it to store discontinuity flags.            __m128i vZeroAlpha = _mm_set1_epi32(0x00FFFFFF);             vPixels0 = _mm_and_si128(vPixels0, vZeroAlpha);             vPixels1 = _mm_and_si128(vPixels1, vZeroAlpha);             vPixels2 = _mm_and_si128(vPixels2, vZeroAlpha);             vPixels3 = _mm_and_si128(vPixels3, vZeroAlpha);                  // Check for horizontal pixel discontinuities, one row of 4 pixels checked per call.             // (we compare a row of 4 pixels with its bottom neighbor)            ComparePixelsSSE(vPixels0, vPixels1, EdgeFlagH);             ComparePixelsSSE(vPixels1, vPixels2, EdgeFlagH);             ComparePixelsSSE(vPixels2, vPixels3, EdgeFlagH);             ComparePixelsSSE(vPixels3, vPixels4, EdgeFlagH);                  // Transpose pixel block so we can use ComparePixelsSSE to check for vertical discontinuities.            _MM_TRANSPOSE4_PS(                 reinterpret_cast<__m128&>(vPixels0),                 reinterpret_cast<__m128&>(vPixels1),                 reinterpret_cast<__m128&>(vPixels2),                 reinterpret_cast<__m128&>(vPixels3));             //_MM_TRANSPOSE4_EPI32(vPixels0, vPixels1, vPixels2, vPixels3);                 vPixels4 = (X == Width - 4) ?                 vPixels3 
                        
关键字:
50000+
5万行代码练就真实本领
17年
创办于2008年老牌培训机构
1000+
合作企业
98%
就业率

联系我们

电话咨询

0532-85025005

扫码添加微信