SSE图像算法优化类别八:自然饱和度(Vibrance)算法的上行下效实现及其SSE优化(附源码,可看成SSE图像入门,Vibrance算法也可用以简单的肤色调整)。

by admin on 2019年3月10日

 

叁 | 离别与重聚

在第1回世界大战时期,纳粹在亚洲残害了近600万犹太人。前后有约3.5万犹太难民避难在新加坡或路过前往第叁国,他们中间除无疾而终外大多都幸存了下去。

日本妥洽后,犹太难民们交叉离开北京,对这几个生活了八年的城池,他们怀有一种特殊的心绪。

亚脱门利女士在17周岁的花样年华来到东京,后来他又回到那里,她说那是他的第一家门。

亚脱门利1捌周岁时的居住证

亚脱门利重反新加坡

现居U.S.A.的贝蒂老人一家和情侣们重回新加坡,他们一度住在通辽路51号。

Betty一家

布罗门撒尔老人曾在新加坡居留八年,他说本身家族在澳洲的骨血都没能幸存下来,北京的劳碌岁月影响了他以后的做官轨迹。离开法国首都后他去到美利哥,出任了美利坚同盟国政坛的财政市长。

United States前财政部市长布罗门撒尔

布兰德女士跟随家里人逃到新加坡时只是三个小女孩,70年后他回到北京,中中原人民共和国情人将她当场留存下的护照交还给她。

布兰德女士再次来到巴黎

犹太美学家亚瑟先生尚未选取离开,他留下来在上音乐教育小提琴演奏。与世长辞后,他也葬在了此处。

Arthur先生和他的住地

今天的Moses会堂上依然有一颗明亮的戴维星,中夏族民共和国布衣和以色列国(The State of Israel)布衣的情谊积厚流光,亲欧洲和美洲的以色列(Israel)却是中东最早认同新中中原人民共和国的国家。

以色列(Israel)总理内塔尼亚胡曾说到:

大家将会永远铭记你们,永远不会忘记这一段历史。

近期,Moses会堂已设立新加坡犹太难民记忆馆,侧边的墙面上密密麻麻的雕饰了137三二十一个犹太难民的名字。

Moses会堂难民墙

墙面上也记录着几段犹太难民的警句:

——明日大家将去三个目生的都会生活。不纯熟的言语,目生的气象和人群,然则在那边,我们是高枕无忧和轻易的。

——当时,没有一个使领事馆给大家发签证。然则,有一天,作者去了中国领事馆,意况产生了转移……大家买到了Bianco
Mano的船票。那是一艘意国邮轮,在1940年12月尾从罗兹相差,前往中中原人民共和国香江,航程约30天。

——那是二回心思之旅,当作者和这些中中原人民共和国定居者道别时,大家都含着泪水。大家已经在菲律宾人的统治下共同相处,那段经历使大家发出了一种亲近感,就接近互相是家里人一样。


参考文献及图片出处:

《虹口回忆,犹太难民的生活》学林出版社

《犹太人在新加坡》北京画报出版社

《永恒的回忆》东方之珠世纪出版社

《生命的记念——犹太人在东京》纪录片,法国巴黎广播电视台新闻大旨

巴黎犹太难民纪念馆

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

壹 | 远东的避难所

上个世纪三 、四十年份,新加坡是亚洲的自由港、远东国际金融中央、冒险家的福地,比较纳粹德意志,遥远的华夏从未有过反犹主义,当时民国的有识之士和东京犹太组织都主动的为犹太难民奔走求助。

早在一九三四年,以宋庆龄女士为首的一个代表团就曾向德意志联邦共和国驻新加坡首脑事声讨了纳粹的反犹暴行,这一个代表团里有民国文化界的长者们——蔡仲申、周豫山、林玉堂。

宋庆龄女士的代表团

一九三三年,纳粹德意志联邦共和国发表《斯特拉斯堡法治》,犹太人被剥夺公民义务,希特勒走出了种族迫害的第3步。

时任中华民国驻苏黎世大使何凤山同情犹太人的饱受,在布宜诺斯艾Liss一百三十八个多国家的外交官中,唯有他向犹太难民发放出大量的签证。

何凤山淡薄名利,直到1999年在美利坚联邦合众国华沙死去,在她的葬礼上女儿才揭露老爸这段传说。何凤山的名字明日还刻在萨尔瓦多的屠杀记念馆中。两千年,以色列国(The State of Israel)政坛授予他“国际义士”的光荣。

民海外交官何凤山大学生

何人又理解,希特勒即将在澳国舞动他的屠刀……一张船票、一纸签证的背后都是一条条鲜活的性命。由此,难民们纷纭逃离澳洲,远渡大洋踏上了炎黄的土地。

