SSE图像算法优化系列八:自然饱和度(Vibrance)算法的模拟实现及其SSE优化(附源码,可作SSE图像入门,Vibrance算法也不过用来简单的肤色调整)。SSE图像算法优化系列八:自然饱和度(Vibrance)算法的套实现及其SSE优化(附源码,可看成SSE图像入门,Vibrance算法也不过用以简单的肤色调整)。

  Vibrance这个单词搜索翻译一般震荡,抖动或者是嘹亮、活力,但是官方的词汇里还从未出现了本饱和度这个词,也非知情就之Adobe中文翻译人员怎么会如此处理。但是咱看看PS对斯力量的分解:

  Vibrance这个单词搜索翻译一般震荡,抖动或者是高、活力,但是官方的词汇里还从未出现过当饱和度这个词,也未晓得这之Adobe中文翻译人员怎么会如此处理。但是咱看PS对这效果的讲:

       Vibrance: Adjusts the
saturation so that clipping is minimized as colors approach full
saturation. This setting changes the saturation of all lower-saturated
colors with less effect on the higher-saturated colors. Vibrance also
prevents skin tones from becoming oversaturated.

       Vibrance: Adjusts the
saturation so that clipping is minimized as colors approach full
saturation. This setting changes the saturation of all lower-saturated
colors with less effect on the higher-saturated colors. Vibrance also
prevents skin tones from becoming oversaturated.

     
 确实是暨饱和度有关的,这样懂中文的翻反而却合理,那么只能怪Adobe的开发者为什么让这个功效于个名字为Vibrance了。

     
 确实是和饱和度有关的,这样懂中文的翻反而却合理,那么只能怪Adobe的开发者为什么吃这个功能于个名字给Vibrance了。

     
 闲话不多说了,其实当饱和度也是近来几单本子的PS才起的功能,在调试有些图片的时刻会发生科学的效能,也得以看作简单的肤色调整的一个算法,比如下面这号小姐,用当饱和度即可以叫其失血过多,也可让他肤色红晕。

     
 闲话不多说了,其实当饱和度也是近年来几只版的PS才出现的机能,在调试有些图片的时光会生出科学的功用,也足以视作简单的肤色调整之一个算法,比如下面就员闺女,用本饱和度即好被它们失血过多,也可以叫他肤色红晕。

     
 图片 1   
 图片 2   
 图片 3

     
 图片 4   
 图片 5   
 图片 6

                                         
   原图                                                                
                                 面色苍白                              
                                                    肤色红晕一点

                                         
   原图                                                                
                                 面色苍白                              
                                                    肤色红晕一点

     
 那么这算法的内在是怎么样兑现的也罢,我没有仔细的去研究他,但是在开源软件PhotoDemon-master(开源地址:https://github.com/tannerhelland/PhotoDemon,visual
basic
6.0的著述,我之无比轻)提供了一个稍微相似之职能,我们贴出他本着转移效果的一对注释:

     
 那么这个算法的内在是怎么贯彻之啊,我没有仔细的夺研究他,但是以开源软件PhotoDemon-master(开源地址:https://github.com/tannerhelland/PhotoDemon,visual
basic
6.0的著作,我的极其爱)提供了一个不怎么相像之意义,我们贴发他针对反效果的一对注释:

