Вопрос

Я создаю веб-сайт для клуба, который является частью материнской организации.Я загружаю (пиявляю ;)) изображения, которые были размещены на страницах профиля материнской организации, чтобы показать их на своей собственной странице.Но у их сайта красивый белый фон, а у моего сайта красивый серый градиент.Это не очень хорошо сочетается.Моя идея заключалась в том, чтобы отредактировать изображения перед сохранением их на сервере.

Я использую GDI+ для улучшения своих изображений, и когда я использую метод MakeTransparent для растрового изображения, он работает и делает то, что должен, но у меня все еще повсюду эти белые артефакты jpeg.Артефакты делают изображение настолько плохим, что мне лучше не делать изображение прозрачным, а просто оставить его белым, но на моем собственном веб-сайте это действительно некрасиво.Конечно, я всегда могу сделать красивую рамку с белым фоном, но я предпочитаю менять фон на прозрачный.

Поэтому мне было интересно, могу ли я удалить некоторые простые артефакты JPEG в C# и как это сделать.Кто-нибудь когда-нибудь делал это раньше?

Спасибо за ваше время.

Пример изображения:

TB-5404

Трансформированное изображение:

TB-5404 transformed

Это было полезно?

Решение

Ну, я попробовал что-то, что далеко от совершенства, но я думаю, что это может быть полезно для кого-то еще.

Я дошел до:

alt text

Возникли проблемы: тени достаточно далеки от «не совсем белого», поэтому их трудно автоматически конвертировать, и даже если бы вы сделали так, тень все равно будет на самом изображении. Блики от верхнего ... хаба, также ближе к беловатому, чем сглаженные биты. На изображении есть три-семь белых пятен, которые не связаны ни с одним из основных углов; и, наконец, все еще остается немного белого по краям (возможно, можно избавиться от него, изменив код, но не снимая часть верхней части бликов.

C # неэффективный код:

    static void Main()
    {
        Bitmap bmp=new Bitmap("test.jpg");

        int width = bmp.Width;
        int height = bmp.Height;
        Dictionary<Point, int> currentLayer = new Dictionary<Point, int>();
        currentLayer[new Point(0, 0)] = 0;
        currentLayer[new Point(width - 1, height - 1)] = 0;
        while (currentLayer.Count != 0)
        {
            foreach (Point p in currentLayer.Keys)
                bmp.SetPixel(p.X, p.Y, Color.Black);
            Dictionary<Point, int> newLayer = new Dictionary<Point, int>();
            foreach (Point p in currentLayer.Keys)
                foreach (Point p1 in Neighbors(p, width, height))
                    if (Distance(bmp.GetPixel(p1.X, p1.Y), Color.White) < 40)
                        newLayer[p1] = 0;
            currentLayer = newLayer;
        }

        bmp.Save("test2.jpg");
    }

    static int Distance(Color c1, Color c2)
    {
        int dr = Math.Abs(c1.R - c2.R);
        int dg = Math.Abs(c1.G - c2.G);
        int db = Math.Abs(c1.B - c2.B);
        return Math.Max(Math.Max(dr, dg), db);
    }

    static List<Point> Neighbors(Point p, int maxX, int maxY)
    {
        List<Point> points=new List<Point>();
        if (p.X + 1 < maxX) points.Add(new Point(p.X + 1, p.Y));
        if (p.X - 1 >= 0) points.Add(new Point(p.X - 1, p.Y));
        if (p.Y + 1 < maxY) points.Add(new Point(p.X, p.Y + 1));
        if (p.Y - 1 >= 0) points.Add(new Point(p.X, p.Y - 1));
        return points;
    }

Код работает, начиная с двух точек; установив их в черный цвет, а затем проверяя, есть ли рядом с ними соседи рядом с белым; если они есть, они добавляются в список, который затем выполняется против. В конце концов заканчиваются белые пиксели для изменения.

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

Другие советы

Прокрутите каждый пиксель в изображении, если R, G и B выше, скажем, 230, то замените цвет на желаемый (или прозрачный). Может быть, даже вес нового цвета зависит от того, насколько далек от «истинного» белого старый цвет.

Ожидайте проблем, если фактическое изображение также белое, в противном случае у вас будет серый штурмовик:)

Вы не сможете сделать это автоматически со 100% точностью.

Причина этого в том, что единственная информация, которой вы располагаете, — это цвет, который вы знаете. некоторый пиксели изображения пытаются хорошо смешаться.Только некоторые пиксели изображения будут использовать цвета с этим значением или близкие к нему для затенения фона, другие будут использовать (в случае белого цвета), потому что фактический представленный объект на самом деле белый (к черту точность эти имперские штурмовики).

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

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

Техника, которую вы хотите использовать, следующая:

  • Используйте алгоритм заливки, чтобы выбрать от краев изображения внутрь все пиксели в пределах x% (1) от известного цвета фона.
  • Для этих пикселей установите для альфа-канала значение, пропорциональное их совпадению с исходным цветом, и устраните связанный с ним цветовой оттенок.
    • Таким образом, если фон составляет значение RGB A, B, C, а пиксель-A+5, B, C-7, то результат-RGBA 5,0,0 ((A+B+C-7)/(A+ b+c)*256) (1)
  • скомпонуйте это изображение альфа-смешивания с квадратом боли нового цвета фона.
  • визуализируйте результат без альфа-канала в качестве нового изображения.