犹太难民抵达北京

即刻的十六铺码头挤满了人群和货物,和平女神像张开翅膀平静的鸟瞰着外滩万国建筑群,东京的犹太组织也开端为逃难的同胞们提供救济。

上个世纪叁 、四十年间的香水之都外滩

       
写的真的好累,休息去了,觉得对你有效的请给自个儿买杯苦艾酒可能咖啡呢。

贰 | 犹太人在香江

犹太民族是四个笃信宗教的部族,他们在逃亡的时日中尚无丢弃自身的笃信。Moses会堂(现长阳路62号)便是难民们的宗派场馆。

一对犹太新人在Moses会堂举行婚礼

她俩住在东京小弄堂的亭子间、阁楼里,用中式厨房做起了西式餐点。

犹太难民的公馆

犹太人善于经营商业,崇尚文艺。他们在隔开区办起了全校、报社、还开起了商铺。

宝鸡路的临沂咖啡吧

她俩在上午太阳洒落的屋顶上啜着咖啡拉着小提琴,孩童们则在破旧的大街上打闹。

犹太小孩子在街头打弹子

想必他们知道平静的生存是打败恐惧最可行的解药。

屋顶小憩

在那么狼狈的条件中,犹太青年们甚至还在东京办起了协调的消息杂志社。

杂志“大家的生存”编辑部

在虹口隔开分离区居住的犹太难民们与中中原人民共和国人民和平相处,不少犹太人有友好的中原房东,还和中华同事们一道干活,建立了巩固的友情。

小女孩们在一块玩耍

亲朋们在虹口游泳池

  宗旨依然这个常数的取舍。

壹玖叁伍年起,数以万计的犹太人为逃离纳粹的恐惧统治来到向他们敞开大门的中原东京。

   
 即使把那么些权且结果和后面包车型地铁Blue8进行或操作依旧直接举行加操作,新的Blue8变量则为:

一九三六年,Hong Kong被侵华日军占领,依旧有2.5万名犹太人把那里作为他们的避难所。他们被东瀛政党迁入隔开分离区里居住,与中华无名小卒们同甘苦、共劫难。

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

新加坡犹太难民纪念馆的七盏圣烛

   
超高速指数模糊算法的实现和优化(一千0*10000在100ms左右贯彻 一文中,小编在篇章最终提到了极限的二个下令:_mm_mulhi_epi16(a,b),他能2次性处理几个十五人数据,其总结结果约等于对于(a*b)>>16,但那边很明a和b必须是short类型所能表明的限定。

翻看一组组黑白照片,看到是3个笼罩着谢世的骚乱时代,一座传说的城池,多个受难的民族,一段尘封的野史。

  前边的shuffle最近的收获的变量为:

辛Diller名单拯救了一千多名犹太人,而中华东京解救了三万。

——“香江犹太人”、前美利坚合众国财政县长布罗门撒尔

     
 那么那一个算法的内在是哪些落到实处的呢,小编未曾仔细的去琢磨他,但是在开源软件Photo德姆on-master(开源地址:https://github.com/tannerhelland/PhotoDemon,visual
basic
6.0的小说,作者的最爱)提供了八个不怎么相像的功力,大家贴出他对改效果的部分注释:

第3遍世界大战时期,希特勒冷酷迫害犹太人,在纳粹德意志联邦共和国的种族主义阴影下差不离全体的欧洲和美洲发达国家都不容或限制犹太难民入境。

     
在SSE里展开如此的操作也是格外不难的,SSE提供了大批量的数据类型转换的函数和指令,比如有byte扩充到short,则足以用_mm_unpacklo_epi8和_mm_unpackhi_epi8配合zero来实现:

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

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

     
我们重点讲下那个算法的优化及其SSE达成,尤其是SSE版本代码是本文的重中之重。

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

  上边包车型大巴VB6.0的耗费时间是原文者的代码编写翻译后的进行进程,假设本人自身去用VB6.0去优化他的话,有信心能达成70ms以内的。

   大家在贴出他的中坚代码:

     
注意观察本例的代码,他的本心是要是最大值和某些分量的值不平等,则开始展览末端的调动操作,不然不开始展览调试。可前面包车型客车调动操作中有最大值减去该分量的操作,也就象征一旦最大值和该分量相同,两者相减则为0,调整量此时也为0,并不影响结果,也就一定于尚未调节,由此,把这一个原则判断去掉,并不会潜移默化结果。同时考虑到骨子里景况,最大值在很多动静也只会和某叁个分量相同,相当于说唯有三分之一的概率不实施跳转后的口舌,在本例中,跳转后的代码执行复杂度并不高,去掉这一个条件判断从而扩大一道代码所花费的品质和压缩三个判断的时光已经在1个水准上了,由此,完全能够去除那一个判断语句,那样就万分适合于SSE达成了。

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

  可以见见,那里的总结是无能为力再byte范围内到位的,中间的Blue

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

     
很其余多划算都以不可能间接在如此的限量内展开了,由此就有必不可中校数据类型扩张,比如扩张到short类型只怕int/float类型。

     大家先贴下代码:

 

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

       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.

    为了达成大家的目标,大家即将采纳SSE中强有力的shuffle指令了,假若能够把shuffle指令运用的独具匠心,可以拿走很多很有意思的遵从,有如鸠摩智的天山六阳掌一样,能够催动大慈大悲千手式发、袈裟服魔攻等等,成就世间能和本身鸠摩智打成平成的尚未多少人一如既往的伟业。哈哈,说远了。

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

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

 

  但好歹,SSE优化的速度进步是高大的。

     
 闲话不多说了,其实自然饱和度也是而今几个本子的PS才出现的遵从,在调节有些图片的时候会有不易的效果,也足以作为简单的肤色调整的3个算法,比如上面那位大姨娘,用自然饱和度即可以让她失血过多,也足以让她肤色红晕。

  大家来设想有个别近似和永恒优化。

        float AmtVal = (abs(Max –
Avg) / 127.0f) * 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));
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
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
G6 R6 B7 G7 R7 B8 G8 R8 B9 G9 R9 B10 G10 R10 B11 G11
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;
}

     
 确实是和饱和度有关的,那样敞亮普通话的翻译反而倒是合理,那么只好怪Adobe的开发者为啥给那些意义起个名字叫Vibrance了。

     

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

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

 

   来个速度比较:

     