'***************************************************************************
'Vibrance Adjustment Tool
'Copyright 2013-2017 by Audioglider
'Created: 26/June/13
'Last updated: 24/August/13
'Last update: added command bar
'
'Many thanks to talented contributer Audioglider for creating this tool.
'
'Vibrance is similar to saturation, but slightly smarter, more subtle. The algorithm attempts to provide a greater boost
' to colors that are less saturated, while performing a smaller adjustment to already saturated colors.
'
'Positive values indicate "more vibrance", while negative values indicate "less vibrance"
'
'All source code in this file is licensed under a modified BSD license. This means you may use the code in your own
' projects IF you provide attribution. For more information, please visit http://photodemon.org/about/license/
'
'***************************************************************************
'***************************************************************************
'Vibrance Adjustment Tool
'Copyright 2013-2017 by Audioglider
'Created: 26/June/13
'Last updated: 24/August/13
'Last update: added command bar
'
'Many thanks to talented contributer Audioglider for creating this tool.
'
'Vibrance is similar to saturation, but slightly smarter, more subtle. The algorithm attempts to provide a greater boost
' to colors that are less saturated, while performing a smaller adjustment to already saturated colors.
'
'Positive values indicate "more vibrance", while negative values indicate "less vibrance"
'
'All source code in this file is licensed under a modified BSD license. This means you may use the code in your own
' projects IF you provide attribution. For more information, please visit http://photodemon.org/about/license/
'
'***************************************************************************

 其中的讲述和PS官方文档的描述来类似之处。

 其中的叙述和PS官方文档的叙说来类似之处。

   我们当贴发出他的中心代码:

   我们在贴有他的主干代码:

     For x = initX To finalX
        quickVal = x * qvDepth
        For y = initY To finalY
            'Get the source pixel color values
            r = ImageData(quickVal + 2, y)
            g = ImageData(quickVal + 1, y)
            b = ImageData(quickVal, y)

            'Calculate the gray value using the look-up table
            avgVal = grayLookUp(r + g + b)
            maxVal = Max3Int(r, g, b)

            'Get adjusted average
            amtVal = ((Abs(maxVal - avgVal) / 127) * vibranceAdjustment)

            If r <> maxVal Then
                r = r + (maxVal - r) * amtVal
            End If
            If g <> maxVal Then
                g = g + (maxVal - g) * amtVal
            End If
            If b <> maxVal Then
                b = b + (maxVal - b) * amtVal
            End If

            'Clamp values to [0,255] range
            If r < 0 Then r = 0
            If r > 255 Then r = 255
            If g < 0 Then g = 0
            If g > 255 Then g = 255
            If b < 0 Then b = 0
            If b > 255 Then b = 255

            ImageData(quickVal + 2, y) = r
            ImageData(quickVal + 1, y) = g
            ImageData(quickVal, y) = b
        Next
    Next
     For x = initX To finalX
        quickVal = x * qvDepth
        For y = initY To finalY
            'Get the source pixel color values
            r = ImageData(quickVal + 2, y)
            g = ImageData(quickVal + 1, y)
            b = ImageData(quickVal, y)

            'Calculate the gray value using the look-up table
            avgVal = grayLookUp(r + g + b)
            maxVal = Max3Int(r, g, b)

            'Get adjusted average
            amtVal = ((Abs(maxVal - avgVal) / 127) * vibranceAdjustment)

            If r <> maxVal Then
                r = r + (maxVal - r) * amtVal
            End If
            If g <> maxVal Then
                g = g + (maxVal - g) * amtVal
            End If
            If b <> maxVal Then
                b = b + (maxVal - b) * amtVal
            End If

            'Clamp values to [0,255] range
            If r < 0 Then r = 0
            If r > 255 Then r = 255
            If g < 0 Then g = 0
            If g > 255 Then g = 255
            If b < 0 Then b = 0
            If b > 255 Then b = 255

            ImageData(quickVal + 2, y) = r
            ImageData(quickVal + 1, y) = g
            ImageData(quickVal, y) = b
        Next
    Next

  很简短的算法,先要出每个像素RGB分量的绝充分价值与平均值,然后求两者之差,之后因输入调节量求来调整量。

  很简单的算法,先要出每个像素RGB分量的不过特别价值和平均值,然后求两者之差,之后根据输入调节量求来调整量。

     
 VB的语法有些人想必不熟悉,我聊开点转翻译成C的代码如下:

     
 VB的语法有些人或许不熟悉,我有点开点转翻译成C的代码如下:

    float VibranceAdjustment = -0.01 * Adjustment;        //       Reverse the vibrance input; this way, positive values make the image more vibrant.  Negative values make it less vibrant.
    for (int Y = 0; Y < Height; Y++)
    {
        unsigned char * LinePS = Src + Y * Stride;
        unsigned char * LinePD = Dest + Y * Stride;
        for (int X = 0; X < Width; X++)
        {
            int Blue = LinePS[0],    Green = LinePS[1],    Red = LinePS[2];
            int Avg = (Blue + Green + Green + Red) >> 2;
            int Max = IM_Max(Blue, IM_Max(Green, Red));
            float AmtVal = (abs(Max - Avg) / 127.0f) * VibranceAdjustment;                        //    Get adjusted average
            if (Blue != Max)    Blue += (Max - Blue) * AmtVal;
            if (Green != Max)    Green += (Max - Green) * AmtVal;
            if (Red != Max)    Red += (Max - Red) * AmtVal;
            LinePD[0] = IM_ClampToByte(Blue);
            LinePD[1] = IM_ClampToByte(Green);
            LinePD[2] = IM_ClampToByte(Red);
            LinePS += 3;
            LinePD += 3;
        }
    }
    float VibranceAdjustment = -0.01 * Adjustment;        //       Reverse the vibrance input; this way, positive values make the image more vibrant.  Negative values make it less vibrant.
    for (int Y = 0; Y < Height; Y++)
    {
        unsigned char * LinePS = Src + Y * Stride;
        unsigned char * LinePD = Dest + Y * Stride;
        for (int X = 0; X < Width; X++)
        {
            int Blue = LinePS[0],    Green = LinePS[1],    Red = LinePS[2];
            int Avg = (Blue + Green + Green + Red) >> 2;
            int Max = IM_Max(Blue, IM_Max(Green, Red));
            float AmtVal = (abs(Max - Avg) / 127.0f) * VibranceAdjustment;                        //    Get adjusted average
            if (Blue != Max)    Blue += (Max - Blue) * AmtVal;
            if (Green != Max)    Green += (Max - Green) * AmtVal;
            if (Red != Max)    Red += (Max - Red) * AmtVal;
            LinePD[0] = IM_ClampToByte(Blue);
            LinePD[1] = IM_ClampToByte(Green);
            LinePD[2] = IM_ClampToByte(Red);
            LinePS += 3;
            LinePD += 3;
        }
    }

  这个的结果和PS的凡比接近的,最起码趋势是大类似的,但是细节要不一样,不过好断定的是来势是对的,如果你一定要是复制PS的结果,我提议您花点时间转移中的有些常数或者计算方式看看。应该能够生出取,国内都有人找出来了。

  这个的结果以及PS的是比较相近的,最起码趋势是那个接近的,但是细节要未相同,不过可以判定的是来势是本着之,如果你得要是复制PS的结果,我提议乃花点时间改中的局部常数或者计算方法看看。应该能起收获,国内就有人搜出了。

     
我们要讲下这个算法的优化及其SSE实现,特别是SSE版本代码是本文的重点。

     
我们任重而道远讲下这个算法的优化及其SSE实现,特别是SSE版本代码是本文的关键。

      第一步优化,去破除不必要计算和除法,很明白,这同一词是本段代码中耗时较为明显的一对

      第一步优化,去解不必要计算和除法,很肯定,这同样句是本段代码中耗时较为强烈的有的

        float AmtVal = (abs(Max –
Avg) / 127.0f) * VibranceAdjustment;     

        float AmtVal = (abs(Max –
Avg) / 127.0f) * VibranceAdjustment;     

  /127.0f可以优化为乘法,同时注意VibranceAdjustment在里未变换,可以管他们成到循环的极度外层,即改呢:

  /127.0f可以优化为乘法,同时注意VibranceAdjustment在内部未换,可以管他们成至循环的极致外层,即改也:

      float VibranceAdjustment = -0.01 * Adjustment / 127.0f;
      float VibranceAdjustment = -0.01 * Adjustment / 127.0f;

  再小心abs里的参数, Max –
Avg,这起必不可少取绝对值吗,最可怜价值难道会比平均值小,浪费时间,最后移呢:

  再小心abs里之参数, Max –
Avg,这起必要取绝对值吗,最要命价值难道会比较平均值小,浪费时间,最后移吧:

      float AmtVal = (Max - Avg) * VibranceAdjustment;
      float AmtVal = (Max - Avg) * VibranceAdjustment;

    这是浮点版本的简单优化,如果非勾选编译器的SSE优化,直接动用FPU,对于一副3000*2000的24员图像耗时在I5的同样尊机械及运行用时盖70毫秒,但立刻不是重要。

    这是浮点版本的大概优化,如果非勾选编译器的SSE优化,直接行使FPU,对于一副3000*2000的24位图像耗时在I5的一模一样光机械及运行用时盖70毫秒,但这不是关键。

  我们来设想某些近似和固化优化。

  我们来设想某些近似和定点优化。

     
 第一咱们把/127移呢/128,这基本不影响效应,同时Adjustment默认的界定也[-100,100],把它为线性扩大一点,比如扩大1.28加倍,扩大至[-128,128],这样以末咱们一次性移位,减少中间的损失,大概的代码如下:

     
 第一我们管/127移也/128,这基本未影响作用,同时Adjustment默认的范围吗[-100,100],把其为线性扩大一点,比如扩大1.28倍,扩大至[-128,128],这样于结尾咱们一次性移位,减少中间的损失,大概的代码如下:

