菜单

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

2018年9月24日 - 中甲报道
      float AmtVal = (Max - Avg) * VibranceAdjustment;
    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));

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

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

   
 处理了晚我们而比方把他们恢复至原始之BGR布局。

    为了达到我们的目的,我们且使SSE中强的shuffle指令了,如果能将shuffle指令运用的棒,可以获很多大有趣的力量,有如鸠摩智的稍无相功一样,可以催动拈花指发、袈裟服魔攻等等,成就世间能同自己鸠摩智打成平成的没有几独人口同一的丰功伟绩。哈哈,说远了。

  接着分析,由于代码中生((Max –
Blue) * AmtVal) >> 14,其中AmtVal
= (Max – Avg) * Adjustment,展开就为:  ((Max – Blue) * (Max – Avg) *
Adjustment)>>14;这三独数据相乘很要命程度达到会见超过short所能发表的限,因此,我们还用对端的16各项数据进行扩张,扩展及32各,这样便基本上矣成百上千指令,那么有没有起非需扩大的措施吗。经过一番虑,我提出了下述解决方案:

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

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

  Src3遭之多少虽然为:

     
如齐代码,则Src1挨保留着:

     我们事先贴下代码:

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

     
很其他多计都是无力回天直接以这样的限定外开展了,因此即便发出必要将数据类型扩展,比如扩展及short类型或者int/float类型。

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

   
 处理终结后我们而比方拿她们过来至原有的BGR布局。

 

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

   
 如果管这临时结果与之前的Blue8进行或者操作还是一直开展加操作,新的Blue8变量则也:

      我们需要将它变成:

   
 为了落实之意义,我参考了采石工大侠的关于代码,分享如下:

     
在SSE里进行这样的操作也是非常简单的,SSE提供了汪洋的数据类型转换的函数和下令,比如有byte扩展及short,则好为此_mm_unpacklo_epi8和_mm_unpackhi_epi8配合zero来实现:

     
Adjustment我们曾经以他限定在了[-128,128]里面,而(Max –
Avg)理论及之无比酷价值为255 –
85=170,(即RGB分量有一个是255,其他的还也0),最小值为0,因此,两者在分别范围外之成就不见面超越short所能发挥的限制,而(Max-Blue)的绝要命价值为255,最小值为0,在就以4啊以short类型所能够表达的界定外。所以,下同样步你们了解了呢?

  但不管怎样,SSE优化的快提升是宏大的。

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

     
Adjustment我们早已以他限定在了[-128,128]以内,而(Max –
Avg)理论及之极端酷价值也255 –
85=170,(即RGB分量有一个凡是255,其他的都也0),最小值为0,因此,两者在独家范围外之实绩不见面高于short所能发挥的限量,而(Max-Blue)的顶要命价值也255,最小值为0,在乘以4啊以short类型所能够表达的界定外。所以,下一样步你们了解了呢?

      float VibranceAdjustment = -0.01 * Adjustment / 127.0f;

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

       
写的实在好累,休息去矣,觉得对而行的呼吁给自己采购杯啤酒或咖啡吧。

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

       

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

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

 

     
 图片 1   
 图片 2   
 图片 3

    为了上我们的目的,我们且使SSE中强大的shuffle指令了,如果会管shuffle指令运用的深,可以博得很多雅风趣的法力,有如鸠摩智的略微无相功一样,可以催动拈花指发、袈裟服魔攻等等,成就世间能同自鸠摩智打成平成的没有几只人同样的伟业。哈哈,说多矣。

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

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

 

 

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

  很粗略的算法,先要出每个像素RGB分量的极老价值与平均值,然后求两者之异,之后根据输入调节量求出调整量。

     
 确实是跟饱和度有关的,这样敞亮中文的翻译反而却合理,那么只能怪Adobe的开发者为什么让此力量于个名为Vibrance了。

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

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

 

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

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

     
 最后咱们第一来讲讲SSE版本的优化。

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

  但好歹,SSE优化的快慢提升是宏大的。

     
 图片 4   
 图片 5   
 图片 6

     我们先贴下代码:

       注意我们的斯表达式:

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

