• <label id="pxtpz"><meter id="pxtpz"></meter></label>
      1. <span id="pxtpz"><optgroup id="pxtpz"></optgroup></span>

        當(dāng)前位置:雨林木風(fēng)下載站 > 圖形圖像教程 > 詳細(xì)頁(yè)面

        PhotoShop算法原理解析系列 -  風(fēng)格化-搜索邊緣。

        PhotoShop算法原理解析系列 - 風(fēng)格化-搜索邊緣。

        更新時(shí)間:2025-09-20 文章作者:未知 信息來(lái)源:網(wǎng)絡(luò) 閱讀次數(shù):

        導(dǎo)Adobe Photoshop,簡(jiǎn)稱(chēng)“PS”,是由Adobe Systems開(kāi)發(fā)和發(fā)行的圖像處理軟件。Photoshop主要處理以像素所構(gòu)成的數(shù)字圖像。使用其眾多的編修與繪圖工具,可以有效地進(jìn)行圖片...
        Adobe Photoshop,簡(jiǎn)稱(chēng)“PS”,是由Adobe Systems開(kāi)發(fā)和發(fā)行的圖像處理軟件。Photoshop主要處理以像素所構(gòu)成的數(shù)字圖像。使用其眾多的編修與繪圖工具,可以有效地進(jìn)行圖片編輯工作。ps有很多功能,在圖像、圖形、文字、視頻、出版等各方面都有涉及。
        之所以不寫(xiě)系列文章一、系列文章二這樣的標(biāo)題,是因?yàn)槲也恢牢夷軋?jiān)持多久。我知道我對(duì)事情的表達(dá)能力和語(yǔ)言的豐富性方面的天賦不高。而一段代碼需要我去用心的把他從基本原理--》初步實(shí)現(xiàn)--》優(yōu)化速度 等過(guò)程用文字的方式表述清楚,恐怕不是一件很容易的事情。

        我所掌握的一些Photoshop中的算法,不能說(shuō)百分之一百就是正確的,但是從執(zhí)行的效果中,大的方向肯定是沒(méi)有問(wèn)題的。

        目前,從別人的文章、開(kāi)源的代碼以及自己的思考中我掌握的PS的算法可能有近100個(gè)吧。如果時(shí)間容許、自身的耐心容許,我會(huì)將這些東西慢慢的整理開(kāi)來(lái),雖然在很多人看來(lái),這些算法并不具有什么研究的價(jià)值了,畢竟人家都已經(jīng)商業(yè)化了。說(shuō)的也有道理,我姑且把他作為自我欣賞和自我滿(mǎn)足的一種方式吧。

        今天,我們講講查找邊緣算法。可能我說(shuō)了原理,很多人就不會(huì)看下去了,可有幾人層仔細(xì)的研究過(guò)呢。

          先貼個(gè)效果圖吧:

        PhotoShop算法原理解析系列 -  風(fēng)格化---》查找邊緣。

          原理:常見(jiàn)的Sobel邊緣算子的結(jié)果進(jìn)行反色即可

        為了能吸引你繼續(xù)看下去,我先給出我的代碼的執(zhí)行速度: 針對(duì)3000*4000*3的數(shù)碼圖片,處理時(shí)間300ms

        何為Sobel,從百度抄幾張圖過(guò)來(lái)了并修改地址后:

        PhotoShop算法原理解析系列 -  風(fēng)格化---》查找邊緣。

          對(duì)上面兩個(gè)式子不做過(guò)多解釋?zhuān)阒恍枰榔渲蠥為輸入圖像,把G作為A的輸出圖像就可以了,最后還要做一步: G=255-G,就是查找邊緣算法。

        查找邊緣類(lèi)算法都有個(gè)問(wèn)題,對(duì)圖像物理邊緣處的像素如何處理,在平日的處理代碼中,很多人就是忽略四個(gè)邊緣的像素,作為專(zhuān)業(yè)的圖像處理軟件,這可是違反最基本的原則的。對(duì)邊緣進(jìn)行的單獨(dú)的代碼處理,又會(huì)給編碼帶來(lái)冗余和繁瑣的問(wèn)題。解決問(wèn)題的最簡(jiǎn)單又高效的方式就是采用哨兵邊界。

        寫(xiě)多了特效類(lèi)算法的都應(yīng)該知道,除了那種對(duì)單個(gè)像素進(jìn)行處理的算法不需要對(duì)原始圖像做個(gè)備份(不一定去全局備份),那些需要領(lǐng)域信息的算法由于算法的前一步修改了一個(gè)像素,而算法的當(dāng)前步需要未修改的像素值,因此,一般這種算法都會(huì)在開(kāi)始前對(duì)原始圖像做個(gè)克隆,在計(jì)算時(shí),需要的領(lǐng)域信息從克隆的數(shù)據(jù)中讀取。如果這個(gè)克隆的過(guò)程不是完完全全的克隆,而是擴(kuò)展適當(dāng)邊界后再克隆,就有可能解決上述的邊界處理問(wèn)題。

          比如對(duì)下面的一個(gè)圖,19×14像素大小,我們的備份圖為上下左右各擴(kuò)展一個(gè)像素的大小,并用邊緣的值填充,變?yōu)?1*16大小:

        PhotoShop算法原理解析系列 -  風(fēng)格化---》查找邊緣。 PhotoShop算法原理解析系列 -  風(fēng)格化---》查找邊緣。

          這樣,在計(jì)算原圖的3*3領(lǐng)域像素時(shí),從擴(kuò)展后的克隆圖對(duì)應(yīng)點(diǎn)取樣,就不會(huì)出現(xiàn)不在圖像范圍內(nèi)的問(wèn)題了,編碼中即可以少很多判斷,可讀性也加強(qiáng)了。

        在計(jì)算速度方面,注意到上面的計(jì)算式G中有個(gè)開(kāi)方運(yùn)算,這是個(gè)耗時(shí)的過(guò)程,由于圖像數(shù)據(jù)的特殊性,都必須是整數(shù),可以采用查找表的方式優(yōu)化速度,這就需要考慮表的建立。

        針對(duì)本文的具體問(wèn)題,我們分兩步討論,第一:針對(duì)根號(hào)下的所有可能情況建立查找表。看看GX和GY的計(jì)算公式,考慮下兩者的平方和的最大值是多少,可能要考慮一會(huì)吧。第二:就是只建立0^2到255^2范圍內(nèi)的查找表,然后確保根號(hào)下的數(shù)字不大于255^2。為什么可以這樣做,就是因?yàn)閳D像數(shù)據(jù)的最大值就是255,如果根號(hào)下的數(shù)字大于255^2,在求出開(kāi)方值后,還是需要規(guī)整為255的。因此,本算法中應(yīng)該取后者。

        private void CmdFindEdgesArray_Click(object sender, EventArgs e)
        {    int X, Y;    int Width, Height, Stride, StrideC, HeightC;    int Speed, SpeedOne, SpeedTwo, SpeedThree;    int BlueOne, BlueTwo, GreenOne, GreenTwo, RedOne, RedTwo;    int PowerRed, PowerGreen, PowerBlue;
            Bitmap Bmp = (Bitmap)Pic.Image;    if (Bmp.PixelFormat != PixelFormat.Format24bppRgb) throw new Exception("不支持的圖像格式.");    byte[] SqrValue = new byte[65026];    for (Y = 0; Y < 65026; Y++) SqrValue[Y] = (byte)(255 - (int)Math.Sqrt(Y));      // 計(jì)算查找表,注意已經(jīng)砸查找表里進(jìn)行了反色
            Width = Bmp.Width; Height = Bmp.Height; Stride = (int)((Bmp.Width * 3 + 3) & 0XFFFFFFFC);
            StrideC = (Width + 2) * 3; HeightC = Height + 2;                                 // 寬度和高度都擴(kuò)展2個(gè)像素
        
            byte[] ImageData = new byte[Stride * Height];                                    // 用于保存圖像數(shù)據(jù),(處理前后的都為他)
            byte[] ImageDataC = new byte[StrideC * HeightC];                                // 用于保存擴(kuò)展后的圖像數(shù)據(jù)
        
            fixed (byte* Scan0 = &ImageData[0])
            {
                BitmapData BmpData = new BitmapData();
                BmpData.Scan0 = (IntPtr)Scan0;                                              //  設(shè)置為字節(jié)數(shù)組的的第一個(gè)元素在內(nèi)存中的地址
                BmpData.Stride = Stride;
                Bmp.LockBits(new Rectangle(0, 0, Bmp.Width, Bmp.Height), ImageLockMode.ReadWrite | ImageLockMode.UserInputBuffer, PixelFormat.Format24bppRgb, BmpData);
        
                Stopwatch Sw = new Stopwatch();                                             //  只獲取計(jì)算用時(shí)        Sw.Start();        for (Y = 0; Y < Height; Y++)
                {
                    System.Buffer.BlockCopy(ImageData, Stride * Y, ImageDataC, StrideC * (Y + 1), 3);        // 填充擴(kuò)展圖的左側(cè)第一列像素(不包括第一個(gè)和最后一個(gè)點(diǎn))
                    System.Buffer.BlockCopy(ImageData, Stride * Y + (Width - 1) * 3, ImageDataC, StrideC * (Y + 1) + (Width + 1) * 3, 3);  // 填充最右側(cè)那一列的數(shù)據(jù)
                    System.Buffer.BlockCopy(ImageData, Stride * Y, ImageDataC, StrideC * (Y + 1) + 3, Width * 3);
                }
                System.Buffer.BlockCopy(ImageDataC, StrideC, ImageDataC, 0, StrideC);              //  第一行
                System.Buffer.BlockCopy(ImageDataC, (HeightC - 2) * StrideC, ImageDataC, (HeightC - 1) * StrideC, StrideC);    //  最后一行               
        
                for (Y = 0; Y < Height; Y++)
                {
                    Speed = Y * Stride;
                    SpeedOne = StrideC * Y;            for (X = 0; X < Width; X++)
                    {
                        SpeedTwo = SpeedOne + StrideC;          //  盡量減少計(jì)算
                        SpeedThree = SpeedTwo + StrideC;        //  下面的就是嚴(yán)格的按照Sobel算字進(jìn)行計(jì)算,代碼中的*2一般會(huì)優(yōu)化為移位或者兩個(gè)Add指令的,如果你不放心,當(dāng)然可以直接改成移位
                        BlueOne = ImageDataC[SpeedOne] + 2 * ImageDataC[SpeedTwo] + ImageDataC[SpeedThree] - ImageDataC[SpeedOne + 6] - 2 * ImageDataC[SpeedTwo + 6] - ImageDataC[SpeedThree + 6];
                        GreenOne = ImageDataC[SpeedOne + 1] + 2 * ImageDataC[SpeedTwo + 1] + ImageDataC[SpeedThree + 1] - ImageDataC[SpeedOne + 7] - 2 * ImageDataC[SpeedTwo + 7] - ImageDataC[SpeedThree + 7];
                        RedOne = ImageDataC[SpeedOne + 2] + 2 * ImageDataC[SpeedTwo + 2] + ImageDataC[SpeedThree + 2] - ImageDataC[SpeedOne + 8] - 2 * ImageDataC[SpeedTwo + 8] - ImageDataC[SpeedThree + 8];
                        BlueTwo = ImageDataC[SpeedOne] + 2 * ImageDataC[SpeedOne + 3] + ImageDataC[SpeedOne + 6] - ImageDataC[SpeedThree] - 2 * ImageDataC[SpeedThree + 3] - ImageDataC[SpeedThree + 6];
                        GreenTwo = ImageDataC[SpeedOne + 1] + 2 * ImageDataC[SpeedOne + 4] + ImageDataC[SpeedOne + 7] - ImageDataC[SpeedThree + 1] - 2 * ImageDataC[SpeedThree + 4] - ImageDataC[SpeedThree + 7];
                        RedTwo = ImageDataC[SpeedOne + 2] + 2 * ImageDataC[SpeedOne + 5] + ImageDataC[SpeedOne + 8] - ImageDataC[SpeedThree + 2] - 2 * ImageDataC[SpeedThree + 5] - ImageDataC[SpeedThree + 8];
        
                        PowerBlue = BlueOne * BlueOne + BlueTwo * BlueTwo;
                        PowerGreen = GreenOne * GreenOne + GreenTwo * GreenTwo;
                        PowerRed = RedOne * RedOne + RedTwo * RedTwo;                if (PowerBlue > 65025) PowerBlue = 65025;           //  處理掉溢出值
                        if (PowerGreen > 65025) PowerGreen = 65025;                if (PowerRed > 65025) PowerRed = 65025;
                        ImageData[Speed] = SqrValue[PowerBlue];             //  查表
                        ImageData[Speed + 1] = SqrValue[PowerGreen];
                        ImageData[Speed + 2] = SqrValue[PowerRed];
        
                        Speed += 3;                                  // 跳往下一個(gè)像素
                        SpeedOne += 3;
                    }
                }
                Sw.Stop();        this.Text = "計(jì)算用時(shí): " + Sw.ElapsedMilliseconds.ToString() + " ms";
        
                Bmp.UnlockBits(BmpData);                         //  必須先解鎖,否則Invalidate失敗     }
            Pic.Invalidate();
        }

          為簡(jiǎn)單的起見(jiàn),這里先是用的C#的一維數(shù)組實(shí)現(xiàn)的,并且計(jì)時(shí)部分未考慮圖像數(shù)據(jù)的獲取和更新, 因?yàn)檎嬲膱D像處理過(guò)程中圖像數(shù)據(jù)肯定是已經(jīng)獲得的了。

        針對(duì)上述代碼,編譯為Release模式后,執(zhí)行編譯后的EXE,對(duì)于3000*4000*3的彩色圖像,耗時(shí)約480ms,如果你是在IDE的模式先運(yùn)行,記得一定要在選項(xiàng)--》調(diào)試--》常規(guī)里不勾選 在模塊加載時(shí)取消JIT優(yōu)化(僅限托管)一欄。

        PhotoShop算法原理解析系列 -  風(fēng)格化---》查找邊緣。

        上述代碼中的填充克隆圖數(shù)據(jù)時(shí)并沒(méi)有新建一副圖,然后再填充其中的圖像數(shù)據(jù),而是直接填充一個(gè)數(shù)組,圖像其實(shí)不就是一片連續(xù)內(nèi)存加一點(diǎn)頭信息嗎,頭信息已經(jīng)有了,所以只要一片內(nèi)存就夠了。

        克隆數(shù)據(jù)的填充采用了系統(tǒng)Buffer.BlockCopy函數(shù),該函數(shù)類(lèi)似于我們以前常用CopyMemory,速度非常快。

        為進(jìn)一步調(diào)高執(zhí)行速度,我們首先來(lái)看看算法的關(guān)鍵耗時(shí)部位的代碼,即for (X = 0; X < Width; X++)內(nèi)部的代碼,我們?nèi)∫恍写a的反編譯碼來(lái)看看:

         BlueOne = ImageDataC[SpeedOne] + 2 * ImageDataC[SpeedTwo] + ImageDataC[SpeedThree] - ImageDataC[SpeedOne + 6] - 2 * ImageDataC[SpeedTwo + 6] - ImageDataC[SpeedThree + 6];

        00000302 cmp ebx,edi 00000304 jae 0000073C             // 數(shù)組是否越界? 0000030a movzx eax,byte ptr [esi+ebx+8]    //  將ImageDataC[SpeedOne]中的數(shù)據(jù)傳送的eax寄存器 0000030f mov dword ptr [ebp-80h],eax 00000312 mov edx,dword ptr [ebp-2Ch] 00000315 cmp edx,edi 00000317 jae 0000073C            // 數(shù)組是否越界?            0000031d movzx edx,byte ptr [esi+edx+8]   //   將ImageDataC[SpeedTwo]中的數(shù)據(jù)傳送到edx寄存器00000322 add edx,edx             // 計(jì)算2*ImageDataC[SpeedTwo]    00000324 add eax,edx             // 計(jì)算ImageDataC[SpeedOne]+2*ImageDataC[SpeedTwo],并保存在eax寄存器中           00000326 cmp ecx,edi 00000328 jae 0000073C 0000032e movzx edx,byte ptr [esi+ecx+8]   // 將ImageDataC[SpeedThree]中的數(shù)據(jù)傳送到edx寄存器00000333 mov dword ptr [ebp+FFFFFF78h],edx 00000339 add eax,edx 0000033b lea edx,[ebx+6] 0000033e cmp edx,edi 00000340 jae 0000073C 00000346 movzx edx,byte ptr [esi+edx+8] 0000034b mov dword ptr [ebp+FFFFFF7Ch],edx 00000351 sub eax,edx 00000353 mov edx,dword ptr [ebp-2Ch] 00000356 add edx,6 00000359 cmp edx,edi 0000035b jae 0000073C 00000361 movzx edx,byte ptr [esi+edx+8] 00000366 add edx,edx 00000368 sub eax,edx 0000036a lea edx,[ecx+6] 0000036d cmp edx,edi 0000036f jae 0000073C 00000375 movzx edx,byte ptr [esi+edx+8] 0000037a mov dword ptr [ebp+FFFFFF74h],edx 00000380 sub eax,edx 00000382 mov dword ptr [ebp-30h],eax

           上述匯編碼我只注釋一點(diǎn)點(diǎn),其中最0000073c 標(biāo)號(hào),我們跟蹤后返現(xiàn)是調(diào)用了另外一個(gè)函數(shù):

        0000073c call 685172A4

        我們看到在獲取每一個(gè)數(shù)組元素前,都必須執(zhí)行一個(gè)cmp 和 jae指令,從分析我認(rèn)為這里是做類(lèi)似于判斷數(shù)組的下標(biāo)是否越界之類(lèi)的工作的。如果我們能確保我們的算法那不會(huì)產(chǎn)生越界,這部分代碼有很用呢,不是耽誤我做正事嗎。

        為此,我認(rèn)為需要在C#中直接利用指針來(lái)實(shí)現(xiàn)算法,C#中有unsafe模式,也有指針,所以很方便,而且指針的表達(dá)即可以用*,也可以用[],比如*(P+4) 和P[4]是一個(gè)意思。那么只要做很少的修改就可以將上述代碼修改為指針版。

        private void CmdFindEdgesPointer_Click(object sender, EventArgs e)
            {        int X, Y;        int Width, Height, Stride, StrideC, HeightC;        int Speed, SpeedOne, SpeedTwo, SpeedThree;        int BlueOne, BlueTwo, GreenOne, GreenTwo, RedOne, RedTwo;        int PowerRed, PowerGreen, PowerBlue;
                Bitmap Bmp = (Bitmap)Pic.Image;        if (Bmp.PixelFormat != PixelFormat.Format24bppRgb) throw new Exception("不支持的圖像格式.");        byte[] SqrValue = new byte[65026];        for (Y = 0; Y < 65026; Y++) SqrValue[Y] = (byte)(255 - (int)Math.Sqrt(Y));      // 計(jì)算查找表,注意已經(jīng)砸查找表里進(jìn)行了反色
                Width = Bmp.Width; Height = Bmp.Height; Stride = (int)((Bmp.Width * 3 + 3) & 0XFFFFFFFC);
                StrideC = (Width + 2) * 3; HeightC = Height + 2;                                 // 寬度和高度都擴(kuò)展2個(gè)像素
        
                byte[] ImageData = new byte[Stride * Height];                                    // 用于保存圖像數(shù)據(jù),(處理前后的都為他)
                byte[] ImageDataC = new byte[StrideC * HeightC];                                 // 用于保存擴(kuò)展后的圖像數(shù)據(jù)
        
                fixed (byte* P = &ImageData[0], CP = &ImageDataC[0], LP = &SqrValue[0])
                {            byte* DataP = P, DataCP = CP, LutP = LP;
                    BitmapData BmpData = new BitmapData();
                    BmpData.Scan0 = (IntPtr)DataP;                                              //  設(shè)置為字節(jié)數(shù)組的的第一個(gè)元素在內(nèi)存中的地址
                    BmpData.Stride = Stride;
                    Bmp.LockBits(new Rectangle(0, 0, Bmp.Width, Bmp.Height), ImageLockMode.ReadWrite | ImageLockMode.UserInputBuffer, PixelFormat.Format24bppRgb, BmpData);
        
                    Stopwatch Sw = new Stopwatch();                                             //  只獲取計(jì)算用時(shí)            Sw.Start();            for (Y = 0; Y < Height; Y++)
                    {
                        System.Buffer.BlockCopy(ImageData, Stride * Y, ImageDataC, StrideC * (Y + 1), 3);        // 填充擴(kuò)展圖的左側(cè)第一列像素(不包括第一個(gè)和最后一個(gè)點(diǎn))
                        System.Buffer.BlockCopy(ImageData, Stride * Y + (Width - 1) * 3, ImageDataC, StrideC * (Y + 1) + (Width + 1) * 3, 3);  // 填充最右側(cè)那一列的數(shù)據(jù)
                        System.Buffer.BlockCopy(ImageData, Stride * Y, ImageDataC, StrideC * (Y + 1) + 3, Width * 3);
                    }
                    System.Buffer.BlockCopy(ImageDataC, StrideC, ImageDataC, 0, StrideC);              //  第一行
                    System.Buffer.BlockCopy(ImageDataC, (HeightC - 2) * StrideC, ImageDataC, (HeightC - 1) * StrideC, StrideC);    //  最后一行               
        
                    for (Y = 0; Y < Height; Y++)
                    {
                        Speed = Y * Stride;
                        SpeedOne = StrideC * Y;                for (X = 0; X < Width; X++)
                        {
                            SpeedTwo = SpeedOne + StrideC;          //  盡量減少計(jì)算
                            SpeedThree = SpeedTwo + StrideC;        //  下面的就是嚴(yán)格的按照Sobel算字進(jìn)行計(jì)算,代碼中的*2一般會(huì)優(yōu)化為移位或者兩個(gè)Add指令的,如果你不放心,當(dāng)然可以直接改成移位
                            BlueOne = DataCP[SpeedOne] + 2 * DataCP[SpeedTwo] + DataCP[SpeedThree] - DataCP[SpeedOne + 6] - 2 * DataCP[SpeedTwo + 6] - DataCP[SpeedThree + 6];
                            GreenOne = DataCP[SpeedOne + 1] + 2 * DataCP[SpeedTwo + 1] + DataCP[SpeedThree + 1] - DataCP[SpeedOne + 7] - 2 * DataCP[SpeedTwo + 7] - DataCP[SpeedThree + 7];
                            RedOne = DataCP[SpeedOne + 2] + 2 * DataCP[SpeedTwo + 2] + DataCP[SpeedThree + 2] - DataCP[SpeedOne + 8] - 2 * DataCP[SpeedTwo + 8] - DataCP[SpeedThree + 8];
                            BlueTwo = DataCP[SpeedOne] + 2 * DataCP[SpeedOne + 3] + DataCP[SpeedOne + 6] - DataCP[SpeedThree] - 2 * DataCP[SpeedThree + 3] - DataCP[SpeedThree + 6];
                            GreenTwo = DataCP[SpeedOne + 1] + 2 * DataCP[SpeedOne + 4] + DataCP[SpeedOne + 7] - DataCP[SpeedThree + 1] - 2 * DataCP[SpeedThree + 4] - DataCP[SpeedThree + 7];
                            RedTwo = DataCP[SpeedOne + 2] + 2 * DataCP[SpeedOne + 5] + DataCP[SpeedOne + 8] - DataCP[SpeedThree + 2] - 2 * DataCP[SpeedThree + 5] - DataCP[SpeedThree + 8];
        
                            PowerBlue = BlueOne * BlueOne + BlueTwo * BlueTwo;
                            PowerGreen = GreenOne * GreenOne + GreenTwo * GreenTwo;
                            PowerRed = RedOne * RedOne + RedTwo * RedTwo;                    if (PowerBlue > 65025) PowerBlue = 65025;           //  處理掉溢出值
                            if (PowerGreen > 65025) PowerGreen = 65025;                    if (PowerRed > 65025) PowerRed = 65025;
        
                            DataP[Speed] = LutP[PowerBlue];                     //  查表
                            DataP[Speed + 1] = LutP[PowerGreen];
                            DataP[Speed + 2] = LutP[PowerRed];
        
                            Speed += 3;                                         //  跳往下一個(gè)像素
                            SpeedOne += 3;
                        }
                    }
                    Sw.Stop();            this.Text = "計(jì)算用時(shí): " + Sw.ElapsedMilliseconds.ToString() + " ms";
        
                    Bmp.UnlockBits(BmpData);                         //  必須先解鎖,否則Invalidate失敗         }
                Pic.Invalidate();
            }

        同樣的效果,同樣的圖像,計(jì)算用時(shí)330ms。

        我們?cè)趤?lái)看看相同代碼的匯編碼:

        BlueOne = DataCP[SpeedOne] + 2 * DataCP[SpeedTwo] + DataCP[SpeedThree] - DataCP[SpeedOne + 6] - 2 * DataCP[SpeedTwo + 6] - DataCP[SpeedThree + 6];

        00000318 movzx eax,byte ptr [esi+edi] 0000031c mov dword ptr [ebp-74h],eax 0000031f movzx edx,byte ptr [esi+ebx] 00000323 add edx,edx 00000325 add eax,edx 00000327 movzx edx,byte ptr [esi+ecx] 0000032b mov dword ptr [ebp-7Ch],edx 0000032e add eax,edx 00000330 movzx edx,byte ptr [esi+edi+6] 00000335 mov dword ptr [ebp-78h],edx 00000338 sub eax,edx 0000033a movzx edx,byte ptr [esi+ebx+6] 0000033f add edx,edx 00000341 sub eax,edx 00000343 movzx edx,byte ptr [esi+ecx+6] 00000348 mov dword ptr [ebp-80h],edx 0000034b sub eax,edx 0000034d mov dword ptr [ebp-30h],eax

        生產(chǎn)的匯編碼簡(jiǎn)潔,意義明確,對(duì)比下少了很多指令。當(dāng)然速度會(huì)快很多。

        注意這一段代碼:

          fixed (byte* P = &ImageData[0], CP = &ImageDataC[0], LP = &SqrValue[0])
                {            byte* DataP = P, DataCP = CP, LutP = LP;

        如果你把更換為:

          fixed (byte* DataP = &ImageData[0], DataCP = &ImageDataC[0], LutP = &SqrValue[0])
        {

        代碼的速度反而比純數(shù)組版的還慢,至于為什么,實(shí)踐為王吧,我也沒(méi)有去分析,反正我知道有這個(gè)結(jié)果。你可以參考鐵哥的一篇文章:

        閑談.Net類(lèi)型之public的不public,fixed的不能fixed

        當(dāng)然這個(gè)還可以進(jìn)一步做小動(dòng)作的的優(yōu)化,比如movzx eax,byte ptr [esi+edi] 這句中,esi其實(shí)就是數(shù)組的基地址,向這樣寫(xiě)DataCP[SpeedOne] ,每次都會(huì)有這個(gè)基址+偏移的計(jì)算的,如果能實(shí)時(shí)直接動(dòng)態(tài)控制一個(gè)指針變量,使他直接指向索要的位置,則少了一次加法,雖然優(yōu)化不是很明顯,基本可以達(dá)到問(wèn)中之前所提到的300ms的時(shí)間了。具體的代碼可見(jiàn)附件。

        很多人可能對(duì)我這些東西不感冒,說(shuō)這些東西丟給GPU比你現(xiàn)在的.......希望這些朋友也不要過(guò)分的打擊吧,每個(gè)人都有自己的愛(ài)好,我只愛(ài)好CPU。

        更多PhotoShop算法原理解析系列 - 風(fēng)格化-查找邊緣。相關(guān)文章請(qǐng)關(guān)注PHP中文網(wǎng)!



        Photoshop默認(rèn)保存的文件格式,可以保留所有有圖層、色版、通道、蒙版、路徑、未柵格化文字以及圖層樣式等。

        溫馨提示:喜歡本站的話(huà),請(qǐng)收藏一下本站!

        本類(lèi)教程下載

        系統(tǒng)下載排行

        主站蜘蛛池模板: 亚洲中文无码线在线观看| 亚洲一区二区在线免费观看| 色欲色香天天天综合网站免费 | 91视频免费网站| 黄色网址免费在线| 亚洲av永久中文无码精品综合| 亚洲国产中文在线二区三区免| 久久精品国产亚洲AV高清热| 亚洲精品中文字幕麻豆| 亚洲精品亚洲人成在线麻豆| 亚洲一线产区二线产区精华| 亚洲AV成人无码天堂| 亚洲国产精品18久久久久久 | 久久久久亚洲AV成人网| 亚洲人成图片小说网站| 婷婷精品国产亚洲AV麻豆不片| 亚洲一区二区三区自拍公司| 亚洲伦另类中文字幕| 亚洲AV综合色区无码二区爱AV| 亚洲入口无毒网址你懂的| 日本一区二区三区在线视频观看免费 | 久久久影院亚洲精品| 亚洲国产成人久久77| 亚洲A∨精品一区二区三区下载| 免费无码又爽又黄又刺激网站| 你懂得的在线观看免费视频| 99爱在线精品免费观看| 亚洲国产精品成人一区| 亚洲成年人电影网站| 一级毛片在线免费视频| 18pao国产成视频永久免费| 国产成人免费全部网站| 亚洲精品国产福利在线观看| 九九全国免费视频| 亚洲欧洲免费无码| 久久亚洲春色中文字幕久久久| 看免费毛片天天看| 国产免费久久精品99re丫y| 亚洲av无码片在线播放| 亚洲国产美女精品久久久| 91九色视频无限观看免费|