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

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_shuffle_epi8(Src2,
_mm_setr_epi8(-1, -1, -1, -1, -1, -1, 2, 5, 8, 11, 14, -1, -1, -1,
-1, -1))拿到的临时数据为:

    为了达成我们的目标,我们将要采用SSE中强大的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

  
对于这种单像素点、和天地无关的图像算法,为了能接纳SSE提升程序速度,一个基本的步骤就是把各颜色分量分离为单身的连续的变量,对于24位图像,大家了然图像在内存中的布局为:

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

  接着分析,由于代码中有((马克斯 –
Blue) * AmtVal) >> 14,其中AmtVal
= (Max – Avg) * Adjustment,展开即为:  ((马克斯(Max) – Blue) * (Max – Avg) *
Adjustment)>>14;那几个数据相乘很大程度上会超出short所能表达的限量,由此,我们还亟需对地点的16位数据举办扩充,扩张到32位,这样就多了累累发令,那么有没有不需要扩充的措施呢。经过一番构思,我提议了下述解决方案:

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

     
很其他多划算都是不可能直接在如此的限量内举行了,由此就有必要将数据类型扩大,比如扩大到short类型或者int/float类型。

     
好,说道这里,我们延续看大家C语言里的这句:

   来个速度相比较:

     

 

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代码为:

       简单的分析了本来饱和度算法的贯彻,分享了其SSE实现的历程,对于那个刚刚接触SSE,想做图像处理的情侣有自然的提携。

     
首先,一遍性加载48个图像数据到内存,正好放置在五个__m128i变量中,同时其它一个很好的政工就是48正要能被3整除,也就是说我们全体的加载了16个24位像素,这样就不会冒出断层,只表示下边48个像素能够和现在的48个像素使用同一的艺术开展拍卖。

     
 那么那一个算法的内在是何许促成的吧,我一直不仔细的去探讨他,但是在开源软件PhotoDemon-master(开源地址:https://github.com/tannerhelland/PhotoDemon,visual
basic
6.0的著作,我的最爱)提供了一个有点相像的功效,我们贴出他对改效果的一对注释:

  Src3中的数据则为:

     我们先贴下代码:

 

  再留意abs里的参数, 马克斯 –
Avg,这有必不可少取相对值吗,最大值难道会比平均值小,浪费时间,最后改为:

 

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

  其中

       
写的的确好累,休息去了,觉得对您有效的请给我买杯干白或者咖啡呢。

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

  大旨依旧这一个常数的取舍。

 

      float VibranceAdjustment = -0.01 * Adjustment / 127.0f;

    这是浮点版本的粗略优化,假如不勾选编译器的SSE优化,直接使用FPU,对于一副3000*2000的24位图像耗时在I5的一台机器上运行用时大概70毫秒,但这不是第一。

      我们需要把它们成为:

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

  下面的VB6.0的耗时是原作者的代码编译后的实施进度,如若自己要好去用VB6.0去优化他的话,有信念能做到70ms以内的。

     
以上是拍卖的首先步,看上去这多少个代码很多,实际上他们的实施时很是快的,3000*2000的图那么些拆分和集合过程也就大致2ms。

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

      Src2中保留着:

 

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
Max8 = _mm_max_epu8(_mm_max_epu8(Blue8, Green8), Red8);

 
 可以看来进展上述操作后Blue8的签6个字节已经符合我们的要求了。

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

     
 大家领略,SSE对于跳转是很不自己的,他不行擅长系列化处理一个作业,即使她提供了广大相比较指令,可是不少意况下复杂的跳转SSE仍然无论为力,对于本例,情形相比优异,如若要接纳SSE的可比指令也是可以直接实现的,实现的章程时,使用相比指令获得一个Mask,Mask中符合比较结实的值会为FFFFFFFF,不合乎的为0,然后把这么些Mask和前边需要总括的某部值举办And操作,由于和FFFFFFFF进行And操作不会变动操作数本身,和0举行And操作则变为0,在无数状态下,就是无论你符合条件与否,都进展末端的揣度,只是不符合条件的臆度不会影响结果,这种总结可能会失效SSE优化的片段提速效果,这一个将要具体情况具体分析了。

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

  很简短的算法,先求出每个像素RGB分量的最大值和平均值,然后求两者之差,之后依据输入调节量求出调整量。

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

     
 VB的语法有些人或者不熟习,我稍稍做点更改翻译成C的代码如下:

      float AmtVal = (Max - Avg) * VibranceAdjustment;
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

     

   
 简单的领悟shuffle指令,就是将__m128i变量内的各样数据遵照指定的逐条举行双重布置,当然那么些布阵不自然要统统拔取原本的多寡,也足以重复某些数据,或者某些地方无数据,比如在推行下面这条指令

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

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

     
注意观望本例的代码,他的原意是只要最大值和某个分量的值不等同,则举办末端的调整操作,否则不开展调试。可前边的调动操作中有最大值减去该分量的操作,也就表示如果最大值和该分量相同,两者相减则为0,调整量此时也为0,并不影响结果,也就相当于尚未调节,因而,把这几个规则判断去掉,并不会潜移默化结果。同时考虑到实际情状,最大值在重重场地也只会和某一个份量相同,也就是说只有1/3的概率不履行跳转后的说话,在本例中,跳转后的代码执行复杂度并不高,去掉这一个条件判断从而扩展一道代码所耗费的性能和压缩3个判断的刻钟已经在一个水准上了,由此,完全可以去除这一个判断语句,这样就卓殊适合于SSE实现了。

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

 

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
G6 R6 B7 G7 R7 B8 G8 R8 B9 G9 R9 B10 G10 R10 B11 G11
     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);

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

 

 

 

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

  假诺通晓了由BGRBGRBGR
