You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在WPF窗口中实现泛洪填充(Flood Fill)填充图片局部区域?

在WPF中实现泛洪填充(Flood Fill)功能

先看需求对应的图:
待填充的原图:
待填充的黑白线条图

目标填充效果:
填充完成后的彩色图

以下是具体实现方案:

核心逻辑

泛洪填充的本质是从点击的起始像素点出发,将相邻的、颜色匹配的像素批量替换为目标颜色。在WPF中,需要将只读的BitmapSource转换为可修改的像素数组,执行填充算法后再转回可显示的位图对象。

分步实现

1. 处理像素数据的工具方法

WPF的BitmapSource默认只读,我们需要先提取像素数组,修改后再更新回位图:

// 从BitmapSource提取像素数组
private byte[] ExtractPixelArray(BitmapSource bitmap)
{
    int stride = bitmap.PixelWidth * (bitmap.Format.BitsPerPixel / 8);
    byte[] pixels = new byte[bitmap.PixelHeight * stride];
    bitmap.CopyPixels(pixels, stride, 0);
    return pixels;
}

// 将修改后的像素数组转换为WriteableBitmap用于显示
private WriteableBitmap RefreshBitmap(BitmapSource original, byte[] modifiedPixels)
{
    WriteableBitmap wb = new WriteableBitmap(original);
    int stride = wb.PixelWidth * (wb.Format.BitsPerPixel / 8);
    wb.WritePixels(new Int32Rect(0, 0, wb.PixelWidth, wb.PixelHeight), modifiedPixels, stride, 0);
    return wb;
}

2. 非递归版泛洪填充算法(避免栈溢出)

递归版在处理大填充区域时容易触发栈溢出,推荐使用队列实现的广度优先搜索(BFS):

private void FloodFill(byte[] pixels, int width, int height, int startX, int startY, Color targetColor, Color replaceColor, int bitsPerPixel)
{
    int bytesPerPixel = bitsPerPixel / 8;
    int stride = width * bytesPerPixel;

    // 获取起始点的原始颜色
    int startIndex = startY * stride + startX * bytesPerPixel;
    Color originalColor = Color.FromArgb(
        pixels[startIndex + 3],
        pixels[startIndex + 2],
        pixels[startIndex + 1],
        pixels[startIndex]
    );

    // 目标颜色与原始颜色一致时直接返回,避免死循环
    if (originalColor == replaceColor) return;

    Queue<Point> pixelQueue = new Queue<Point>();
    pixelQueue.Enqueue(new Point(startX, startY));

    // 标记已处理的像素,防止重复入队
    bool[,] processed = new bool[height, width];
    processed[startY, startX] = true;

    while (pixelQueue.Count > 0)
    {
        Point current = pixelQueue.Dequeue();
        int x = (int)current.X;
        int y = (int)current.Y;

        // 替换当前像素颜色
        int pixelIndex = y * stride + x * bytesPerPixel;
        pixels[pixelIndex] = replaceColor.B;
        pixels[pixelIndex + 1] = replaceColor.G;
        pixels[pixelIndex + 2] = replaceColor.R;
        pixels[pixelIndex + 3] = replaceColor.A;

        // 检查并处理上下左右四个方向的相邻像素
        CheckNeighbor(x, y - 1, width, height, originalColor, pixels, bytesPerPixel, stride, processed, pixelQueue);
        CheckNeighbor(x, y + 1, width, height, originalColor, pixels, bytesPerPixel, stride, processed, pixelQueue);
        CheckNeighbor(x - 1, y, width, height, originalColor, pixels, bytesPerPixel, stride, processed, pixelQueue);
        CheckNeighbor(x + 1, y, width, height, originalColor, pixels, bytesPerPixel, stride, processed, pixelQueue);
    }
}

// 检查相邻像素是否符合填充条件,符合则入队
private void CheckNeighbor(int x, int y, int width, int height, Color originalColor, byte[] pixels, int bytesPerPixel, int stride, bool[,] processed, Queue<Point> queue)
{
    if (x >= 0 && x < width && y >= 0 && y < height && !processed[y, x])
    {
        int index = y * stride + x * bytesPerPixel;
        Color currentColor = Color.FromArgb(
            pixels[index + 3],
            pixels[index + 2],
            pixels[index + 1],
            pixels[index]
        );

        if (currentColor == originalColor)
        {
            processed[y, x] = true;
            queue.Enqueue(new Point(x, y));
        }
    }
}

3. 在WPF控件中触发填充

在Image控件的鼠标点击事件中调用填充逻辑,将控件坐标转换为图片像素坐标:

private void Image_MouseDown(object sender, MouseButtonEventArgs e)
{
    Image targetImage = sender as Image;
    if (targetImage.Source == null) return;

    Point clickPoint = e.GetPosition(targetImage);
    BitmapSource originalBitmap = (BitmapSource)targetImage.Source;

    // 将控件点击坐标转换为图片像素坐标
    int pixelX = (int)(clickPoint.X * originalBitmap.PixelWidth / targetImage.ActualWidth);
    int pixelY = (int)(clickPoint.Y * originalBitmap.PixelHeight / targetImage.ActualHeight);

    byte[] pixelArray = ExtractPixelArray(originalBitmap);

    // 示例:将点击区域填充为红色,需根据原图实际像素格式调整原始颜色判断
    FloodFill(pixelArray, originalBitmap.PixelWidth, originalBitmap.PixelHeight, pixelX, pixelY,
        Color.FromRgb(0, 0, 0), // 原图中需要填充的目标颜色(黑色线条内的白色区域需对应调整)
        Color.FromRgb(255, 0, 0), // 填充的目标颜色
        originalBitmap.Format.BitsPerPixel);

    targetImage.Source = RefreshBitmap(originalBitmap, pixelArray);
}

注意事项

  • 确保图片像素格式为Bgra32或Rgb24,其他格式需要调整颜色提取和替换的逻辑。
  • 大尺寸图片建议在后台线程执行填充,避免UI卡顿。
  • 如果需要支持模糊边缘的填充,可以添加颜色相似度判断(允许一定RGB差值),而非严格匹配颜色。

内容的提问来源于stack exchange,提问作者ramy_hackintosh

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.09 00:40:09