int IM_VibranceI(unsigned char *Src, unsigned char *Dest, int Width, int Height, int Stride, int Adjustment)
{
    int Channel = Stride / Width;
    if ((Src == NULL) || (Dest == NULL))                return IM_STATUS_NULLREFRENCE;
    if ((Width <= 0) || (Height <= 0))                    return IM_STATUS_INVALIDPARAMETER;
    if (Channel != 3)                                    return IM_STATUS_INVALIDPARAMETER;

    Adjustment = -IM_ClampI(Adjustment, -100, 100) * 1.28;        //       Reverse the vibrance input; this way, positive values make the image more vibrant.  Negative values make it less vibrant.
    for (int Y = 0; Y < Height; Y++)
    {
        unsigned char *LinePS = Src + Y * Stride;
        unsigned char *LinePD = Dest + Y * Stride;
        for (int X = 0; X < Width; X++)
        {
            int Blue, Green, Red, Max;
            Blue = LinePS[0];    Green = LinePS[1];    Red = LinePS[2];
            int Avg = (Blue + Green + Green + Red) >> 2;
            if (Blue > Green)
                Max = Blue;
            else
                Max = Green;
            if (Red > Max)
                Max = Red;
            int AmtVal = (Max - Avg) * Adjustment;                                //    Get adjusted average
            if (Blue != Max)    Blue += (((Max - Blue) * AmtVal) >> 14);
            if (Green != Max)    Green += (((Max - Green) * AmtVal) >> 14);
            if (Red != Max)        Red += (((Max - Red) * AmtVal) >> 14);
            LinePD[0] = IM_ClampToByte(Blue);
            LinePD[1] = IM_ClampToByte(Green);
            LinePD[2] = IM_ClampToByte(Red);
            LinePS += 3;
            LinePD += 3;
        }
    }
    return IM_STATUS_OK;
}
int IM_VibranceI(unsigned char *Src, unsigned char *Dest, int Width, int Height, int Stride, int Adjustment)
{
    int Channel = Stride / Width;
    if ((Src == NULL) || (Dest == NULL))                return IM_STATUS_NULLREFRENCE;
    if ((Width <= 0) || (Height <= 0))                    return IM_STATUS_INVALIDPARAMETER;
    if (Channel != 3)                                    return IM_STATUS_INVALIDPARAMETER;

    Adjustment = -IM_ClampI(Adjustment, -100, 100) * 1.28;        //       Reverse the vibrance input; this way, positive values make the image more vibrant.  Negative values make it less vibrant.
    for (int Y = 0; Y < Height; Y++)
    {
        unsigned char *LinePS = Src + Y * Stride;
        unsigned char *LinePD = Dest + Y * Stride;
        for (int X = 0; X < Width; X++)
        {
            int Blue, Green, Red, Max;
            Blue = LinePS[0];    Green = LinePS[1];    Red = LinePS[2];
            int Avg = (Blue + Green + Green + Red) >> 2;
            if (Blue > Green)
                Max = Blue;
            else
                Max = Green;
            if (Red > Max)
                Max = Red;
            int AmtVal = (Max - Avg) * Adjustment;                                //    Get adjusted average
            if (Blue != Max)    Blue += (((Max - Blue) * AmtVal) >> 14);
            if (Green != Max)    Green += (((Max - Green) * AmtVal) >> 14);
            if (Red != Max)        Red += (((Max - Red) * AmtVal) >> 14);
            LinePD[0] = IM_ClampToByte(Blue);
            LinePD[1] = IM_ClampToByte(Green);
            LinePD[2] = IM_ClampToByte(Red);
            LinePS += 3;
            LinePD += 3;
        }
    }
    return IM_STATUS_OK;
}

  这样优化后,同样大小的图像算法用时35毫秒,效果与浮点版本的主导没有啥区别。

  这样优化后,同样大小的图像算法用时35毫秒,效果与浮点版本的骨干无啥区别。

     
 最后咱们最主要来讲讲SSE版本的优化。

     
 最后咱们主要来讲讲SSE版本的优化。

  
对于这种单像素点、和世界无关之图像算法,为了能够使用SSE提高程序速度,一个中心的步调就是是将各颜色分量分离也单独的连天的变量,对于24各图像,我们了解图像在内存中的布局也:

  
对于这种单像素点、和天地无关之图像算法,为了能够用SSE提高程序速度,一个着力之步调就是是管每颜色分量分离也独立的连天的变量,对于24员图像,我们清楚图像在内存中的布局也:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
B1 G1 R1 B2 G2 R2 B3 G3 R3 B4 G4 R4 B5 G5 R5 B6 G6 R6 B7 G7 R7 B8 G8 R8 B9 G9 R9 B10 G10 R10 B11 G11 R11 B12 G12 R12 B13 G13 R13 B14 G14 R14 B15 G15 R15 B16 G16 R16
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
B1 G1 R1 B2 G2 R2 B3 G3 R3 B4 G4 R4 B5 G5 R5 B6 G6 R6 B7 G7 R7 B8 G8 R8 B9 G9 R9 B10 G10 R10 B11 G11 R11 B12 G12 R12 B13 G13 R13 B14 G14 R14 B15 G15 R15 B16 G16 R16

 

 

       

       

      我们要拿其成为:

      我们需要将她变成:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
