SSE图像算法优化连串八:自然饱和度(Vibrance)算法的效仿完结及其SSE优化(附源码,可看作SSE图像入门,Vibrance算法也可用于简单的肤色调整)。

  对应的SSE代码为:

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

     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);

  大家来设想有些近似和一定优化。

 

  要是大家须求举行在int范围内开展总结,则还需特别扩大,此时得以选择_mm_unpackhi_epi16/_mm_unpacklo_epi16格外zero继续展开扩充,那样一个Blue8变量须求四个__美学原理,m128i
int范围的多寡来发挥。

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

  中间多少个格林相加是用移动照旧间接相加对进度没啥影响的。

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

 

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

  此时总计Avg就马到功成了:

  接着分析,由于代码中有((马克斯 –
Blue) * AmtVal) >> 14,其中AmtVal
= (Max – Avg) * Adjustment,展开即为:  ((马克斯 – Blue) * (Max – Avg) *
Adjustment)>>14;那三个数据相乘一点都不小程度上会超出short所能表达的限量,由此,大家还需求对上面包车型地铁13人数据开展扩充,扩大到三拾三个人,那样就多了广大限令,那么有没有不必要扩充的章程啊。经过一番合计,作者提议了下述消除方案:

      float AmtVal = (Max - Avg) * VibranceAdjustment;

 个中的描述和PS官方文书档案的叙述有类似之处。

 

        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)));
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

  相当粗略的算法,先求出每种像素TiguanGB分量的最大值和平均值,然后求两者之差,之后遵照输入调节量求出调整量。

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

   
 不难的知道shuffle指令,便是将__m128i变量内的相继数据依据钦定的逐一进行再一次陈设,当然那一个布阵不必然要统统使用原来的多少,也得以重复有个别数据,或然有些地点无数据,比如在举办上边这条指令

 

    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)));
_mm_setr_epi8指令的参数顺序可能更适合于我们常用的从左到右的理解习惯,其中的某个参数如果不在0和15之间时,则对应位置的数据就是被设置为0。

     
好,说道那里,我们继续看大家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;
        }
    }

      大家必要把它们变成:

   
 为了落成这么些功效,笔者参考了采石工豪杰的关于代码,分享如下:

     
 美学原理 1   
 美学原理 2   
 美学原理 3

Zero = _mm_setzero_si128();
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)));

       注意大家的那个表明式:

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
0 0 0 0 0 0 0 0 0 0 0 B12 B13 B14 B15 B16

 

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;
}

       
写的真正好累,休息去了,觉得对您有效的请给作者买杯干白恐怕咖啡呢。

     
 闲话不多说了,其实自然饱和度也是近来多少个版本的PS才现身的法力,在调节和测试有个别图片的时候会有科学的机能,也足以当做简单的肤色调整的一个算法,比如上边那位小姐,用自然饱和度即可以让她失血过多,也得以让他肤色红晕。

  上边的VB6.0的耗费时间是原来的小说者的代码编写翻译后的施行进程,如若本身要好去用VB6.0去优化他的话,有信念能不负众望70ms以内的。

 
 能够看出进展上述操作后Blue8的签五个字节已经符合大家的供给了。

     
最终这一句和Blue8相关的代码为:

  最终一步正是将这个十九位的数额重复员和转业移为八个人的,注意原始代码中有Clamp操作,那个操作实际是个耗费时间的进程,而SSE天然的持有抗饱和的函数。

  再理会abs里的参数, 马克斯 –
