好評だった前回の記事に引き続き、もう少し単純なアルゴリズムについてお話しましょう。
この記事では断片化アルゴリズムについて説明します。最初にいくつかのレンダリングを投稿します:
これは破壊的なフィルターであり、画像を作成する人の 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;
(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 を使用して確認できます):