图片 7

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

     

  接着分析,由于代码中发出((Max –
Blue) * AmtVal) >> 14,其中AmtVal
= (Max – Avg) * Adjustment,展开就为:  ((Max – Blue) * (Max – Avg) *
Adjustment)>>14;这三只数据相乘很老程度及会胜出short所能够表达的限定,因此,我们尚需针对点的16各类数据开展扩张,扩展至32各项,这样就多矣森命令,那么有没有发出未待扩大的道吗。经过一番构思,我提出了下述解决方案:

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

     
 闲话不多说了,其实当饱和度也是近年来几个版本的PS才起的作用,在调节有些图片的时段会时有发生科学的效能,也堪作为简单的肤色调整的一个算法,比如下面这号姑娘,用自然饱和度即好为她失血过多,也堪于他肤色红晕。

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

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

     
 第一咱们拿/127反吗/128,这基本无影响效果,同时Adjustment默认的界定吗[-100,100],把它也线性扩大一点,比如扩大1.28倍增,扩大到[-128,128],这样以最终我们一次性移位,减少中间的损失,大概的代码如下:

   在拘留代码的生一样句子:

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

BL16 = _mm_unpacklo_epi8(Blue8, Zero);
BH16 = _mm_unpackhi_epi8(Blue8, Zero);
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

     
 那么这算法的内在是什么样促成的吗,我没有仔细的失研究他,但是以开源软件PhotoDemon-master(开源地址:https://github.com/tannerhelland/PhotoDemon,visual
basic
6.0的著作,我之尽轻)提供了一个略带相似之功力,我们贴发他本着转移效果的部分注释:

   Blue8中的数量为:

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

     
首先,一次性加载48个图像数据到内存,正好停于三独__m128i变量中,同时另外一个不行好之政工就是是48恰好能被3整理除,也就是说我们圆的加载了16独24位像素,这样就是不见面并发断层,只表示下面48只像素可以与今天之48个像素使用同一的方法进行拍卖。

Zero = _mm_setzero_si128();

       简单的解析了自饱和度算法的实现,分享了那个SSE实现的进程,对于那些刚接触SSE,想做图像处理的恋人发出一定之帮助。

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

   很风趣的操作,比如_mm_unpacklo_epi8凡是以少只__m128i的低8位交错布置形成一个初的128号数据,如果中间一个参数为0,则就是是把另外一个参数的低8只字节无损的扩充为16位了,以上述BL16啊条例,其里面布局也:

 

 

     
 我们明白,SSE对于跳转是杀无谐和之,他很擅长序列化处理一个作业,虽然他提供了众较指令,但是不少情下复杂的跳转SSE还是无为力,对于本例,情况较特别,如果要是以SSE的于指令也是可一直促成之,实现之法子时,使用于指令得到一个Mask,Mask中符合于结实的值会为FFFFFFFF,不相符的为0,然后把这个Mask和后要计算的之一值进行And操作,由于和FFFFFFFF进行And操作不会见改变操作数本身,和0进行And操作则变为0,在成千上万动静下,就是无你符合条件与否,都开展末端的乘除,只是不符合条件的计不见面影响结果,这种计算可能会见劳而无功SSE优化的有些提速效果,这个将要具体情况具体分析了。

     

  中间两个Green相加是因此移动或直接相加对速度没有啥影响的。

     
