С# .NET удалить фон зеленого экрана

Я работаю над программным обеспечением для обработки фотографий для настольного ПК, которое работает на Windows 8. Я хотел бы иметь возможность удалить зеленый фон с фотографии с помощью хроматической рирпроекции.

Я новичок в обработке изображений, я нашел несколько интересных ссылок (например, http://www.quasimondo.com/archives/000615.php ), но я не могу перевести его в код С#.

Я использую веб-камеру (с aforge.net), чтобы просмотреть предварительный просмотр и сделать снимок. . Я попробовал цветные фильтры, но зеленый фон не совсем однороден, так что это не работает.

Как это сделать правильно на С#?


person user2964381    schedule 03.01.2014    source источник
comment
Не уверен, почему это было отмечено - вопрос и ответ помогли решить проблему, которую я пытался решить. Ваше здоровье.   -  person Gregory William Bryant    schedule 03.05.2014


Ответы (3)


Это сработает, даже если фон неоднороден, вам просто понадобится правильная стратегия, достаточно щедрая, чтобы захватить весь ваш зеленый экран, не заменяя ничего другого.

Поскольку по крайней мере некоторые ссылки на вашей связанной странице не работают, я попробовал свой собственный подход:

  • Основы просты: сравните цвет пикселя изображения с некоторым эталонным значением или примените какую-либо другую формулу, чтобы определить, должен ли он быть прозрачным или замененным.

  • Самая простая формула будет включать что-то простое, например, «определить, является ли зеленый цвет самым большим значением». Хотя это будет работать с очень простыми сценами, это может вас испортить (например, белый или серый цвет также будут отфильтрованы).

Я немного поиграл, используя простой пример кода. Хотя я использовал Windows Forms, он должен без проблем переноситься, и я уверен, что вы сможете интерпретировать код. Просто обратите внимание, что это не обязательно самый эффективный способ сделать это.

Bitmap input = new Bitmap(@"G:\Greenbox.jpg");

Bitmap output = new Bitmap(input.Width, input.Height);

// Iterate over all piels from top to bottom...
for (int y = 0; y < output.Height; y++)
{
    // ...and from left to right
    for (int x = 0; x < output.Width; x++)
    {
        // Determine the pixel color
        Color camColor = input.GetPixel(x, y);

        // Every component (red, green, and blue) can have a value from 0 to 255, so determine the extremes
        byte max = Math.Max(Math.Max(camColor.R, camColor.G), camColor.B);
        byte min = Math.Min(Math.Min(camColor.R, camColor.G), camColor.B);

        // Should the pixel be masked/replaced?
        bool replace =
            camColor.G != min // green is not the smallest value
            && (camColor.G == max // green is the biggest value
            || max - camColor.G < 8) // or at least almost the biggest value
            && (max - min) > 96; // minimum difference between smallest/biggest value (avoid grays)

        if (replace)
            camColor = Color.Magenta;

        // Set the output pixel
        output.SetPixel(x, y, camColor);
    }
}

Я использовал пример изображения из Википедии и получил следующий результат:

Замаскированный результат (пурпурный будет заменен вашим фоном)

Просто обратите внимание, что вам могут понадобиться другие пороговые значения (8 и 96 в моем коде выше), вы даже можете использовать другой термин, чтобы определить, следует ли заменить какой-либо пиксель. Вы также можете добавить сглаживание между кадрами, смешивание (где разница в зеленом меньше) и т. д., чтобы также уменьшить резкие края.

person Mario    schedule 03.01.2014
comment
Я провел исследование: вы должны заблокировать растровое изображение для повышения производительности. Я использовал простую библиотеку отсюда: codeproject.com/Articles/15192/ - person daniel; 22.06.2015
comment
@zoidbergi да, GetPixel() и SetPixel() не совсем подходят для этого. В продуктивном коде я бы, вероятно, использовал нативный код для всего этого. - person Mario; 22.06.2015

Я попробовал решение Mario, и оно сработало отлично, но для меня это немного медленно. Я искал другое решение и нашел проект, в котором используется более эффективный метод. Github публикует GreenScreen

Этот проект берет папку и обрабатывает все файлы, мне просто нужно изображение, поэтому я сделал это:

private Bitmap RemoveBackground(Bitmap input)
    {
        Bitmap clone = new Bitmap(input.Width, input.Height, PixelFormat.Format32bppArgb);
        {
            using (input)
            using (Graphics gr = Graphics.FromImage(clone))
            {
                gr.DrawImage(input, new Rectangle(0, 0, clone.Width, clone.Height));
            }

            var data = clone.LockBits(new Rectangle(0, 0, clone.Width, clone.Height), ImageLockMode.ReadWrite, clone.PixelFormat);

            var bytes = Math.Abs(data.Stride) * clone.Height;
            byte[] rgba = new byte[bytes];
            System.Runtime.InteropServices.Marshal.Copy(data.Scan0, rgba, 0, bytes);

            var pixels = Enumerable.Range(0, rgba.Length / 4).Select(x => new {
                B = rgba[x * 4],
                G = rgba[(x * 4) + 1],
                R = rgba[(x * 4) + 2],
                A = rgba[(x * 4) + 3],
                MakeTransparent = new Action(() => rgba[(x * 4) + 3] = 0)
            });

            pixels
                .AsParallel()
                .ForAll(p =>
                {
                    byte max = Math.Max(Math.Max(p.R, p.G), p.B);
                    byte min = Math.Min(Math.Min(p.R, p.G), p.B);

                    if (p.G != min && (p.G == max || max - p.G < 7) && (max - min) > 20)
                        p.MakeTransparent();
                });

            System.Runtime.InteropServices.Marshal.Copy(rgba, 0, data.Scan0, bytes);
            clone.UnlockBits(data);

            return clone;
        }
    }

Не забудьте избавиться от входного растрового изображения и возврата этого метода. Если вам нужно сохранить изображение, просто используйте инструкцию Save Bitmap.

clone.Save(@"C:\your\folder\path", ImageFormat.Png);

Здесь вы можете найти методы для еще более быстрой обработки изображения.Быстрая обработка изображений в C#

person Francesc MP    schedule 12.11.2018

Хромакей на фотографии должен предполагать аналоговый ввод. В реальном мире точные значения встречаются очень редко.

Как вы это компенсируете? Обеспечьте порог вокруг зеленого по вашему выбору как в оттенке, так и в тоне. Любой цвет в пределах этого порога (включительно) должен быть заменен выбранным вами фоном; прозрачный может быть лучше. В первой ссылке это достигается параметрами Mask In и Mask Out. Параметры до и после размытия пытаются сделать фон более однородным, чтобы уменьшить побочные эффекты шума кодирования, чтобы вы могли использовать более узкий (предпочтительный) порог.

Для повышения производительности вы можете написать пиксельный шейдер, чтобы заменить «зеленый» на прозрачный, но это следует учитывать после того, как он заработает.

person Gusdor    schedule 03.01.2014
comment
Хорошо, но, как я уже сказал, я новичок в манипулировании изображениями. Мне тоже нужен код. Многие люди и программное обеспечение делают это. Вы делаете фото, веб-камеру или что-то еще; с зеленым фоном экрана, и он удаляет зеленый, чтобы поставить свой фон. Я просто хочу сделать это, и я не нахожу ничего хорошего - person user2964381; 03.01.2014
comment
можно ссылку на пиксельные шейдеры? это зависит от гпу? - person daniel; 22.10.2016