—》变为了BBBGGGRRR这样的情势的法则后,那么由BBBGGGRRR–>变为BGRBGRBGR的道理就非凡浅显了,这里不赘述,直接贴出代码:

     
在SSE里开展如此的操作也是非凡简单的,SSE提供了汪洋的数据类型转换的函数和指令,比如有byte扩展到short,则可以用_mm_unpacklo_epi8和_mm_unpackhi_epi8配合zero来实现:

   
   首先,我们将她恢弘为运动16位的结果,变为如下:

   
 假若把这么些临时结果和事先的Blue8举办或操作如故平昔开展加操作,新的Blue8变量则为:

 

  此时统计Avg就水到渠成了:

 

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

 

    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)));
  • 格林(Green) + 格林(Green) +
    Red在大多数情景下都会超出255而相对小于255*4,,因而我们需要扩张数据到16位,按上述格局,对Blue8\Green8\Red8\马克斯(Max)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_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)));

     
Adjustment我们早已将她限定在了[-128,128]以内,而(马克斯(Max) –
Avg)理论上的最大值为255 –
85=170,(即RGB分量有一个是255,其他的都为0),最小值为0,由此,两者在独家范围内的成就不会高于short所能表明的限制,而(马克斯(Max)-Blue)的最大值为255,最小值为0,在乘以4也在short类型所能表达的限定内。所以,下一步你们懂了吗?

       注意咱们的这一个表明式:

 

     
大家着重讲下这个算法的优化及其SSE实现,特别是SSE版本代码是本文的严重性。

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

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

C语言 1

  可以见见,这里的统计是无法再byte范围内成功的,中间的Blue

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

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

   很有意思的操作,比如_mm_unpacklo_epi8是将多少个__m128i的低8位交错布置形成一个新的128位数据,倘使内部一个参数为0,则就是把此外一个参数的低8个字节无损的恢宏为16位了,以上述BL16为例,其中间布局为:

   最后优化速度:5ms。

   Blue8中的多少为:

     
如上代码,则Src1中保留着:

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

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

     
当然由于字节数据类型的表述范围万分简单,除了少有的几个简单的操作能针对字节类型直接处理外,比如本例的丘RGB的马克斯(Max)值,就可以直接用下边的SIMD指令实现:

Zero = _mm_setzero_si128();

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

结论:

 

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版本的优化。

   
 再一次和事先的Blue8结果举行或操作得到终极的结果:

       

       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.

     
 闲话不多说了,其实自然饱和度也是如今多少个版本的PS才面世的功用,在调试有些图片的时候会有不易的效率,也能够视作简单的肤色调整的一个算法,比如下边这位小姐,用自然饱和度即可以让她失血过多,也得以让他肤色红晕。

     
 第一我们把/127改为/128,这基本不影响意义,同时Adjustment默认的界定为[-100,100],把它也线性扩充一点,比如扩展1.28倍,扩充到[-128,128],这样在末了大家五遍性移位,裁减中间的损失,大概的代码如下:

  Vibrance这么些单词搜索翻译一般震荡,抖动或者是响当当、活力,可是官方的词汇里还平素未出现过自然饱和度这多少个词,也不知底当时的Adobe中文翻译人士怎么会这么处理。不过我们看看PS对那一个职能的分解:

   大家在贴出他的主题代码:

 

   
 处理完后大家又要把他们恢复生机到原来的BGR布局。

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

   
 对于格林(Green)和Red分量,处理的点子和步子是平等的,只是出于地点不同,每便举办shuffle操作的常数有所不同,但原理完全一致。

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

   
超高速指数模糊算法的落实和优化(10000*10000在100ms左右兑现 一文中,我在篇章最后提到了极点的一个下令:_mm_mulhi_epi16(a,b),他能五回性处理8个16位数据,其统计结果一定于对于(a*b)>>16,但这里很明a和b必须是short类型所能表明的范围。

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

     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

 

  假设我们需要举行在int范围内举办测算,则还需进一步壮大,此时可以动用_mm_unpackhi_epi16/_mm_unpacklo_epi16匹配zero继续进行扩充,这样一个Blue8变量需要4个__m128i
int范围的数码来抒发。

     
 确实是和饱和度有关的,这样敞亮粤语的翻译反而倒是合理,那么只可以怪Adobe的开发者为啥给这几个效用起个名字叫Vibrance了。

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

 

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

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

     
 C语言 2   
 C语言 3   
 C语言 4

  但好歹,SSE优化的快慢提高是远大的。

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

  这么些的结果和PS的是比较像样的,最起码趋势是十分相近的,可是细节仍旧不一致,可是可以断定的是可行性是对的,假如您肯定要复制PS的结果,我指出你花点时间变更其中的一些常数或者统计模式看看。应该能有拿到,国内曾经有人摸索出来了。

      第一步优化,去除掉不必要总括和除法,很理解,这一句是本段代码中耗时较为彰着的一些

  中间两个格林相加是用运动如故平素相加对速度没啥影响的。