Maison > Article > interface Web > Série d'analyses des principes de l'algorithme PhotoShop - stylisation - recherche des bords.
La raison pour laquelle je n'écris pas de titres comme la série 1 et la série 2 est parce que je ne sais pas combien de temps je peux persister. Je sais que je ne suis pas très doué en termes de capacité d'expression et de richesse du langage. Et un morceau de code m'oblige à décrire soigneusement le processus à partir des principes de base - "mise en œuvre préliminaire -", vitesse d'optimisation et autres processus avec des mots, ce qui n'est probablement pas une tâche facile.
Je ne peux pas dire que certains des algorithmes de Photoshop que je maîtrise sont corrects à 100%, mais du point de vue de l'effet d'exécution, il n'y a certainement aucun problème dans le sens général.
À l'heure actuelle, j'ai probablement appris près de 100 algorithmes PS à partir d'articles d'autres personnes, de codes open source et de ma propre réflexion. Si le temps le permet et si ma patience le permet, je réglerai lentement ces problèmes. Même si aux yeux de beaucoup de gens, ces algorithmes n'ont aucune valeur pour la recherche. Après tout, ils ont été commercialisés. C’est logique, permettez-moi simplement de l’utiliser comme un moyen d’appréciation de soi et d’autosatisfaction.
Aujourd'hui, nous parlons de l'algorithme de recherche de bord. Peut-être qu'après avoir expliqué le principe, beaucoup de gens ne le liraient pas, mais quelques personnes l'ont étudié attentivement.
Postons d'abord un rendu :
Principe : Les résultats des opérateurs de bord Sobel courants peuvent être inversés.
Afin de vous inciter à continuer la lecture, je vais d'abord donner la vitesse d'exécution de mon code : Pour l'image numérique de 3000*4000*3, le temps de traitement300ms.
Qu'est-ce que Sobel ? J'ai copié quelques photos de Baidu et modifié l'adresse :
Je ne le ferai pas expliquez trop sur les deux formules ci-dessus. Il vous suffit de savoir que A est l'image d'entrée et G est l'image de sortie de A. La dernière étape consiste à faire : G=255-G , qui est l'algorithme de recherche de contour.
Il y a un problème avec l'algorithme de recherche de contour. Comment traiter les pixels au
bord physiquede l'image Dans le code de traitement habituel, beaucoup de gens. ignorez simplement les quatre pixels Edge, en tant que logiciel de traitement d'image professionnel, violez les principes les plus élémentaires. Un traitement de code séparé pour les bords entraînera des problèmes de codage redondants et fastidieux. La manière la plus simple et la plus efficace de résoudre le problème consiste à utiliser des frontières sentinelles. Ceux qui ont écrit de nombreux algorithmes d'effets spéciaux doivent savoir qu'à l'exception de l'algorithme qui traite un seul pixel, il n'est pas nécessaire de faire une sauvegarde de l'image originale (pas nécessairement une sauvegarde globale sauvegarde), les algorithmes qui nécessitent des informations de domaine modifient un pixel dans l'étape précédente de l'algorithme, et l'étape actuelle de l'algorithme nécessite une valeur de pixel non modifiée. Par conséquent, cet algorithme clone généralement l'image d'origine avant de démarrer le calcul. Les informations de domaine sont lues à partir des données clonées. Si le processus de clonage n'est pas un clone complet, mais étend la limite appropriée puis la clone, il est possible de résoudre le problème de traitement des limites ci-dessus.
Par exemple, pour l'image ci-dessous, qui mesure 19 × 14 pixels, notre image de sauvegarde est agrandie d'un pixel en haut, en bas, à gauche et à droite, et remplie de les valeurs de bord deviendront 21*16 Taille :
De cette façon, lors du calcul des pixels de la zone 3*3 de l'image originale, l'échantillonnage à partir des points correspondants de l'image clone développée ne posera pas le problème de ne pas être dans la plage de l'image, et il y aura beaucoup moins de jugement dans l'encodage. La lisibilité est également améliorée. En termes de vitesse de calcul, notez qu'il y a une opération de racine carrée dans la formule de calcul G ci-dessus. C'est un processus qui prend du temps en raison de la particularité des données d'image, il doit s'agir d'un nombre entier. Vous pouvez optimiser la vitesse en utilisant une table de recherche, ce qui nécessite de considérer l'établissement de la table. Concernant les problématiques spécifiques de cet article, nous en discuterons en deux étapes : Premièrement : Créer une table de recherche pour toutes les situations possibles sous le signe racine. Jetez un œil aux formules de calcul de GX et GY et réfléchissez à la valeur maximale de la somme des carrés des deux. Vous voudrez peut-être y réfléchir un moment. Deuxièmement : créez simplement une table de recherche comprise entre 0^2 et 255^2, puis assurez-vous que le nombre sous le signe racine n'est pas supérieur à 255^2. Cela est possible parce que la valeur maximale des données d'image est de 255. Si le nombre sous le signe racine est supérieur à 255^2, il doit toujours être ajusté à 255 après avoir trouvé la valeur de la racine carrée. C’est donc ce dernier qui doit être choisi dans cet algorithme. Par souci de simplicité, ceci est d'abord implémenté en utilisant un tableau unidimensionnel en C#, et la partie timing ne prend pas en compte l'acquisition et la mise à jour des données d'image, car les données d'image doivent avoir été obtenues au cours du processus de traitement d'image réel. Pour le code ci-dessus, après avoir compilé en mode Release, exécutez l'EXE compilé pour une image couleur 3000*4000*3, cela prend environ 480 ms si vous êtes en mode IDE. et n'oubliez pas de décocher la colonne Annuler l'optimisation JIT (hébergement uniquement) lorsque le module est chargé dans Options--"Débogage--"Général. Il n'y a pas de nouvelle image lorsque les données sont remplies dans le code ci-dessus, puis y remplissent les données d'image, mais remplit directement un tableau. L'image n'est-elle pas en réalité juste un morceau de mémoire continue plus un peu d'informations d'en-tête ? Les informations d'en-tête sont déjà là, donc un seul morceau de mémoire suffit. Le remplissage des données de clonage utilise la fonction système Buffer.BlockCopy, qui est similaire à la CopyMemory que nous utilisions auparavant et est très rapide. Afin d'augmenter encore la vitesse d'exécution, nous examinons d'abord le code de la partie clé qui prend beaucoup de temps de l'algorithme, c'est-à-dire le code à l'intérieur de (X = 0; X < Largeur , regardons le code décompilé d'une ligne de code : I n'avons commenté qu'un petit peu le code assembleur ci-dessus, parmi lesquels La dernière étiquette 0000073c, nous avons suivi le retour et appelé une autre fonction : tout en obtenant each Avant de saisir un élément de tableau, un Les instructions cmp et jae doivent être exécutées. D'après l'analyse, je pense qu'il s'agit de faire quelque chose de similaire pour déterminer si l'indice du tableau est hors limites. Si nous pouvons garantir que notre algorithme ne franchira pas la frontière, cette partie du code sera très utile, cela ne me retardera-t-il pas dans mes affaires ? À cette fin, je pense qu'il est nécessaire d'utiliser directement des pointeurs pour implémenter des algorithmes en C# a un mode non sécurisé et des pointeurs, c'est donc très pratique, et les pointeurs peuvent être exprimés en utilisant. *, Vous pouvez également utiliser [], par exemple, *(P 4) et P[4] ont la même signification. Ensuite, le code ci-dessus peut être modifié en une version pointeur avec très peu de modifications. Même effet, même image, temps de calcul 330ms . Faites attention à ce code :
Si vous le remplacez par :
La vitesse du code est en fait plus lente que la version pure array. Quant à savoir pourquoi, la pratique est reine, et je ne l'ai pas analysé de toute façon, je connais ce résultat. Vous pouvez vous référer à un article de Tie Brother : Chat .net de type public, FIXED's impossible à corriger Bien sûr Vous pouvez aussi optimisez davantage les petites actions, telles que movzx eax,byte ptr [esi edi] Dans cette phrase, esi est en fait l'adresse de base du tableau. Si vous écrivez DataCP[SpeedOne] comme ceci, le décalage de l'adresse de base sera calculé à chaque fois. Oui, si une variable pointeur peut être contrôlée directement et dynamiquement en temps réel pour qu'elle pointe directement vers la position demandée, un ajout de moins sera nécessaire. Même si l'optimisation n'est pas très évidente, elle peut en gros atteindre le temps de 300 ms mentionné précédemment dans. la question. Le code spécifique se trouve dans la pièce jointe. Beaucoup de gens ne sont peut-être pas intéressés par mes choses, disant que jeter ces choses au GPU est mieux que ce que vous avez maintenant... J'espère que ces amis ne les attaqueront pas trop. Chacun a ses propres passe-temps, je n'aime que le processeur. Plus de séries d'analyses des principes de l'algorithme PhotoShop - Stylisation - Trouver les bords. Pour les articles connexes, veuillez faire attention au site Web PHP chinois ! 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)); // 计算查找表,注意已经砸查找表里进行了反色
Width = Bmp.Width; Height = Bmp.Height; Stride = (int)((Bmp.Width * 3 + 3) & 0XFFFFFFFC);
StrideC = (Width + 2) * 3; HeightC = Height + 2; // 宽度和高度都扩展2个像素
byte[] ImageData = new byte[Stride * Height]; // 用于保存图像数据,(处理前后的都为他)
byte[] ImageDataC = new byte[StrideC * HeightC]; // 用于保存扩展后的图像数据
fixed (byte* Scan0 = &ImageData[0])
{
BitmapData BmpData = new BitmapData();
BmpData.Scan0 = (IntPtr)Scan0; // 设置为字节数组的的第一个元素在内存中的地址
BmpData.Stride = Stride;
Bmp.LockBits(new Rectangle(0, 0, Bmp.Width, Bmp.Height), ImageLockMode.ReadWrite | ImageLockMode.UserInputBuffer, PixelFormat.Format24bppRgb, BmpData);
Stopwatch Sw = new Stopwatch(); // 只获取计算用时 Sw.Start(); for (Y = 0; Y < Height; Y++)
{
System.Buffer.BlockCopy(ImageData, Stride * Y, ImageDataC, StrideC * (Y + 1), 3); // 填充扩展图的左侧第一列像素(不包括第一个和最后一个点)
System.Buffer.BlockCopy(ImageData, Stride * Y + (Width - 1) * 3, ImageDataC, StrideC * (Y + 1) + (Width + 1) * 3, 3); // 填充最右侧那一列的数据
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; // 尽量减少计算
SpeedThree = SpeedTwo + StrideC; // 下面的就是严格的按照Sobel算字进行计算,代码中的*2一般会优化为移位或者两个Add指令的,如果你不放心,当然可以直接改成移位
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; // 跳往下一个像素
SpeedOne += 3;
}
}
Sw.Stop(); this.Text = "计算用时: " + Sw.ElapsedMilliseconds.ToString() + " ms";
Bmp.UnlockBits(BmpData); // 必须先解锁,否则Invalidate失败 }
Pic.Invalidate();
}
<span style="font-size: 13px;"> BlueOne = ImageDataC[SpeedOne] + <span style="color: #800080;">2</span> * ImageDataC[SpeedTwo] + ImageDataC[SpeedThree] - ImageDataC[SpeedOne + <span style="color: #800080;">6</span>] - <span style="color: #800080;">2</span> * ImageDataC[SpeedTwo + <span style="color: #800080;">6</span>] - ImageDataC[SpeedThree + <span style="color: #800080;">6</span><span style="color: #000000;">];<br/><br/></span><span style="color: #800080;">00000302</span><span style="color: #000000;"> cmp ebx,edi
</span><span style="color: #800080;">00000304</span><span style="color: #000000;"> jae 0000073C // 数组是否越界?
0000030a movzx eax,</span><span style="color: #0000ff;">byte</span> ptr [esi+ebx+<span style="color: #800080;">8</span><span style="color: #000000;">] // 将ImageDataC[SpeedOne]中的数据传送的eax寄存器
0000030f mov dword ptr [ebp</span>-<span style="color: #000000;">80h],eax
</span><span style="color: #800080;">00000312</span> mov edx,dword ptr [ebp-<span style="color: #000000;">2Ch]
</span><span style="color: #800080;">00000315</span><span style="color: #000000;"> cmp edx,edi
</span><span style="color: #800080;">00000317</span><span style="color: #000000;"> jae 0000073C // 数组是否越界?
0000031d movzx edx,</span><span style="color: #0000ff;">byte</span> ptr [esi+edx+<span style="color: #800080;">8</span><span style="color: #000000;">] // 将ImageDataC[SpeedTwo]中的数据传送到edx寄存器</span><span style="color: #800080;">00000322</span><span style="color: #000000;"> add edx,edx // 计算2*ImageDataC[SpeedTwo] </span><span style="color: #800080;">00000324</span><span style="color: #000000;"> add eax,edx // 计算ImageDataC[SpeedOne]+2*ImageDataC[SpeedTwo],并保存在eax寄存器中 </span><span style="color: #800080;">00000326</span><span style="color: #000000;"> cmp ecx,edi
</span><span style="color: #800080;">00000328</span><span style="color: #000000;"> jae 0000073C
0000032e movzx edx,</span><span style="color: #0000ff;">byte</span> ptr [esi+ecx+<span style="color: #800080;">8</span><span style="color: #000000;">] // 将ImageDataC[SpeedThree]中的数据传送到edx寄存器</span><span style="color: #800080;">00000333</span> mov dword ptr [ebp+<span style="color: #000000;">FFFFFF78h],edx
</span><span style="color: #800080;">00000339</span><span style="color: #000000;"> add eax,edx
0000033b lea edx,[ebx</span>+<span style="color: #800080;">6</span><span style="color: #000000;">]
0000033e cmp edx,edi
</span><span style="color: #800080;">00000340</span><span style="color: #000000;"> jae 0000073C
</span><span style="color: #800080;">00000346</span> movzx edx,<span style="color: #0000ff;">byte</span> ptr [esi+edx+<span style="color: #800080;">8</span><span style="color: #000000;">]
0000034b mov dword ptr [ebp</span>+<span style="color: #000000;">FFFFFF7Ch],edx
</span><span style="color: #800080;">00000351</span><span style="color: #000000;"> sub eax,edx
</span><span style="color: #800080;">00000353</span> mov edx,dword ptr [ebp-<span style="color: #000000;">2Ch]
</span><span style="color: #800080;">00000356</span> add edx,<span style="color: #800080;">6</span> <span style="color: #800080;">00000359</span><span style="color: #000000;"> cmp edx,edi
0000035b jae 0000073C
</span><span style="color: #800080;">00000361</span> movzx edx,<span style="color: #0000ff;">byte</span> ptr [esi+edx+<span style="color: #800080;">8</span><span style="color: #000000;">]
</span><span style="color: #800080;">00000366</span><span style="color: #000000;"> add edx,edx
</span><span style="color: #800080;">00000368</span><span style="color: #000000;"> sub eax,edx
0000036a lea edx,[ecx</span>+<span style="color: #800080;">6</span><span style="color: #000000;">]
0000036d cmp edx,edi
0000036f jae 0000073C
</span><span style="color: #800080;">00000375</span> movzx edx,<span style="color: #0000ff;">byte</span> ptr [esi+edx+<span style="color: #800080;">8</span><span style="color: #000000;">]
0000037a mov dword ptr [ebp</span>+<span style="color: #000000;">FFFFFF74h],edx
</span><span style="color: #800080;">00000380</span><span style="color: #000000;"> sub eax,edx
</span><span style="color: #800080;">00000382</span> mov dword ptr [ebp-30h],eax </span>
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)); // 计算查找表,注意已经砸查找表里进行了反色
Width = Bmp.Width; Height = Bmp.Height; Stride = (int)((Bmp.Width * 3 + 3) & 0XFFFFFFFC);
StrideC = (Width + 2) * 3; HeightC = Height + 2; // 宽度和高度都扩展2个像素
byte[] ImageData = new byte[Stride * Height]; // 用于保存图像数据,(处理前后的都为他)
byte[] ImageDataC = new byte[StrideC * HeightC]; // 用于保存扩展后的图像数据
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; // 设置为字节数组的的第一个元素在内存中的地址
BmpData.Stride = Stride;
Bmp.LockBits(new Rectangle(0, 0, Bmp.Width, Bmp.Height), ImageLockMode.ReadWrite | ImageLockMode.UserInputBuffer, PixelFormat.Format24bppRgb, BmpData);
Stopwatch Sw = new Stopwatch(); // 只获取计算用时 Sw.Start(); for (Y = 0; Y < Height; Y++)
{
System.Buffer.BlockCopy(ImageData, Stride * Y, ImageDataC, StrideC * (Y + 1), 3); // 填充扩展图的左侧第一列像素(不包括第一个和最后一个点)
System.Buffer.BlockCopy(ImageData, Stride * Y + (Width - 1) * 3, ImageDataC, StrideC * (Y + 1) + (Width + 1) * 3, 3); // 填充最右侧那一列的数据
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; // 尽量减少计算
SpeedThree = SpeedTwo + StrideC; // 下面的就是严格的按照Sobel算字进行计算,代码中的*2一般会优化为移位或者两个Add指令的,如果你不放心,当然可以直接改成移位
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; // 跳往下一个像素
SpeedOne += 3;
}
}
Sw.Stop(); this.Text = "计算用时: " + Sw.ElapsedMilliseconds.ToString() + " ms";
Bmp.UnlockBits(BmpData); // 必须先解锁,否则Invalidate失败 }
Pic.Invalidate();
}</p>
<p></p>
<p> Jetons un coup d'œil au code assembleur du même code : <span style="font-size: 13px;"></span></p>
<p><span style="font-size: 13px;"></span></p>
<p class="cnblogs_code"></p> Le code d'assemblage produit est concis, a une signification claire et manque de nombreuses instructions en comparaison. Bien sûr, ce sera beaucoup plus rapide. <pre class="brush:php;toolbar:false"><span style="font-size: 13px;">BlueOne = DataCP[SpeedOne] + <span style="color: #800080;">2</span> * DataCP[SpeedTwo] + DataCP[SpeedThree] - DataCP[SpeedOne + <span style="color: #800080;">6</span>] - <span style="color: #800080;">2</span> * DataCP[SpeedTwo + <span style="color: #800080;">6</span>] - DataCP[SpeedThree + <span style="color: #800080;">6</span><span style="color: #000000;">];<br><br></span><span style="color: #800080;">00000318</span> movzx eax,<span style="color: #0000ff;">byte</span> ptr [esi+<span style="color: #000000;">edi]
0000031c mov dword ptr [ebp</span>-<span style="color: #000000;">74h],eax
0000031f movzx edx,</span><span style="color: #0000ff;">byte</span> ptr [esi+<span style="color: #000000;">ebx]
</span><span style="color: #800080;">00000323</span><span style="color: #000000;"> add edx,edx
</span><span style="color: #800080;">00000325</span><span style="color: #000000;"> add eax,edx
</span><span style="color: #800080;">00000327</span> movzx edx,<span style="color: #0000ff;">byte</span> ptr [esi+<span style="color: #000000;">ecx]
0000032b mov dword ptr [ebp</span>-<span style="color: #000000;">7Ch],edx
0000032e add eax,edx
</span><span style="color: #800080;">00000330</span> movzx edx,<span style="color: #0000ff;">byte</span> ptr [esi+edi+<span style="color: #800080;">6</span><span style="color: #000000;">]
</span><span style="color: #800080;">00000335</span> mov dword ptr [ebp-<span style="color: #000000;">78h],edx
</span><span style="color: #800080;">00000338</span><span style="color: #000000;"> sub eax,edx
0000033a movzx edx,</span><span style="color: #0000ff;">byte</span> ptr [esi+ebx+<span style="color: #800080;">6</span><span style="color: #000000;">]
0000033f add edx,edx
</span><span style="color: #800080;">00000341</span><span style="color: #000000;"> sub eax,edx
</span><span style="color: #800080;">00000343</span> movzx edx,<span style="color: #0000ff;">byte</span> ptr [esi+ecx+<span style="color: #800080;">6</span><span style="color: #000000;">]
</span><span style="color: #800080;">00000348</span> mov dword ptr [ebp-<span style="color: #000000;">80h],edx
0000034b sub eax,edx
0000034d mov dword ptr [ebp</span>-30h],eax </span>
fixed (byte* P = &ImageData[0], CP = &ImageDataC[0], LP = &SqrValue[0])
{ byte* DataP = P, DataCP = CP, LutP = LP;