B1 B2 B3 B4 B4 B5 B7 B8 B9 B10 B11 B12 B13 B14 B15 B16 G1 G2 G3 G4 G5 G6 G7 G8 G9 G10 G11 G12 G13 G14 G15 G16 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
B1 B2 B3 B4 B4 B5 B7 B8 B9 B10 B11 B12 B13 B14 B15 B16 G1 G2 G3 G4 G5 G6 G7 G8 G9 G10 G11 G12 G13 G14 G15 G16 R1 R2 R3 R4 R5 R6 R7 R8 R9 R10 R11 R12 R13 R14 R15 R16

 

 

     

     

   
 处理了晚我们而比方将她们过来至原的BGR布局。

   
 处理完毕后我们而比方将他们恢复到老之BGR布局。

   
 为了实现这个职能,我参考了采石工大侠的有关代码,分享如下:

   
 为了促成者效果,我参考了采石工大侠的关于代码,分享如下:

     我们先贴下代码:

     我们先行贴下代码:

    Src1 = _mm_loadu_si128((__m128i *)(LinePS + 0));
    Src2 = _mm_loadu_si128((__m128i *)(LinePS + 16));
    Src3 = _mm_loadu_si128((__m128i *)(LinePS + 32));

    Blue8 = _mm_shuffle_epi8(Src1, _mm_setr_epi8(0, 3, 6, 9, 12, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1));
    Blue8 = _mm_or_si128(Blue8, _mm_shuffle_epi8(Src2, _mm_setr_epi8(-1, -1, -1, -1, -1, -1, 2, 5, 8, 11, 14, -1, -1, -1, -1, -1)));
    Blue8 = _mm_or_si128(Blue8, _mm_shuffle_epi8(Src3, _mm_setr_epi8(-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 4, 7, 10, 13)));

    Green8 = _mm_shuffle_epi8(Src1, _mm_setr_epi8(1, 4, 7, 10, 13, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1));
    Green8 = _mm_or_si128(Green8, _mm_shuffle_epi8(Src2, _mm_setr_epi8(-1, -1, -1, -1, -1, 0, 3, 6, 9, 12, 15, -1, -1, -1, -1, -1)));
    Green8 = _mm_or_si128(Green8, _mm_shuffle_epi8(Src3, _mm_setr_epi8(-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 2, 5, 8, 11, 14)));

    Red8 = _mm_shuffle_epi8(Src1, _mm_setr_epi8(2, 5, 8, 11, 14, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1));
    Red8 = _mm_or_si128(Red8, _mm_shuffle_epi8(Src2, _mm_setr_epi8(-1, -1, -1, -1, -1, 1, 4, 7, 10, 13, -1, -1, -1, -1, -1, -1)));
    Red8 = _mm_or_si128(Red8, _mm_shuffle_epi8(Src3, _mm_setr_epi8(-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 3, 6, 9, 12, 15)));
    Src1 = _mm_loadu_si128((__m128i *)(LinePS + 0));
    Src2 = _mm_loadu_si128((__m128i *)(LinePS + 16));
    Src3 = _mm_loadu_si128((__m128i *)(LinePS + 32));

    Blue8 = _mm_shuffle_epi8(Src1, _mm_setr_epi8(0, 3, 6, 9, 12, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1));
    Blue8 = _mm_or_si128(Blue8, _mm_shuffle_epi8(Src2, _mm_setr_epi8(-1, -1, -1, -1, -1, -1, 2, 5, 8, 11, 14, -1, -1, -1, -1, -1)));
    Blue8 = _mm_or_si128(Blue8, _mm_shuffle_epi8(Src3, _mm_setr_epi8(-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 4, 7, 10, 13)));

    Green8 = _mm_shuffle_epi8(Src1, _mm_setr_epi8(1, 4, 7, 10, 13, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1));
    Green8 = _mm_or_si128(Green8, _mm_shuffle_epi8(Src2, _mm_setr_epi8(-1, -1, -1, -1, -1, 0, 3, 6, 9, 12, 15, -1, -1, -1, -1, -1)));
    Green8 = _mm_or_si128(Green8, _mm_shuffle_epi8(Src3, _mm_setr_epi8(-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 2, 5, 8, 11, 14)));

    Red8 = _mm_shuffle_epi8(Src1, _mm_setr_epi8(2, 5, 8, 11, 14, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1));
    Red8 = _mm_or_si128(Red8, _mm_shuffle_epi8(Src2, _mm_setr_epi8(-1, -1, -1, -1, -1, 1, 4, 7, 10, 13, -1, -1, -1, -1, -1, -1)));
    Red8 = _mm_or_si128(Red8, _mm_shuffle_epi8(Src3, _mm_setr_epi8(-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 3, 6, 9, 12, 15)));

     
首先,一次性加载48个图像数据到内存,正好停于三只__m128i变量中,同时另外一个不胜好之事务就是是48恰恰能吃3理除,也就是说我们圆的加载了16个24各项像素,这样尽管不见面并发断层,只象征下面48只像素可以与今底48独像素使用同一的艺术开展处理。

     
首先,一次性加载48只图像数据及内存,正好停在三独__m128i变量中,同时另外一个生好的业务就是48正要能为3规整除,也就是说我们完全的加载了16只24各类像素,这样即便非会见冒出断层,只代表下面48独像素可以跟本的48个像素使用同样的计进行拍卖。

     
如达到代码,则Src1受到保存在:

     
如达到代码,则Src1受保存在:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
B1 G1 R1 B2 G2 R2 B3 G3 R3 B4 G4 R4 B5 G5 R5 B6
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
B1 G1 R1 B2 G2 R2 B3 G3 R3 B4 G4 R4 B5 G5 R5 B6

 

 

 

 

      Src2遭保留在:

      Src2吃保留着:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
G6 R6 B7 G7 R7 B8 G8 R8 B9 G9 R9 B10 G10 R10 B11 G11
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
G6 R6 B7 G7 R7 B8 G8 R8 B9 G9 R9 B10 G10 R10 B11 G11

 

 

 

 

  Src3挨的数额虽然为:

  Src3负之多寡虽然为:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
R11 B12 G12 R12 B13 G13 R13 B14 G14 R14 B15 G15 R15 B16 G16 R16
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
R11 B12 G12 R12 B13 G13 R13 B14 G14 R14 B15 G15 R15 B16 G16 R16

 

 

 

 

    为了达到我们的目的,我们即将动用SSE中所向披靡的shuffle指令了,如果会把shuffle指令运用的巧夺天工,可以落很多很风趣的法力,有如鸠摩智的略无相功一样,可以催动拈花指发、袈裟服魔攻等等,成就世间能同自身鸠摩智打成平成的无几个人一样的丰功伟绩。哈哈,说远矣。

    为了上我们的目的,我们将要采取SSE中强有力的shuffle指令了,如果能管shuffle指令运用的巧夺天工,可以落很多非常有趣的成效,有如鸠摩智的有点无相功一样,可以催动拈花指发、袈裟服魔攻等等,成就世间能和自鸠摩智打成平成的无几单人同样的丰功伟绩。哈哈,说远了。

   
 简单的解shuffle指令,就是以__m128i变量内之逐条数据以指定的依次进行更摆,当然这布阵不肯定要是全采用原本的数码,也得重新某些数据,或者某些位置多按,比如以实践下这长长的指令

   
 简单的理解shuffle指令,就是将__m128i变量内的顺序数据论指定的相继进行双重摆,当然这个布阵不必然要是完全用原来的多少,也得以另行某些数据,或者某些位置多遵循,比如在尽下就漫长指令

    Blue8 = _mm_shuffle_epi8(Src1,
_mm_setr_epi8(0, 3, 6, 9, 12, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1));

    Blue8 = _mm_shuffle_epi8(Src1,
_mm_setr_epi8(0, 3, 6, 9, 12, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1));

   Blue8中的数也:

   Blue8中的数吧:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