Avg,那有须要取相对值吗,最大值难道会比平均值小,浪费时间,最终改为:

   Blue第88中学的数量为:

      Src第22中学保存着:

  这句的后半片段和前边的切近,只是在这之中的常数不相同,由_mm_shuffle_epi8(Src2,
_mm_setr_epi8(-1, -1, -1, -1, -1, -1, 2, 5, 8, 11, 14, -1, -1, -1,
-1, -1))获得的临时数据为:

     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

 

  Vibrance这一个单词搜索翻译一般震荡,抖动也许是响当当、活力,不过官方的词汇里还一向未出现过自然饱和度那些词,也不懂伏贴时的Adobe中文翻译人士怎么会如此处理。可是我们看看PS对那个意义的解释:

     
 大家精晓,SSE对于跳转是很不协调的,他尤其擅长连串化处理一个政工,就算她提供了无数相比指令,但是众多情状下复杂的跳转SSE如故无论为力,对于本例,情况比较新鲜,借使要接纳SSE的可比指令也是足以一向促成的,实现的章程时,使用相比较指令得到1个Mask,Mask中符合比较结实的值会为FFFFFFFF,不符合的为0,然后把那么些Mask和前面需求总结的某部值实行And操作,由于和FFFFFFFF进行And操作不会转移操作数本身,和0实行And操作则变为0,在许多情状下,正是不管你符合条件与否,都进展末端的盘算,只是不符合条件的盘算不会潜移默化结果,那种总括大概会失效SSE优化的片段提速效果,那个将要具体情况具体分析了。

 

  
对于那种单像素点、和世界毫无干系的图像算法,为了能运用SSE进步程序速度,两其中坚的步子就是把各颜色分量分离为单身的总是的变量,对于二十四位图像,大家清楚图像在内部存储器中的布局为:

     

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
0 0 0 0 0 0 B7 B8 B9 B10 B11 0 0 0 0 0

  Src3中的数据则为:

     
 确实是和饱和度有关的,那样敞亮粤语的翻译反而倒是合理,那么只好怪Adobe的开发者为何给那些职能起个名字叫Vibrance了。

   
 再度和事先的Blue8结果举办或操作获得终极的结果:

结论:

     
当然是因为字节数据类型的发挥范围10分有限,除了少有的多少个简单的操作能针对字节类型直接处理外,比如本例的丘福特ExplorerGB的马克斯值,就能够直接用上边的SIMD指令完结:

 

  int Avg = (Blue + Green + Green + Red) >> 2;
    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);

     
如上代码,则Src第11中学保存着:

    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)));

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

 

     大家先贴下代码:

     
 经过上述分析,上面那四行C代码可由下述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));

 

     
Adjustment大家已经将她限定在了[-128,128]里面,而(马克斯 –
Avg)理论上的最大值为255 –
85=170,(即RubiconGB分量有二个是255,别的的都为0),最小值为0,由此,两者在个别范围内的大成不会胜出short所能表明的限定,而(马克斯-Blue)的最大值为255,最小值为0,在乘以4也在short类型所能表明的界定内。所以,下一步你们懂了吗?

  那样优化后,同样大小的图像算法用时35飞秒,效果和浮点版本的主导没啥分化。

     
以上是处理的第③步,看上去那个代码很多,实际上他们的实施时特别快的,两千*三千的图那么些拆分和集合进程也就大致2ms。

     
很别的多划算都以无能为力直接在如此的范围内展开了,因而就有必不可少校数据类型扩大,比如扩充到short类型恐怕int/float类型。

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

     

     
首先,2遍性加载五十多少个图像数据到内部存储器,正好放置在四个__m128i变量中,同时其余二个很好的事务正是48刚好能被3整除,也正是说我们完全的加载了17个贰13人像素,那样就不会见世断层,只表示上面五十多个像素能够和今后的肆18个像素使用同样的主意开始展览处理。

   
 对于格林和Red分量,处理的艺术和步子是如出一辙的,只是由于地点分歧,每一趟举办shuffle操作的常数有所不相同,但原理完全一致。

   
   首先,大家将他恢弘为移动12位的结果,变为如下:

   我们在贴出他的基本代码:

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

     
在SSE里举办如此的操作也是万分不难的,SSE提供了大气的数据类型转换的函数和指令,比如有byte扩充到short,则能够用_mm_unpacklo_epi8和_mm_unpackhi_epi8配合zero来实现:

      float VibranceAdjustment = -0.01 * Adjustment / 127.0f;

   最后优化速度:5ms。

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

     
 第壹我们把/127改为/128,那基本不影响效应,同时Adjustment暗中同意的界定为[-100,100],把它也线性扩张学一年级点,比如扩展1.28倍,增添到[-128,128],那样在结尾咱们贰回性移位,减弱中间的损失,大致的代码如下:

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

  其中

  但无论怎么样,SSE优化的快慢升高是伟人的。

     