注意观察本例的代码,他的本心是要是尽酷价值与有分量的价值不等同,则开展末端的调操作,否则不进行调剂。可后面的调操作中来极其酷价值减去该分量的操作,也就是意味着要尽要命价值和欠分量相同,两者相减则为0,调整量此时也为0,并无影响结果,也便一定给尚未调节,因此,把此法判断失丢,并无见面影响结果。同时考虑到骨子里情况,最深价值当不少情景为只见面跟某个一个轻重相同,也就是说只生1/3之几率不执跳转后底说话,在本例中,跳反后底代码执行复杂度并无愈,去丢这些规范判断用增加一道代码所消耗的性及削减3独判断的年月已经以一个水平及了,因此,完全可以去这些判断语句,这样便非常适合于SSE实现了。

结论:

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

 

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

结论:

       注意我们的此表达式:

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

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

图片 8

 

    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_packus_epi16(BL16, BH16);
  Green8 = _mm_packus_epi16(GL16, GH16);
  Red8 = _mm_packus_epi16(RL16, RH16);

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

 

 

  其中

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

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

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

  可以视,这里的算计是心有余而力不足再byte范围外得的,中间的Blue

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

 

  可以看到,这里的测算是无能为力再byte范围外成功的,中间的Blue

 

     
以上是处理的率先步,看上去是代码很多,实际上他们的施行时老抢之,3000*2000底图这拆分和集合过程为即横2ms。

    这是浮点版本的简约优化,如果无勾选编译器的SSE优化,直接运用FPU,对于一副3000*2000底24号图像耗时在I5的如出一辙玉机械及运行用时盖70毫秒,但立刻不是非同小可。

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

 

   
在超过高速指数模糊算法的实现和优化(10000*10000以100ms左右贯彻 一和平遭遇,我在篇章最后提到了极的一个命:_mm_mulhi_epi16(a,b),他能够一次性处理8独16各数据,其计算结果一定给对于(a*b)>>16,但这边十分明a和b必须是short类型所能够表达的限制。

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

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

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

 

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

     
首先,一次性加载48单图像数据及内存,正好停在三独__m128i变量中,同时另外一个异常好之业务就是是48刚能吃3整除,也就是说我们圆的加载了16个24各项像素,这样尽管不会见产出断层,只表示下面48独像素可以跟当今之48个像素使用同一的道开展处理。

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

 

 

     
好,说道这里,我们继承羁押我们C语言里之即刻词:

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

 

 

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

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

  /127.0f可以优化为乘法,同时注意VibranceAdjustment在里不转移,可以将她们成到循环的极致外层,即改吗:

   Blue8中的数吧:

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

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

 

      我们要将其成为:

 

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

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

     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

 

   我们于粘贴出他的基本代码:

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

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

   来只速度较:

  很简短的算法,先要出每个像素RGB分量的无限特别价值与平均值,然后求两者之异,之后据悉输入调节量求出调整量。

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

 

   
 简单的知情shuffle指令,就是以__m128i变量内之相继数据据指定的逐一进行更摆,当然这布阵不肯定要是了用本来的数量,也堪还某些数据,或者某些位置多本,比如当执行下这条指令

 

  最后一步就是是以这些16个的数再度转移为8位的,注意原始代码中有Clamp操作,这个操作实际是单耗时的进程,而SSE天然的有抗饱和的函数。

 

版本 VB6.0 C++,float优化版本 C++定点版 C++/SSE版
速度 400ms 70ms 35ms 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
_mm_setr_epi8指令的参数顺序可能更适合于我们常用的从左到右的理解习惯,其中的某个参数如果不在0和15之间时,则对应位置的数据就是被设置为0。
    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)));

  如果知道了由于BGRBGRBGR
—》变为了BBBGGGRRR这样的模式的原理后,那么由BBBGGGRRR–>变为BGRBGRBGR的道理就好浅显了,这里不赘述,直接贴发出代码:

  如果掌握了由BGRBGRBGR