B1 B2 B3 B4 B5 B6 0 0 0 0 0  0  0  0  0
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
B1 B2 B3 B4 B5 B6 0 0 0 0 0  0  0  0  0
_mm_setr_epi8指令的参数顺序可能更适合于我们常用的从左到右的理解习惯,其中的某个参数如果不在0和15之间时,则对应位置的数据就是被设置为0。
_mm_setr_epi8指令的参数顺序可能更适合于我们常用的从左到右的理解习惯,其中的某个参数如果不在0和15之间时,则对应位置的数据就是被设置为0。

 
 可以观看进展上述操作后Blue8的签6单字节已经入我们的求了。

 
 可以看到进展上述操作后Blue8的签6个字节已经入我们的需要了。

   于羁押代码的生一样句子:

   以扣押代码的下同样句:

        Blue8 = _mm_or_si128(Blue8, _mm_shuffle_epi8(Src2, _mm_setr_epi8(-1, -1, -1, -1, -1, -1, 2, 5, 8, 11, 14, -1, -1, -1, -1, -1)));
        Blue8 = _mm_or_si128(Blue8, _mm_shuffle_epi8(Src2, _mm_setr_epi8(-1, -1, -1, -1, -1, -1, 2, 5, 8, 11, 14, -1, -1, -1, -1, -1)));

  这词的晚半片与前面的切近,只是其中的常数不同,由_mm_shuffle_epi8(Src2,
_mm_setr_epi8(-1, -1, -1, -1, -1, -1, 2, 5, 8, 11, 14, -1, -1, -1,
-1, -1))得到的即数据也:

  这句之后半片与前面的接近,只是其中的常数不同,由_mm_shuffle_epi8(Src2,
_mm_setr_epi8(-1, -1, -1, -1, -1, -1, 2, 5, 8, 11, 14, -1, -1, -1,
-1, -1))得到的即数据也:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
0 0 0 0 0 0 B7 B8 B9 B10 B11 0 0 0 0 0
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
0 0 0 0 0 0 B7 B8 B9 B10 B11 0 0 0 0 0

 

 

 

 

   
 如果将这临时结果和事先的Blue8进行或者操作还是一直开展加操作,新的Blue8变量则为:

   
 如果管此临时结果和事先的Blue8进行或者操作还是直接进行加操作,新的Blue8变量则也:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
B1 B2 B3 B4 B5 B6 B7 B8 B9 B10 B11 0 0 0 0 0
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
B1 B2 B3 B4 B5 B6 B7 B8 B9 B10 B11 0 0 0 0 0

 

 

 

 

     
最后就同词和Blue8相关的代码为:

     
最后就同一句子和Blue8相关的代码为:

Blue8 = _mm_or_si128(Blue8, _mm_shuffle_epi8(Src3, _mm_setr_epi8(-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 4, 7, 10, 13)));
Blue8 = _mm_or_si128(Blue8, _mm_shuffle_epi8(Src3, _mm_setr_epi8(-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 1, 4, 7, 10, 13)));

  后面的shuffle临时之拿走的变量为:

  后面的shuffle临时之得到的变量为:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
0 0 0 0 0 0 0 0 0 0 0 B12 B13 B14 B15 B16
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
0 0 0 0 0 0 0 0 0 0 0 B12 B13 B14 B15 B16

 

 

 

 

   
 再次和前的Blue8结果开展或者操作得到最终的结果:

   
 再次与前面的Blue8结果进行或者操作得到终极之结果:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
B1 B2 B3 B4 B5 B6 B7 B8 B9 B10 B11 B12 B13 B14 B15 B16
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
B1 B2 B3 B4 B5 B6 B7 B8 B9 B10 B11 B12 B13 B14 B15 B16

 

 

 

 

   
 对于Green和Red分量,处理的主意及步子是一模一样的,只是出于位置不同,每次进行shuffle操作的常数有所不同,但原理完全同。

   
 对于Green和Red分量,处理的道和步子是同样的,只是出于位置不同,每次进行shuffle操作的常数有所不同,但原理完全一样。

  如果知道了是因为BGRBGRBGR
—》变为了BBBGGGRRR这样的模式的规律后,那么由BBBGGGRRR–>变为BGRBGRBGR的理就可怜浅显了,这里不赘述,直接贴起代码:

  如果掌握了是因为BGRBGRBGR
—》变为了BBBGGGRRR这样的模式之法则后,那么由BBBGGGRRR–>变为BGRBGRBGR的道理就生浅显了,这里不赘述,直接贴出代码:

    Dest1 = _mm_shuffle_epi8(Blue8, _mm_setr_epi8(0, -1, -1, 1, -1, -1, 2, -1, -1, 3, -1, -1, 4, -1, -1, 5));
    Dest1 = _mm_or_si128(Dest1, _mm_shuffle_epi8(Green8, _mm_setr_epi8(-1, 0, -1, -1, 1, -1, -1, 2, -1, -1, 3, -1, -1, 4, -1, -1)));
    Dest1 = _mm_or_si128(Dest1, _mm_shuffle_epi8(Red8, _mm_setr_epi8(-1, -1, 0, -1, -1, 1, -1, -1, 2, -1, -1, 3, -1, -1, 4, -1)));

    Dest2 = _mm_shuffle_epi8(Blue8, _mm_setr_epi8(-1, -1, 6, -1, -1, 7, -1, -1, 8, -1, -1, 9, -1, -1, 10, -1));
    Dest2 = _mm_or_si128(Dest2, _mm_shuffle_epi8(Green8, _mm_setr_epi8(5, -1, -1, 6, -1, -1, 7, -1, -1, 8, -1, -1, 9, -1, -1, 10)));
    Dest2 = _mm_or_si128(Dest2, _mm_shuffle_epi8(Red8, _mm_setr_epi8(-1, 5, -1, -1, 6, -1, -1, 7, -1, -1, 8, -1, -1, 9, -1, -1)));

    Dest3 = _mm_shuffle_epi8(Blue8, _mm_setr_epi8(-1, 11, -1, -1, 12, -1, -1, 13, -1, -1, 14, -1, -1, 15, -1, -1));
    Dest3 = _mm_or_si128(Dest3, _mm_shuffle_epi8(Green8, _mm_setr_epi8(-1, -1, 11, -1, -1, 12, -1, -1, 13, -1, -1, 14, -1, -1, 15, -1)));
    Dest3 = _mm_or_si128(Dest3, _mm_shuffle_epi8(Red8, _mm_setr_epi8(10, -1, -1, 11, -1, -1, 12, -1, -1, 13, -1, -1, 14, -1, -1, 15)));
    Dest1 = _mm_shuffle_epi8(Blue8, _mm_setr_epi8(0, -1, -1, 1, -1, -1, 2, -1, -1, 3, -1, -1, 4, -1, -1, 5));
    Dest1 = _mm_or_si128(Dest1, _mm_shuffle_epi8(Green8, _mm_setr_epi8(-1, 0, -1, -1, 1, -1, -1, 2, -1, -1, 3, -1, -1, 4, -1, -1)));
    Dest1 = _mm_or_si128(Dest1, _mm_shuffle_epi8(Red8, _mm_setr_epi8(-1, -1, 0, -1, -1, 1, -1, -1, 2, -1, -1, 3, -1, -1, 4, -1)));

    Dest2 = _mm_shuffle_epi8(Blue8, _mm_setr_epi8(-1, -1, 6, -1, -1, 7, -1, -1, 8, -1, -1, 9, -1, -1, 10, -1));
    Dest2 = _mm_or_si128(Dest2, _mm_shuffle_epi8(Green8, _mm_setr_epi8(5, -1, -1, 6, -1, -1, 7, -1, -1, 8, -1, -1, 9, -1, -1, 10)));
    Dest2 = _mm_or_si128(Dest2, _mm_shuffle_epi8(Red8, _mm_setr_epi8(-1, 5, -1, -1, 6, -1, -1, 7, -1, -1, 8, -1, -1, 9, -1, -1)));

    Dest3 = _mm_shuffle_epi8(Blue8, _mm_setr_epi8(-1, 11, -1, -1, 12, -1, -1, 13, -1, -1, 14, -1, -1, 15, -1, -1));
    Dest3 = _mm_or_si128(Dest3, _mm_shuffle_epi8(Green8, _mm_setr_epi8(-1, -1, 11, -1, -1, 12, -1, -1, 13, -1, -1, 14, -1, -1, 15, -1)));
    Dest3 = _mm_or_si128(Dest3, _mm_shuffle_epi8(Red8, _mm_setr_epi8(10, -1, -1, 11, -1, -1, 12, -1, -1, 13, -1, -1, 14, -1, -1, 15)));

  核心要这些常数的挑三拣四。

  核心还是这些常数的选料。

     