При этом по-прежнему будут возникать проблемы с объектами, цвет которых близок к цвету фона.* в случае с оригиналом возможно, что затенение используется для обозначения присутствия объекта, поэтому заливка будет «вторгаться» внутрь изображения.* в последнем случае результирующее изображение потеряет четкость объекта и не будет присутствовать тонких теней, бликов или просто простых линий, указывающих, где заканчивается объект и заканчивается фон.

Это очень грубое первое приближение, но оно может покрыть разумный процент вашей цели.Эти изображения с прозрачными полностью закрытыми отверстиями (например, промежутками во внешней арке в вашем примере) вряд ли когда-либо будут работать автоматически автоматически, поскольку алгоритм не сможет отличить белые дыры от белого штурмовика.
Возможно, вы захотите заставить свой алгоритм выделять области изображения, которые он планирует повторно смешивать, и разрешить простой выбор областей для включения/исключения (используя инструмент выбора «волшебная палочка» от Pain.Net в качестве примера того, как это сделать, если вы хотите быть необычным, позволяя выполнять простой выбор попикселям с меньшими первоначальными усилиями.


  1. значение x будет тем, что вы настроите - возможно, на основе некоторых аспектов изображения (скажем, пропорции изображения, близкого к цвету фона) вы можете настроить его автоматически.
  2. Обратите внимание, что в этой формуле предполагается цвет, близкий к белому, для цветов, близких к черному, необходимо инвертировать

Еще один подход, основанный на диалоге комментариев:

static void Main()
{
    Bitmap mask = new Bitmap(@"mask.bmp");
    Bitmap bmp=new Bitmap(@"test.jpg");
    int width = bmp.Width;
    int height = bmp.Height;

    for(int x=0; x<width; x++)
        for (int y = 0; y < height; y++)
            if (mask.GetPixel(x, y).R < 250)
                bmp.SetPixel(x,y,mask.GetPixel(x,y));
    bmp.Save(@"test3.jpg");
}

Данная маска:

alt text

Вы получите результат:

alt text

С краской маски, слегка очищенной в Paint.NET, с отключенным сглаживанием. Опять же, это применимо только в том случае, если вы можете различить, какая граница используется ... но это получилось хорошо ... за исключением Зеленого ...

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

Нет (удаленно простого) способа решить эту проблему программно. Белые области артефакта-y по краю изображения являются результатом пикселей, которые почти белые, но не совсем, поэтому они не улавливают эффект прозрачности. На маске / кофейной кружке также есть несколько пятен, которые являются чисто белыми, поэтому они становятся прозрачными и, следовательно, серыми.

Лучше всего связаться с веб-мастером исходного сайта и выяснить, могут ли они прислать вам оригинальные изображения, будем надеяться, в фотошопе или в каком-либо другом формате, где оригинальные слои сохраняются отдельно. Затем вы можете заново сгенерировать изображения в формате, который сохраняет исходную прозрачность (PNG или что-то в этом роде), или же использовать свой градиент для фона (было бы очень сложно сделать это правильно, так как вы точно не знаете, где внутри градиент, изображение будет отображаться).

Я бы пошел с какими-то границами вокруг изображений, как вы предложили.

Спасибо всем за ваши ответы .. все хорошие предложения ..

Вот что я приготовил вчера:

public const int PIXEL_REGION = 60;
public const int TRANSPARENT_DISTANCE = 60;
public static readonly Color TRANSPARENT_COLOR = Color.White;

private System.Drawing.Image ProcessImage(System.Drawing.Image image)
{
    Bitmap result = new Bitmap(image.Width, image.Height);

    Bitmap workImage = new Bitmap(image);

    for (int x = 0; x < image.Width; x++)
    {
        for (int y = 0; y < image.Height; y++)
        {
            Color color = workImage.GetPixel(x, y);

            if (x < PIXEL_REGION || x > image.Width - PIXEL_REGION || y < PIXEL_REGION || y > image.Height - PIXEL_REGION)
            {
                double distance = CalculateColourDistance(TRANSPARENT_COLOR, color);

                if(distance < TRANSPARENT_DISTANCE)
                {
                    result.SetPixel(x, y, Color.Transparent);
                    continue;
                }
            }

            result.SetPixel(x, y, color);
        }
    }

    return result;
}

private double CalculateColourDistance(Color c1, Color c2)
{
    int a = c2.A - c1.A;
    int r = c2.R - c1.R;
    int g = c2.G - c1.G;
    int b = c2.B - c1.B;

    return Math.Sqrt(Math.Pow(a, 2) + Math.Pow(r, 2) + Math.Pow(g, 2) + Math.Pow(b, 2));
}

Это не самый красивый код в книге, но он делает то, что для каждого пикселя в пиксельной области он вычисляет расстояние между Color.White и моим собственным цветом, и когда он ниже предварительно определенного расстояния, он будет быть цветным прозрачным ..

Этот фрагмент кода дает следующий результат:

TB-5404 преобразуется в 2.0

Это неплохо, но это все равно отстой:)

У меня есть один трюк в рукаве, которым я поделюсь с вами, ребята, если это удастся ...

Вот еще одна неудачная попытка;)

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

Зеленое наложение с прозрачной внутренней вещью

И результаты:

Зеленые вещи повсюду

Итак, я выучил еще один ценный урок о сжатии PNG ... Я также попробовал это с bmp, но каким-то образом я всегда получаю зеленую рамку ... Я могу попытаться стереть это, но, возможно, я просто должен оставить его на белом фон, поместите вокруг него какую-нибудь глупую границу и покончите с этим ...

Ну хорошо ..;)

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top