—》变为了BBBGGGRRR这样的模式的规律后,那么由BBBGGGRRR–>变为BGRBGRBGR的道理就特别浅显了,这里不赘述,直接贴发出代码:

  这个的结果以及PS的凡较接近的,最起码趋势是蛮相近的,但是细节要无平等,不过可以判定的是主旋律是针对性的,如果您早晚要复制PS的结果,我提议您花点时间转移中的一些常数或者计算方法看看。应该会发生收获,国内就有人摸出了。

  这个的结果及PS的是比较像样的,最起码趋势是老接近的,但是细节要不雷同,不过好判的凡来势是指向之,如果你势必要是复制PS的结果,我提议乃花点时间改中的片段常数或者计算办法省。应该能够来获取,国内曾经有人搜出了。

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

     
如达到代码,则Src1负保留在:

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

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

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

      第一步优化,去排除不必要计算和除法,很显,这无异词是本段代码中耗时较为强烈的片段

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

Zero = _mm_setzero_si128();

  后面的shuffle临时之获的变量为:

 

   来单速度比:

  这句之晚半局部和前的近乎,只是内的常数不同,由_mm_shuffle_epi8(Src2,
_mm_setr_epi8(-1, -1, -1, -1, -1, -1, 2, 5, 8, 11, 14, -1, -1, -1,
-1, -1))得到的临时数据吧:

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

  上面的VB6.0的耗时凡原作者的代码编译后的实施进度,如果自身好失去用VB6.0去优化他的说话,有信心能够完成70ms以内的。

 

     
最后这等同句和Blue8相关的代码为:

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

    这是浮点版本的概括优化,如果不勾选编译器的SSE优化,直接使用FPU,对于一副3000*2000底24各类图像耗时在I5的均等贵机械及运行用时盖70毫秒,但当时不是重中之重。

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

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

  这句之继半组成部分以及前的类,只是内的常数不同,由_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 0 B2 0 B3 0 B3 0 B4 0 B5 0 B6 0 B7 0
    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)));

  其中

   
在跨越快速指数模糊算法的贯彻和优化(10000*10000以100ms左右兑现 一柔和遭遇,我以篇章最后提到了顶点的一个命:_mm_mulhi_epi16(a,b),他会一次性处理8单16各项数据,其计算结果相当给对于(a*b)>>16,但这边特别明a和b必须是short类型所能够发挥的界定。

     
当然由于字节数据列的表述范围非常少,除了少有的几只简单的操作会对字节类型直接处理外,比如本例的丘RGB的Max值,就好直接用底的SIMD指令实现:

 

  Vibrance这个单词搜索翻译一般震荡,抖动或者是响、活力,但是官方的词汇里还向未出现了本饱和度这个词,也非明白这底Adobe中文翻译人员怎么会这样处理。但是咱省PS对是力量的说:

  Vibrance这个单词搜索翻译一般震荡,抖动或者是响当当、活力,但是官方的词汇里还根本未出现了本饱和度这个词,也无知道这之Adobe中文翻译人员怎么会如此处理。但是咱看PS对斯职能的说明:

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

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

 

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

     
注意观察本例的代码,他的本意是设尽充分价值和某某分量的价未均等,则展开末端的调整操作,否则不开展调节。可后面的调整操作着发出无限充分价值减去该分量的操作,也不怕代表如果尽老价值与该分量相同,两者相减则也0,调整量此时吗为0,并无影响结果,也就是相当给无调节,因此,把这个条件判断失丢,并无会见潜移默化结果。同时考虑到实际情形,最特别价值在重重状为唯有会暨某个一个份额相同,也就是说只发生1/3底概率不实行跳转后底口舌,在本例中,跳反后的代码执行复杂度并无强,去丢这些条件判断用增加并代码所耗费的性质与压缩3只判断的时空都当一个水平及了,因此,完全好去除这些判断语句,这样就算非常适合于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);
        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的签6个字节已经入我们的需求了。

  上面的VB6.0的耗时凡原作者的代码编译后底行进度,如果本身要好去用VB6.0去优化他的语,有信念会做到70ms以内的。

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