以上是拍卖的首先步,看上去是代码很多,实际上他们的施行时那个急匆匆之,3000*2000的希冀这拆分和归并过程为就是约2ms。

     
以上是处理的率先步,看上去是代码很多,实际上他们之推行时杀快的,3000*2000之觊觎是拆分和归并过程吧尽管大概2ms。

     
当然由于字节数据类的表达范围大有限,除了少有的几乎单鲜的操作会对字节类型直接处理外,比如本例的丘RGB的Max值,就得一直用脚的SIMD指令实现:

     
当然由于字节数据类的达范围大少,除了少有的几乎单鲜的操作会对字节类型直接处理外,比如本例的丘RGB的Max值,就可以一直用脚的SIMD指令实现:

Max8 = _mm_max_epu8(_mm_max_epu8(Blue8, Green8), Red8);
Max8 = _mm_max_epu8(_mm_max_epu8(Blue8, Green8), Red8);

     
很其他多计都是无法直接以如此的限量外进行了,因此即便发生必不可少将数据类型扩展,比如扩展及short类型或者int/float类型。

     
很其他多计都是力不从心直接以如此的限制外进行了,因此尽管有必不可少将数据类型扩展,比如扩展至short类型或者int/float类型。

     
在SSE里开展这样的操作也是非常简单的,SSE提供了大量的数据类型转换的函数和指令,比如有byte扩展至short,则可就此_mm_unpacklo_epi8和_mm_unpackhi_epi8配合zero来实现:

     
在SSE里展开如此的操作为是非常简单的,SSE提供了大气之数据类型转换的函数和下令,比如有byte扩展及short,则足以据此_mm_unpacklo_epi8和_mm_unpackhi_epi8配合zero来实现:

BL16 = _mm_unpacklo_epi8(Blue8, Zero);
BH16 = _mm_unpackhi_epi8(Blue8, Zero);
BL16 = _mm_unpacklo_epi8(Blue8, Zero);
BH16 = _mm_unpackhi_epi8(Blue8, Zero);

  其中

  其中

Zero = _mm_setzero_si128();
Zero = _mm_setzero_si128();

   很风趣的操作,比如_mm_unpacklo_epi8是将少单__m128i底低8各交错布置形成一个初的128个数据,如果内部一个参数为0,则就是将另外一个参数的低8独字节无损的恢弘为16各类了,以上述BL16也条例,其中间布局也:

   很风趣的操作,比如_mm_unpacklo_epi8是将少单__m128i的低8各项交错布置形成一个初的128员数据,如果内部一个参数为0,则就是是将另外一个参数的低8个字节无损的壮大为16个了,以上述BL16乎条例,其中间布局也:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
B1 0 B2 0 B3 0 B3 0 B4 0 B5 0 B6 0 B7 0
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
B1 0 B2 0 B3 0 B3 0 B4 0 B5 0 B6 0 B7 0

 

 

 

 

  如果我们用开展以int范围外展开计算,则还得更加壮大,此时好下_mm_unpackhi_epi16/_mm_unpacklo_epi16匹配zero继续进行扩张,这样一个Blue8变量需要4独__m128i
int范围之数码来表述。

  如果我们要展开以int范围外进行计算,则还索要进一步扩展,此时得以用_mm_unpackhi_epi16/_mm_unpacklo_epi16郎才女貌zero继续开展扩张,这样一个Blue8变量需要4独__m128i