大家注重讲下这么些算法的优化及其SSE实现,越发是SSE版本代码是本文的首要。

 

   来个速度相比:

  假设明白了由BGRBGRBGRAV4—》变为了BBBGGG奥德赛QX56汉兰达那样的格局的法则后,那么由BBBGGGTucsonPAJERO陆风X8–>变为BGRBGRBGENVISION的道理就极度浅显了,那里不赘述,直接贴出代码:

   
 处理完后大家又要把她们过来到原有的BG景逸SUV布局。

     
注意观看本例的代码,他的原意是要是最大值和有个别分量的值分化等,则进行末端的调动操作,不然不实行调节和测试。可后边的调整操作中有最大值减去该分量的操作,也就表示若是最大值和该分量相同,两者相减则为0,调整量此时也为0,并不影响结果,也就一定于尚未调节,因而,把那些原则判断去掉,并不会影响结果。同时考虑到骨子里情形,最大值在无数状态也只会和某三个分量相同,相当于说唯有三分一的概率不实施跳转后的话语,在本例中,跳转后的代码执行复杂度并不高,去掉这几个原则判断从而扩充一道代码所费用的性质和压缩二个判断的光阴已经在2个水准上了,由此,完全可以去除这几个判断语句,那样就分外适合于SSE落成了。

     
 最终我们第叁来讲讲SSE版本的优化。

  • 格林 + 格林 +
    Red在超越百分之五十情形下都会高于255而相对小于255*4,,因而大家要求扩展数据到十三个人,按上述措施,对Blue8\Green8\Red8\马克斯8展开扩展,如下所示:

      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);
    

 

 

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

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

     
 VB的语法有个别人或者不熟悉,笔者有点做点更改翻译成C的代码如下:

  能够看看,那里的持筹握算是力不从心再byte范围内到位的,中间的Blue

   很有趣的操作,比如_mm_unpacklo_epi8是将四个__m128i的低6位交错计划形成2个新的127人数据,借使内部3个参数为0,则就是把此外一个参数的低几个字节无损的扩大为十两个人了,以上述BL16为例,在那之中间布局为:

     
 那么那几个算法的内在是什么落实的呢,小编并未仔细的去研商他,可是在开源软件Photo德姆on-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/
'
'***************************************************************************

  /127.0f能够优化为乘法,同时注意VibranceAdjustment在里面不变,能够把他们结合到循环的最外层,即改为:

    那是浮点版本的差不多优化,若是不勾选编译器的SSE优化,直接运用FPU,对于一副2000*3000的2四人图像耗费时间在I5的一台机械上运转用时大约70微秒,但那不是首要。

      第2步优化,去除掉不须要计算和除法,很分明,这一句是本段代码中耗费时间较为明显的部分

 

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
版本 VB6.0 C++,float优化版本 C++定点版 C++/SSE版
速度 400ms 70ms 35ms 5ms

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

 

   
 借使把这些一时半刻结果和此前的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

  那个的结果和PS的是相比相近的,最起码趋势是非常相近的,但是细节依然不平等,可是能够看清的是方向是对的,假如您势必要复制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.

   在看代码的下一句:

美学原理 4

   
超高速指数模糊算法的兑现和优化(一千0*10000在100ms左右落实 一文中,笔者在篇章最终提到了极限的三个下令:_mm_mulhi_epi16(a,b),他能贰遍性处理八个1三人数据,其计算结果一定于对于(a*b)>>16,但此间很明a和b必须是short类型所能表明的界定。

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

 

       简单的剖析了本来饱和度算法的落到实处,分享了其SSE完毕的历程,对于那么些刚刚接触SSE,想做图像处理的爱人有必然的佑助。

  后面包车型客车shuffle一时的获得的变量为:

    为了完结我们的指标,大家就要选择SSE中无所畏惧的shuffle指令了,假如能够把shuffle指令运用的过硬,能够拿走很多很有趣的效率,有如鸠摩智的天山杖法一样,能够催动查拳发、袈裟服魔攻等等,成就世间能和自家鸠摩智打成平成的尚未多少人一样的伟业。哈哈,说远了。