'***************************************************************************
'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/
'
'***************************************************************************
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

     
很其他基本上算都是无能为力直接当这么的限制外展开了,因此即使出必不可少将数据类型扩展,比如扩展至short类型或者int/float类型。

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

 

 

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

 

    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函数实现:

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

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

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

       

   
 对于Green和Red分量,处理的措施以及手续是平等的,只是出于位置不同,每次进行shuffle操作的常数有所不同,但原理完全等同。

     
 那么这个算法的内在是如何促成的吧,我没有仔细的失研究他,但是当开源软件PhotoDemon-master(开源地址:https://github.com/tannerhelland/PhotoDemon,visual
basic
6.0的创作,我的最为轻)提供了一个稍微相似的效力,我们贴发他本着改变效果的有些注释:

  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

   最终优化速度:5ms。

 

      Src2备受保存在:

 

 

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

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

      Src2遭受保存在:

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

       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.

     
 第一我们拿/127变动也/128,这基本未影响效果,同时Adjustment默认的限制为[-100,100],把它为线性扩大一点,比如扩大1.28加倍,扩大到[-128,128],这样于最后我们一次性移位,减少中间的损失,大概的代码如下:

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

        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)));
'***************************************************************************
'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/
'
'***************************************************************************

 

  对应的SSE代码为:

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

   最终优化速度:5ms。

       简单的辨析了当饱和度算法的落实,分享了该SSE实现的长河,对于那些刚接触SSE,想做图像处理的心上人发自然之助。

      float VibranceAdjustment = -0.01 * Adjustment / 127.0f;

  再小心abs里的参数, Max –
Avg,这发生必要取绝对值吗,最酷价值难道会较平均值小,浪费时间,最后移也:

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

  /127.0f可以优化为乘法,同时注意VibranceAdjustment在里边不转移,可以拿她们组成到循环的卓绝外层,即改变吧:

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

  对应的SSE代码为:

 

     
以上是拍卖的第一步,看上去是代码很多,实际上他们之推行时特别急匆匆的,3000*2000的希冀是拆分和合过程也就算大概2ms。

     
 我们明白,SSE对于跳转是坏无友善之,他万分擅长序列化处理一个政工,虽然他提供了多比指令,但是不少情形下复杂的跳转SSE还是无为力,对于本例,情况比特殊,如果只要运SSE的于指令也是可以一直促成之,实现的措施时,使用于指令得到一个Mask,Mask中可于结实的值会为FFFFFFFF,不入的为0,然后将这Mask和后要计算的有值进行And操作,由于和FFFFFFFF进行And操作不会见改变操作数本身,和0进行And操作则变为0,在诸多情景下,就是凭你符合条件与否,都进行末端的精打细算,只是不符合条件的测算不见面影响结果,这种计算可能会见失效SSE优化的片提速效果,这个将要具体情况具体分析了。

  最后一步就是是将这些16位的数还转移为8各类的,注意原始代码中发出Clamp操作,这个操作实际是单耗时的过程,而SSE天然的保有抗饱和的函数。

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

       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.

   
   首先,我们将他恢弘为移动16员之结果,变为如下:

 

 

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

   
 为了实现这作用,我参考了采石工大侠的关于代码,分享如下:

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

      float AmtVal = (Max - Avg) * VibranceAdjustment;

     

 

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

   以圈代码的下同样句:

   
 简单的明白shuffle指令,就是以__m128i变量内之逐一数据据指定的一一进行重复布置,当然是布阵不肯定要完全使用本来的数,也得以还某些数据,或者某些位置多按,比如当实施下就长长的指令

     
 闲话不多说了,其实当饱和度也是近年来几个版本的PS才起的功能,在调节有些图片的时光会发生是的功效,也堪作为简单的肤色调整的一个算法,比如下面这号姑娘,用自然饱和度即好叫她失血过多,也堪为他肤色红晕。

相关文章

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图