使用影像生成 AI,我可以在深色背景上獲得居中的物件。我的目標是將這個物件之外的所有像素轉換為透明的。我認為一個足夠好的方法是使用模糊閾值從所有 4 個角填充,這樣類似的顏色也會被洗掉。但是使用例如以下遞回方法會導致 StackOverflow:
static void FillPixels(Color[][] pixels, int x, int y, Color originColor, Color fillColor, float threshold)
{
int width = pixels.Length;
int height = pixels[0].Length;
bool isInLimits = x >= 0 && x < width && y >= 0 && y < height;
if (isInLimits && ColorDistance(pixels[x][y], originColor) <= threshold)
{
pixels[x][y] = fillColor;
FillPixels(pixels, x - 1, y, originColor, fillColor, threshold);
FillPixels(pixels, x 1, y, originColor, fillColor, threshold);
FillPixels(pixels, x, y - 1, originColor, fillColor, threshold);
FillPixels(pixels, x, y 1, originColor, fillColor, threshold);
}
}
影像最大為 1024x1024 像素。具體的背景顏色是未知的——我可以指示影像 AI 將其設為黑色,但它通常不會是精確的 rgb(0,0,0)——所以我最初是在每個角落動態挑選顏色。可以做些什么來用閾值填充填充,或者以其他方式為物件找到一個好的蒙版來擦除其背景?謝謝!
uj5u.com熱心網友回復:
首先要檢查的是 to 之間的距離fillColor是否originColor大于閾值。
另一種方法是使用 abool[][]或 a保留訪問節點的顯式記錄HashSet<(int x, int y)>。
接下來的事情是轉向迭代演算法。由于這是影像,最壞情況下的堆疊深度將是width*height. 這不太可能發生,但實際深度可能會變得足夠大,可以用于 stackoverflow。更改為顯式堆疊應該非常容易,例如:
var stack = new Stack<(int x, int y)>();
stack.Push((x, y));
while (stack.Count > 0)
{
var (x, y) = stack.Pop();
// insert logic
if(...){
stack.Push((x 1, y));
...
}
}
我也會考慮使用一些更好的資料型別。一個多維陣列,即Color[,]會更好,那么至少你知道所有行都具有相同的長度。但是在影像處理中,使用原始資料(即byte[]or Span<byte>)并手動計算像素索引是相當普遍的:var indexToFirstPixelByte = y * span x * bytesPerPixel,其中 span 是一行中的位元組數。然后您可以直接獲取顏色的位元組。這應該可以節省時間/記憶體,因為 Color-struct 比 ARGB 所需的 4 個位元組大得多。使用 aPoint或其他型別來表示一對 x,y 坐標可能也是一個好主意。
uj5u.com熱心網友回復:
JonasH 應該感謝幫助解決這個問題的正確答案(也感謝 Andrey Ischencko),但如果到達這里的任何人都在尋找完整的洪水填充結果類,這里是:
using System.Collections.Generic;
using UnityEngine;
public static class ImageFloodFill
{
public static void FillFromPoint(Texture2D texture, Color color, Vector2Int point, float threshold = 0f)
{
var points = new Vector2Int[] { point };
FillFromPoints(texture, color, points, threshold);
}
public static void FillFromCorners(Texture2D texture, Color color, float threshold = 0f)
{
var points = new Vector2Int[]
{
new Vector2Int(0, 0),
new Vector2Int(texture.width - 1, 0),
new Vector2Int(0, texture.height - 1),
new Vector2Int(texture.width - 1, texture.height - 1)
};
FillFromPoints(texture, color, points, threshold);
}
public static void FillFromPoints(Texture2D texture, Color color, Vector2Int[] points, float threshold = 0f)
{
Color[,] pixelsGrid = GetPixelsGrid(texture);
foreach (Vector2Int point in points)
{
FillPixels(pixelsGrid, point, color, threshold);
}
texture.SetPixels(GetPixelsLinearFromGrid(pixelsGrid));
texture.Apply();
}
static void FillPixels(Color[,] pixels, Vector2Int startPoint, Color color, float threshold)
{
int width = pixels.GetLength(0);
int height = pixels.GetLength(1);
bool[,] pixelsHandled = new bool[width, height];
Color originColor = pixels[startPoint.x, startPoint.y];
var size = new RectInt(0, 0, width, height);
var stack = new Stack<Vector2Int>();
stack.Push(startPoint);
while (stack.Count > 0)
{
Vector2Int point = stack.Pop();
if (size.Contains(point) && !pixelsHandled[point.x, point.y])
{
pixelsHandled[point.x, point.y] = true;
if (ColorDistance(pixels[point.x, point.y], originColor) <= threshold)
{
pixels[point.x, point.y] = color;
stack.Push(new Vector2Int(point.x - 1, point.y));
stack.Push(new Vector2Int(point.x 1, point.y));
stack.Push(new Vector2Int(point.x, point.y - 1));
stack.Push(new Vector2Int(point.x, point.y 1));
}
}
}
}
static Color[,] GetPixelsGrid(Texture2D texture)
{
int width = texture.width;
int height = texture.height;
Color[] pixelsLinear = texture.GetPixels();
Color[,] pixels = new Color[width, height];
for (int x = 0; x < width; x )
{
for (int y = 0; y < height; y )
{
pixels[x, y] = pixelsLinear[y * height x];
}
}
return pixels;
}
static Color[] GetPixelsLinearFromGrid(Color[,] pixelsGrid)
{
int width = pixelsGrid.GetLength(0);
int height = pixelsGrid.GetLength(1);
Color[] pixelsLinear = new Color[width * height];
for (int x = 0; x < width; x )
{
for (int y = 0; y < height; y )
{
pixelsLinear[y * height x] = pixelsGrid[x, y];
}
}
return pixelsLinear;
}
static float ColorDistance(Color color1, Color color2)
{
return Mathf.Sqrt(
Mathf.Pow(color1.r - color2.r, 2)
Mathf.Pow(color1.g - color2.g, 2)
Mathf.Pow(color1.b - color2.b, 2)
);
}
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/caozuo/528938.html
