ホームページ > 記事 > ウェブフロントエンド > PhotoShopアルゴリズム原理解析シリーズ - ピクセレーション - フラグメンテーション
好評だった前回の記事に引き続き、もう少し単純なアルゴリズムについてお話しましょう。
この記事では断片化アルゴリズムについて説明します。最初にいくつかのレンダリングを投稿します:
これは破壊的なフィルターであり、画像を作成する人の 90% は美しいことについて話しています。女性たち、それは男です、好色な男です。
フラグメント フィルターの原理に関して、インターネットで入手可能な情報は次のとおりです。 互いにオフセットされた画像のコピーを 4 つ作成し、ゴーストに似た効果を生成します。
上記の文から始めましょう。
分析: 上記の画像、特に目を比較すると、処理された画像は 1 つの目が 4 つの目になっていることがわかります。したがって、インターネット上の記述は信頼できます。
それで、オフセットの中心はどこですか? 4 つのオフセットはどの方向にありますか? これらの質問も非常に簡単です:
具体的な手順は次のとおりです。画像を開き、画像の色が比較的単調な場所(上記の美女の腕など)を2*2ピクセルの赤色で塗りつぶし、レイヤーをコピーして適用します。コピーしたレイヤーにフラグメンテーション フィルターを適用します。処理し、レイヤーの透明度を 50% に調整し、ローカルにズームインして次の画像を取得します。 この効果を使用すると、次の結論を簡単に導き出すことができます:
オフセットの中心は各ピクセルの中心にあり、4 つのオフセットは中心に対して対称で、45 度の角度で円形に均等に配置され、水平および垂直のオフセットはそれぞれ 45 度で、オフセットは 4 ピクセルです。
したがって、どのように重ね合わせるかという問題は推測できますが、4つのオフセット後の累積値の平均を取ることです。
この考えに基づいて、次のアルゴリズムを書きました:
private void CmdFragment_Click(object sender, EventArgs e) { int X, Y, Z, XX, YY; int Width, Height, Stride; int Speed, Index; int SumR, SumG, SumB; Bitmap Bmp = (Bitmap)Pic.Image; if (Bmp.PixelFormat != PixelFormat.Format24bppRgb) throw new Exception("不支持的图像格式."); Width = Bmp.Width; Height = Bmp.Height; Stride = (int)((Bmp.Width * 3 + 3) & 0XFFFFFFFC); byte[] ImageData = new byte[Stride * Height]; // 用于保存图像数据,(处理前后的都为他) byte[] ImageDataC = new byte[Stride * Height]; // 用于保存克隆的图像数据 int[] OffsetX = new int[] { 4, -4, -4, 4 }; // 每个点的偏移量 int[] OffsetY = new int[] { -4, -4, 4, 4 }; fixed (byte* P = &ImageData[0], CP = &ImageDataC[0]) { byte* DataP = P, DataCP = CP; 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(); System.Buffer.BlockCopy(ImageData, 0, ImageDataC, 0, Stride * Height); // 填充克隆数据 for (Y = 0; Y < Height; Y++) { Speed = Y * Stride; for (X = 0; X < Width; X++) { SumB = 0; SumG = 0; SumR = 0; for (Z = 0; Z < 4; Z++) // 累积取样点的取样和 { XX = X + OffsetX[Z]; YY = Y + OffsetY[Z]; if (XX < 0) // 注意越界 XX = 0; else if (XX >= Width) XX = Width - 1; if (YY < 0) YY = 0; else if (YY >= Height) YY = Height - 1; Index = YY * Stride + XX * 3; SumB += DataCP[Index]; SumG += DataCP[Index + 1]; SumR += DataCP[Index + 2]; } DataP[Speed] = (byte)((SumB+2) >> 2); // 求平均值(Sum+2)/4,为什么要+2,就为了四舍五入。比如如果计算结果为108.6,则取像素109更为合理 DataP[Speed + 1] = (byte)((SumG + 2) >> 2); DataP[Speed + 2] = (byte)((SumR + 2) >> 2); Speed += 3; // 跳往下一个像素 } } Sw.Stop(); this.Text = "计算用时: " + Sw.ElapsedMilliseconds.ToString() + " ms"; Bmp.UnlockBits(BmpData); // 必须先解锁,否则Invalidate失败 } Pic.Invalidate();}
このアルゴリズムでは、OffsetX と OffsetY はそれぞれサンプリング点ピクセルのオフセットです。同様に、このフィルタはフィールド操作を伴うため、処理前にピクセルのバックアップを行う必要がありますが、ここではバックアップ データは展開されません。したがって、サンプリング ポイントの座標がその範囲を超えているかどうかを内部コードで確認する必要があります。範囲を超えている場合、通常は画像フィルター アルゴリズムの範囲内にあります。
(1) 超過 これは、最も近い境界値、つまり、繰り返されるエッジ ピクセルとみなされます。コードのこの部分は、上に掲載した if...else if 部分です。 , (2) それを折り返す 次のコードは次のように記述できます:
while (XX >= Width) XX = XX - Width;while (XX < 0) XX = XX + Width;while (YY >= Height) YY = YY - Height;while (YY < 0) YY = YY + Height;e
(3) 画像範囲内のピクセルのみを計算します:
if (XX >= 0 && XX < Width && YY >= 0 && YY < Height) { // 累加计算 }もちろん変数です。適格な計算が実行された回数を記録するために使用する必要があります。
興味のある友達はコードを変更して試してみてください。
上記のコード スニペットでは、 DataP[Speed] = (byte)((SumB+2) >> 2); SumB に 2 を加算する理由は、結果を四捨五入するためであり、その方が合理的です。 テスト後、上記のコードは PS 処理の効果と 100% 一致しています。これは、私たちの推測が完全に正しいことを示しています。 アルゴリズムをさらに拡張することもできます: もう少し考えて、なぜ 4 つのゴーストでなければならないのか、角度は 45 度でなければならず、水平および垂直オフセットは 4 でなければなりません。ピクセル。興味のある読者が自分で開発できるように、以下の図を示します。 角度は 32 度、半径は 10、フラグメントの数は 7 です。次のような効果を生成します (私の Imageshop を使用して確認できます):