int范围之数码来发表。

     
好,说道这里,我们后续羁押我们C语言里的即时句:

     
好,说道这里,我们继承看咱们C语言里之当下句:

  int Avg = (Blue + Green + Green + Red) >> 2;
  int Avg = (Blue + Green + Green + Red) >> 2;

  可以看到,这里的乘除是无能为力再byte范围外做到的,中间的Blue

  可以见见,这里的计算是无法再byte范围外得的,中间的Blue

  • Green + Green +
    Red在大多数状下都见面压倒255万一绝对小于255*4,,因此我们要扩大数据及16个,按上述措施,对Blue8\Green8\Red8\Max8展开扩展,如下所示:

      BL16 = _mm_unpacklo_epi8(Blue8, Zero);
      BH16 = _mm_unpackhi_epi8(Blue8, Zero);
      GL16 = _mm_unpacklo_epi8(Green8, Zero);
      GH16 = _mm_unpackhi_epi8(Green8, Zero);
      RL16 = _mm_unpacklo_epi8(Red8, Zero);
      RH16 = _mm_unpackhi_epi8(Red8, Zero);
      MaxL16 = _mm_unpacklo_epi8(Max8, Zero);
      MaxH16 = _mm_unpackhi_epi8(Max8, Zero);
    
  • Green + Green +
    Red在大多数气象下还见面压倒255只要决小于255*4,,因此我们要扩大数据及16个,按上述措施,对Blue8\Green8\Red8\Max8进展扩展,如下所示:

      BL16 = _mm_unpacklo_epi8(Blue8, Zero);
      BH16 = _mm_unpackhi_epi8(Blue8, Zero);
      GL16 = _mm_unpacklo_epi8(Green8, Zero);
      GH16 = _mm_unpackhi_epi8(Green8, Zero);
      RL16 = _mm_unpacklo_epi8(Red8, Zero);
      RH16 = _mm_unpackhi_epi8(Red8, Zero);
      MaxL16 = _mm_unpacklo_epi8(Max8, Zero);
      MaxH16 = _mm_unpackhi_epi8(Max8, Zero);
    

  这计算Avg就回到渠道成了:

  这计算Avg就和至渠道成了:

     AvgL16 = _mm_srli_epi16(_mm_add_epi16(_mm_add_epi16(BL16, RL16), _mm_slli_epi16(GL16, 1)), 2);
     AvgH16 = _mm_srli_epi16(_mm_add_epi16(_mm_add_epi16(BH16, RH16), _mm_slli_epi16(GH16, 1)), 2);
     AvgL16 = _mm_srli_epi16(_mm_add_epi16(_mm_add_epi16(BL16, RL16), _mm_slli_epi16(GL16, 1)), 2);
     AvgH16 = _mm_srli_epi16(_mm_add_epi16(_mm_add_epi16(BH16, RH16), _mm_slli_epi16(GH16, 1)), 2);

  中间两只Green相加是用移动或直接相加对进度没有啥影响之。

  中间两单Green相加是故运动或一直相加对快没有啥影响的。

     
 接下来的优化则是本例的一个风味有了。我们来详细分析。

     
 接下来的优化则是本例的一个特性有了。我们来详细分析。

     
 我们了解,SSE对于跳转是特别不谐和之,他不行擅长序列化处理一个工作,虽然他供了广大于指令,但是不少场面下复杂的跳转SSE还是无为力,对于本例,情况比较新鲜,如果一旦下SSE的比较指令也是得一直促成之,实现的点子时,使用于指令得到一个Mask,Mask中入于结实的值会为FFFFFFFF,不称的为0,然后把这个Mask和后要计算的某部值进行And操作,由于与FFFFFFFF进行And操作不见面转操作数本身,和0进行And操作则变为0,在许多情景下,就是随便你符合条件与否,都进行末端的盘算,只是不符合条件的算计不见面影响结果,这种计算可能会见劳而无功SSE优化的有些提速效果,这个即将具体情况具体分析了。

     
 我们知晓,SSE对于跳转是老大不协调的,他蛮擅长序列化处理一个政工,虽然他提供了多比较指令,但是过多情形下复杂的跳转SSE还是无为力,对于本例,情况于异常,如果一旦动用SSE的比较指令也是得一直促成之,实现之点子时,使用比较指令得到一个Mask,Mask中称于结实的值会为FFFFFFFF,不适合的为0,然后把这个Mask和后面要计算的某部值进行And操作,由于与FFFFFFFF进行And操作不见面转操作数本身,和0进行And操作则变为0,在无数情形下,就是任你符合条件与否,都开展末端的测算,只是不符合条件的计算不见面影响结果,这种计算可能会见劳而无功SSE优化的一些提速效果,这个即将具体情况具体分析了。

     
