澳门网上娱乐平台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

 

 

相关文章