以上是拍卖的率先步,看上去那个代码很多,实际上他们的履行时那多少个快的,三千*3000的图这么些拆分和统一进度也就大概2ms。

     
首先,三次性加载五十个图像数据到内部存款和储蓄器,正好放置在多少个__m128i变量中,同时此外1个很好的作业正是48恰好能被3整除,也等于说大家全部的加载了15个二十多少人像素,那样就不会并发断层,只代表下边47个像素能够和现行反革命的44个像素使用相同的措施进行拍卖。

 

  借使我们须要展开在int范围内实行测算,则还需进一步扩张,此时能够动用_mm_unpackhi_epi16/_mm_unpacklo_epi16合营zero继续开始展览扩大,那样3个Blue8变量须要伍个__m128i
int范围的多少来表述。

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

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

     
Adjustment大家早就将他限定在了[-128,128]里面,而(马克斯 –
Avg)理论上的最大值为255 –
85=170,(即奥迪Q5GB分量有八个是255,其余的都为0),最小值为0,因而,两者在各自范围内的成绩不会当先short所能表明的范围,而(马克斯-Blue)的最大值为255,最小值为0,在乘以4也在short类型所能表明的限制内。所以,下一步你们懂了吧?

 

  对应的SSE代码为:

      float AmtVal = (Max - Avg) * VibranceAdjustment;

   
 为了完毕这么些效应,笔者参考了采石工铁汉的关于代码,分享如下:

   
 处理完后大家又要把她们复苏到原始的BGCRUISER布局。

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

  • 格林 + 格林 +
    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);
    
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

 

 

  Vibrance那个单词搜索翻译一般震荡,抖动可能是嘹亮、活力,可是官方的词汇里还向来未出现过自然饱和度这几个词,也不精晓当时的Adobe汉译职员怎么会如此处理。可是大家看看PS对那么些效能的分解:

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

   在看代码的下一句:

     
 必发365乐趣网投手机版 1   
 必发365乐趣网投手机版 2   
 必发365乐趣网投手机版 3

  最终一步正是将这几个十陆位的数量再度转移为五人的,注意原始代码中有Clamp操作,那些操作实际是个耗费时间的经过,而SSE天然的具备抗饱和的函数。

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 0 B2 0 B3 0 B3 0 B4 0 B5 0 B6 0 B7 0

 

   最后优化速度:5ms。

  中间八个Green相加是用运动依然一向相加对速度没啥影响的。

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

   
 不难的敞亮shuffle指令,正是将__m128i变量内的一一数据依照钦点的一一进行重复布置,当然那一个布阵不必然要统统选用本来的数码,也得以再度有个别数据,或然有个别地点无数据,比如在实践上边那条指令