注意观察本例的代码,他的本意是要尽深价值与某分量的价值不平等,则进行末端的调操作,否则不进行调试。可后面的调动操作着发生太要命价值减去该分量的操作,也即意味着一旦尽老价值和欠分量相同,两者相减则也0,调整量此时呢为0,并无影响结果,也就算一定给尚未调节,因此,把这极判断失丢,并无会见潜移默化结果。同时考虑到实在情况,最老价值当众情景吗特会跟某个一个重相同,也就是说只来1/3的几率不履跳转后的语句,在本例中,跳反后底代码执行复杂度并无赛,去丢这些极判断用增加并代码所吃的特性及削减3独判断的工夫就以一个品位及了,因此,完全好去除这些判断语句,这样就非常适合于SSE实现了。

     
注意观察本例的代码,他的本心是如尽深价值和某个分量的值未雷同,则展开末端的调动操作,否则不开展调节。可后面的调操作中出极端要命价值减去该分量的操作,也即代表要尽老价值和拖欠分量相同,两者相减则也0,调整量此时吗为0,并无影响结果,也就算相当给无调节,因此,把这个极判断失丢,并无见面影响结果。同时考虑到骨子里状况,最老价值当过剩状态吗无非见面和某某一个分量相同,也就是说只生1/3底票房价值不执行跳转后的言语,在本例中,跳反后的代码执行复杂度并无愈,去丢这些原则判断用增加并代码所耗费的性质和压缩3只判断的年华曾当一个档次及了,因此,完全可以去这些判断语句,这样即使非常适合于SSE实现了。

  接着分析,由于代码中生出((Max –
Blue) * AmtVal) >> 14,其中AmtVal
= (Max – Avg) * Adjustment,展开就为:  ((Max – Blue) * (Max – Avg) *
Adjustment)>>14;这三只数据相乘很十分程度上会见压倒short所能发挥的克,因此,我们还得针对上面的16各数据开展扩张,扩展至32个,这样便基本上了累累指令,那么有没有发出非需要扩大的措施吧。经过一番想,我提出了下述解决方案:

  接着分析,由于代码中发出((Max –
Blue) * AmtVal) >> 14,其中AmtVal
= (Max – Avg) * Adjustment,展开就为:  ((Max – Blue) * (Max – Avg) *
Adjustment)>>14;这三独数据相乘很老程度及会压倒short所能发挥的限量,因此,我们还待对地方的16员数据开展扩展,扩展至32位,这样虽差不多矣累累令,那么来没出无需要扩大的不二法门呢。经过一番思考,我提出了下述解决方案:

   
在超越快速指数模糊算法的实现与优化(10000*10000当100ms左右贯彻 一温柔被,我以篇章最后提到了顶点的一个下令:_mm_mulhi_epi16(a,b),他会一次性处理8个16员数据,其计算结果相当给对(a*b)>>16,但此间十分明a和b必须是short类型所能够表达的限制。

   
在超高速指数模糊算法的贯彻和优化(10000*10000于100ms左右落实 一轻柔被,我于篇章最后提到了极的一个限令:_mm_mulhi_epi16(a,b),他能够一次性处理8只16员数据,其计算结果相当给对于(a*b)>>16,但这里非常明a和b必须是short类型所能发表的限制。

       注意我们的这表达式:

       注意我们的斯表达式:

              ((Max – Blue) * (Max –
Avg) * Adjustment)>>14

              ((Max – Blue) * (Max –
Avg) * Adjustment)>>14

   
   首先,我们拿他恢弘为走16各类之结果,变为如下:

   
   首先,我们用他恢弘为活动16个的结果,变为如下:

         ((Max – Blue) * 4 * (Max –
Avg) * Adjustment)>>16

         ((Max – Blue) * 4 * (Max –
Avg) * Adjustment)>>16

     
Adjustment我们曾经以他限定于了[-128,128]里头,而(Max –
Avg)理论及的极其要命价值也255 –
85=170,(即RGB分量有一个凡是255,其他的还为0),最小值为0,因此,两者在各自范围外之成绩不见面盖short所能发表的范围,而(Max-Blue)的极致深价值也255,最小值为0,在乘胜以4吗以short类型所能够表达的范围外。所以,下一致步你们知道了为?

     
Adjustment我们都拿他限定于了[-128,128]以内,而(Max –
Avg)理论及之太深价值吗255 –
85=170,(即RGB分量有一个凡是255,其他的都也0),最小值为0,因此,两者在独家范围外之实绩未会见超过short所能发表的限,而(Max-Blue)的尽酷价值也255,最小值为0,在乘胜以4吧在short类型所能够发表的克外。所以,下同样步你们了解了呢?

     
 经过上述分析,下面这四执C代码可由于下述SSE函数实现:

     
 经过上述分析,下面这四执行C代码可由于下述SSE函数实现:

    int AmtVal = (Max - Avg) * Adjustment;                                //    Get adjusted average
    if (Blue != Max)    Blue += (((Max - Blue) * AmtVal) >> 14);
    if (Green != Max)    Green += (((Max - Green) * AmtVal) >> 14);
    if (Red != Max)        Red += (((Max - Red) * AmtVal) >> 14);
    int AmtVal = (Max - Avg) * Adjustment;                                //    Get adjusted average
    if (Blue != Max)    Blue += (((Max - Blue) * AmtVal) >> 14);
    if (Green != Max)    Green += (((Max - Green) * AmtVal) >> 14);
    if (Red != Max)        Red += (((Max - Red) * AmtVal) >> 14);

  对应的SSE代码为:

  对应的SSE代码为:

    AmtVal = _mm_mullo_epi16(_mm_sub_epi16(MaxL16, AvgL16), Adjustment128);
    BL16 = _mm_adds_epi16(BL16, _mm_mulhi_epi16(_mm_slli_epi16(_mm_sub_epi16(MaxL16, BL16), 2), AmtVal));
    GL16 = _mm_adds_epi16(GL16, _mm_mulhi_epi16(_mm_slli_epi16(_mm_sub_epi16(MaxL16, GL16), 2), AmtVal));
    RL16 = _mm_adds_epi16(RL16, _mm_mulhi_epi16(_mm_slli_epi16(_mm_sub_epi16(MaxL16, RL16), 2), AmtVal));

    AmtVal = _mm_mullo_epi16(_mm_sub_epi16(MaxH16, AvgH16), Adjustment128);
    BH16 = _mm_adds_epi16(BH16, _mm_mulhi_epi16(_mm_slli_epi16(_mm_sub_epi16(MaxH16, BH16), 2), AmtVal));
    GH16 = _mm_adds_epi16(GH16, _mm_mulhi_epi16(_mm_slli_epi16(_mm_sub_epi16(MaxH16, GH16), 2), AmtVal));
    RH16 = _mm_adds_epi16(RH16, _mm_mulhi_epi16(_mm_slli_epi16(_mm_sub_epi16(MaxH16, RH16), 2), AmtVal));
    AmtVal = _mm_mullo_epi16(_mm_sub_epi16(MaxL16, AvgL16), Adjustment128);
    BL16 = _mm_adds_epi16(BL16, _mm_mulhi_epi16(_mm_slli_epi16(_mm_sub_epi16(MaxL16, BL16), 2), AmtVal));
    GL16 = _mm_adds_epi16(GL16, _mm_mulhi_epi16(_mm_slli_epi16(_mm_sub_epi16(MaxL16, GL16), 2), AmtVal));
    RL16 = _mm_adds_epi16(RL16, _mm_mulhi_epi16(_mm_slli_epi16(_mm_sub_epi16(MaxL16, RL16), 2), AmtVal));

    AmtVal = _mm_mullo_epi16(_mm_sub_epi16(MaxH16, AvgH16), Adjustment128);
    BH16 = _mm_adds_epi16(BH16, _mm_mulhi_epi16(_mm_slli_epi16(_mm_sub_epi16(MaxH16, BH16), 2), AmtVal));
    GH16 = _mm_adds_epi16(GH16, _mm_mulhi_epi16(_mm_slli_epi16(_mm_sub_epi16(MaxH16, GH16), 2), AmtVal));
    RH16 = _mm_adds_epi16(RH16, _mm_mulhi_epi16(_mm_slli_epi16(_mm_sub_epi16(MaxH16, RH16), 2), AmtVal));

  最后一步就是是拿这些16各类之数量再度转移为8各的,注意原始代码中生出Clamp操作,这个操作实际是只耗时的历程,而SSE天然的有抗饱和的函数。

  最后一步就是是拿这些16各之数量再次转移为8员的,注意原始代码中产生Clamp操作,这个操作实际是只耗时的长河,而SSE天然的保有抗饱和的函数。

  Blue8 = _mm_packus_epi16(BL16, BH16);
  Green8 = _mm_packus_epi16(GL16, GH16);
  Red8 = _mm_packus_epi16(RL16, RH16);

 _mm_packus_epi16这个的用法和含义自己去MSDN搜索一下吧,实在是懒得解释了。
  Blue8 = _mm_packus_epi16(BL16, BH16);
  Green8 = _mm_packus_epi16(GL16, GH16);
  Red8 = _mm_packus_epi16(RL16, RH16);

 _mm_packus_epi16这个的用法和含义自己去MSDN搜索一下吧,实在是懒得解释了。

   最终优化速度:5ms。

   最终优化速度:5ms。

   来只速度比较:

   来个速度比:

版本 VB6.0 C++,float优化版本 C++定点版 C++/SSE版
速度 400ms 70ms 35ms 5ms
版本 VB6.0 C++,float优化版本 C++定点版 C++/SSE版
速度 400ms 70ms 35ms 5ms

 

 

     

     

  上面的VB6.0的耗时凡原作者的代码编译后底尽进度,如果自己好去用VB6.0去优化他的口舌,有信念能够好70ms以内的。

  上面的VB6.0的耗时凡原作者的代码编译后底实践进度,如果自己要好去用VB6.0去优化他的语句,有信念会不负众望70ms以内的。

  但无论如何,SSE优化的速提升是英雄的。

  但好歹,SSE优化的快慢提升是惊天动地的。

结论:

结论:

       简单的剖析了当然饱和度算法的落实,分享了彼SSE实现的历程,对于那些刚刚接触SSE,想做图像处理的情人起得的扶植。

       简单的辨析了自然饱和度算法的贯彻,分享了该SSE实现的过程,对于那些刚接触SSE,想做图像处理的意中人发生得之援。

     
  源代码下载地址:http://files.cnblogs.com/files/Imageshop/Vibrance.rar

     
  源代码下载地址:http://files.cnblogs.com/files/Imageshop/Vibrance.rar

       
写的真好累,休息去了,觉得对而行之请给本人买杯啤酒或咖啡吧。

       
写的的确好累,休息去了,觉得对你中之求给我买杯啤酒或咖啡吧。

图片 7

图片 8

 

 

相关文章