结论:

  这些的结果和PS的是比较相近的,最起码趋势是相当接近的,可是细节依旧分裂,不过能够看清的是可行性是对的,要是您早晚要复制PS的结果,笔者建议你花点时间变更个中的一部分常数可能总结形式看看。应该能有得到,国内已经有人摸索出来了。

 

  其中

   很有意思的操作,比如_mm_unpacklo_epi8是将八个__m128i的低七人交错陈设形成二个新的126位数据,假设内部三个参数为0,则正是把此外二个参数的低7个字节无损的扩张为拾陆人了,以上述BL16为例,在那之中间布局为:

  那样优化后,同样大小的图像算法用时35微秒,效果和浮点版本的基本没啥差异。

必发365乐趣网投手机版 4

       注意大家的这些表明式:

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

  如若知道了由BGRBGRBG路虎极光—》变为了BBBGGG本田CR-V君越瑞鹰那样的情势的规律后,那么由BBBGGGEnclaveSportage帕杰罗–>变为BGRBGRBG福特Explorer的道理就特别浅显了,这里不赘述,间接贴出代码:

必发365乐趣网投手机版, 
 能够观望进展上述操作后Blue8的签陆个字节已经符合大家的供给了。

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

  此时总括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
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)));

 

     
 第②大家把/127改为/128,那基本不影响效应,同时Adjustment暗中同意的限定为[-100,100],把它也线性扩张学一年级点,比如扩充1.28倍,扩张到[-128,128],那样在最终大家一遍性移位,裁减中间的损失,大致的代码如下:

     
 接下来的优化则是本例的1个特点部分了。大家来详细分析。

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

  
对于那种单像素点、和天地非亲非故的图像算法,为了能应用SSE升高程序速度,1个宗旨的手续正是把各颜色分量分离为独立的连天的变量,对于2二个人图像,大家了解图像在内部存款和储蓄器中的布局为:

 

       简单的剖析了本来饱和度算法的落实,分享了其SSE完成的进程,对于这几个刚刚接触SSE,想做图像处理的心上人有肯定的帮扶。

 在那之中的叙说和PS官方文书档案的讲述有类似之处。

   
   首先,大家将他恢弘为移动十四人的结果,变为如下:

     
当然由于字节数据类型的抒发范围11分不难,除了少有的多少个简单的操作能针对字节类型直接处理外,比如本例的丘揽胜GB的马克斯值,就足以直接用上边包车型大巴SIMD指令实现:

    那是浮点版本的简练优化,借使不勾选编写翻译器的SSE优化,直接使用FPU,对于一副三千*3000的22位图像耗费时间在I5的一台机械上运转用时差不离70微秒,但那不是首要。

      Src第22中学保存着:

       

    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_shuffle_epi8(Src2,
_mm_setr_epi8(-1, -1, -1, -1, -1, -1, 2, 5, 8, 11, 14, -1, -1, -1,
-1, -1))获得的近年来数据为:

  接着分析,由于代码中有((马克斯 –
Blue) * AmtVal) >> 14,其中AmtVal
= (Max – Avg) * Adjustment,展开即为:  ((马克斯 – Blue) * (Max – Avg) *
Adjustment)>>14;那八个数据相乘一点都不小程度上会超出short所能表明的限制,由此,大家还索要对上边的1六个人数据开展扩充,扩充到3一位,那样就多了不少发令,那么有没有不要求扩充的法门呢。经过一番盘算,笔者建议了下述化解方案:

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

 

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

     
 咱们通晓,SSE对于跳转是很不自个儿的,他万分擅长种类化处理一个事务,尽管他提供了许多比较指令,可是众多气象下复杂的跳转SSE还是无论为力,对于本例,境况相比特殊,假使要使用SSE的对比指令也是足以一向促成的,达成的办法时,使用相比较指令获得三个Mask,Mask中符合相比结实的值会为FFFFFFFF,不合乎的为0,然后把那个Mask和前面供给总括的某部值进行And操作,由于和FFFFFFFF进行And操作不会改变操作数自身,和0进行And操作则变为0,在诸多气象下,就是随便你符合条件与否,都开展末端的持筹握算,只是不符合条件的一个钱打二17个结不会潜移默化结果,那种总计只怕会失效SSE优化的有些提速效果,这些将要具体情状具体分析了。

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

 

     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

 

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

 

      float VibranceAdjustment = -0.01 * Adjustment / 127.0f;

     
 VB的语法有些人只怕面生,笔者稍稍做点更改翻译成C的代码如下:

 

 

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

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

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

   Blue第88中学的数额为:

   
 再度和从前的Blue8结果开始展览或操作获得最后的结果:

   
 对于格林和Red分量,处理的点子和步子是平等的,只是出于地点分化,每一次实行shuffle操作的常数有所不相同,但原理完全一致。

 

  Src3中的数据则为:

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

发表评论

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

网站地图xml地图