Question

Je construis un site Web pour un club qui fait partie d’une organisation mère. Je télécharge (leeching;)) les images qui ont été mises sur les pages de profil de l’organisation mère à afficher sur ma propre page. Mais leur site Web a un joli fond blanc et mon site a un joli dégradé gris sur le fond. Cela ne correspond pas bien. Mon idée était donc de modifier les images avant de les enregistrer sur mon serveur.

J'utilise GDI + pour améliorer mes images, et lorsque j'utilise la méthode MakeTransparent of Bitmap, cela fonctionne et il fonctionne comme prévu, mais j'ai toujours ces artefacts jpeg blancs un peu partout. Les artefacts rendent l’image si mauvaise qu’il vaut mieux ne pas rendre l’image transparente et la laisser blanche, mais c’est vraiment moche sur mon propre site Web. Je peux toujours avoir une jolie bordure avec un fond blanc bien sûr, mais je préfère changer le fond en transparent.

Je me demandais donc si et comment je pouvais supprimer quelques artefacts JPEG simples en C #. Quelqu'un a-t-il déjà fait cela auparavant?

Merci de votre temps.

Exemple d'image:

TB-5404

Image transformée:

TB-5404 transformé

Était-ce utile?

La solution

Bien, j’ai essayé quelque chose qui n’est pas parfait, mais je pense que cela pourrait être utile à quelqu'un d'autre.

Je suis arrivé à:

alt text

Problèmes rencontrés: les ombres sont suffisamment éloignées du "blanc cassé" pour qu'il soit difficile de les convertir automatiquement, et même si vous l'utilisiez, l'ombre serait toujours dans l'image elle-même. L’éblouissement du haut ... moyeu est également plus proche du blanc cassé que des éléments anti-aliasés. Il y a trois à sept points blancs dans l'image qui ne sont connectés à aucun des coins principaux; et enfin, il reste encore un peu de blanc sur les bords (on pourrait probablement s'en débarrasser en modifiant le code, mais pas sans enlever une partie du sommet de l'éblouissement.

Code inefficace en 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;
    }

Le code fonctionne en commençant par deux points; en les plaçant en noir, puis en vérifiant si des voisins proches d'eux sont proches de blanc; s'ils le sont, ils sont ajoutés à une liste qui est ensuite exécutée. Finalement, il ne reste plus de pixels blancs à changer.

Vous pouvez également envisager de redéfinir le site pour utiliser un arrière-plan blanc.

Autres conseils

Faites une boucle sur chaque pixel de l’image, si R, V et B est supérieur à, par exemple, 230 puis remplacez la couleur par la couleur souhaitée (ou transparente). Vous pouvez même pondérer la nouvelle couleur en fonction de son éloignement du blanc "vrai".

Attendez-vous à des problèmes si l'image est également blanche, sinon vous allez vous retrouver avec un stormtrooper gris:)

Vous ne pourrez pas le faire automatiquement avec une précision de 100%.

La raison en est que la seule information dont vous disposez est la couleur avec laquelle vous savez que certains pixels de l’image tentent de se fondre parfaitement. Seuls quelques pixels de l’image utiliseront réellement des couleurs proches de cette valeur aux fins de l’ombrage en arrière-plan, d’autres utiliseront (dans le cas du blanc) parce que l’objet réel représenté est en fait blanc (sacrément la précision de ces troupes d'assaut impériales).

Ce type de machine sophistiquée apprend à détecter quel est le domaine du problème intéressant et peut-être un projet amusant pour vous, mais cela ne constituera certainement pas une solution rapide à votre problème immédiat.

Votre autre problème est que, même si vous pouviez détecter avec une bonne fiabilité les zones de l'image qui tentent de se fondre dans le fond, vous aurez des problèmes pour les "dé-mélanger" et les re-mélanger ensuite dans votre nouvelle couleur d'arrière-plan. sauf si les couleurs sont raisonnablement compatibles. Dans ce cas, votre gris peut fonctionner puisqu'il s'agit d'une couleur à large spectre comme le blanc.

La technique que vous souhaitez utiliser est la suivante:

  • Utilisez un algorithme de remplissage par inondation pour sélectionner, à partir des bords de l'image vers l'intérieur, tous les pixels compris entre x% (1) et la couleur de fond connue.
  • Pour ces pixels, définissez leur couche alpha sur une valeur proportionnelle à leur correspondance avec la couleur d'origine et éliminez la dominante de couleur qui lui était associée.
    • Donc, si le fond est une valeur RVB a, b, c et le pixel est a + 5, b, c-7, alors le résultat est RGBA 5,0,0, ((a + b + c-7) / (a ??+ b + c) * 256) (1)
  • composez cette image de fusion alpha sur un carré douloureux de la nouvelle couleur de fond.
  • restituez le résultat sans canal alpha comme nouvelle image.

Cela posera toujours des problèmes pour les objets dont la couleur est proche de la couleur de fond.   * Dans le cas de l'original, il se peut que l'ombrage soit utilisé pour impliquer les présences de l'objet, de sorte que le remplissage saturera l'enveloppe de l'intérieur de l'image.   * Dans le cas de ce dernier, l’image résultante perdra la définition de l’objet et il n’y aura ni ombrage subtil, ni rehauts ou lignes simples pour indiquer où l’objet se termine et l’arrière-plan se termine.

Ceci est une première approximation très approximative, mais peut couvrir un pourcentage raisonnable de votre cible. Les images comportant des trous transparents complètement fermés (comme les trous de l’arc externe dans votre exemple) ne fonctionneront probablement jamais aussi bien de manière automatique, car l’algorithme ne pourra pas faire la distinction entre les trous blancs et le stormtrooper blanc.
Vous voudrez peut-être que votre algorithme mette en surbrillance les régions de l’image qu’il envisage de remélanger et permette la sélection simple de régions à inclure / exclure (en utilisant l’outil de sélection de baguette magique de Pain.Net comme exemple de procédure à suivre si vous le souhaitez. être sophistiqué, permettant une sélection simple par pixel pour moins d'effort immédiat.

  1. la valeur pour x sera quelque chose que vous accorderez - il se peut que, en fonction de certains aspects de l'image (par exemple, la proportion de l'image qui soit proche de la couleur de fond), vous pouvez la modifier automatiquement.
  2. Notez que cette formule suppose une couleur proche du blanc, vous voudriez inverser le noir

Une autre approche basée sur le dialogue de commentaire:

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");
}

Masque donné:

alt text

Vous obtenez le résultat:

alt text

Avec la bordure du masque légèrement nettoyée dans Paint.NET avec l'anti-aliasing désactivé. Encore une fois, cela ne s’applique que si vous pouvez discerner quelle bordure est utilisée ... mais cela s’est bien passé ... sauf pour le vert ...

Vous devrez également gérer les tons de blanc cassé. Probablement quand ils ont créé les images initialement et défini la couleur de fond, il y avait un peu d'anti-aliasing, puis lors de l'enregistrement en jpg, toutes les couleurs ne seront pas parfaitement conservées. Donc, si vous rendez une couleur particulière transparente, elle ne donnera pas toutes les nuances de cette couleur, ce qui laissera de nombreux artefacts. Vous avez besoin de quelque chose qui permette une transparence proportionnelle à la proximité d'une couleur avec la couleur de votre clé. Cela pourrait être quelque chose de plus facile à faire en tant que script batch dans quelque chose comme Photoshop, mais je ne sais pas si c'est quelque chose que vous devez faire en temps réel.

Il n’existe aucun moyen (à distance facile) de traiter ce problème par programmation. Les zones blanches d'artefacts situées autour du bord de l'image sont le résultat de pixels presque blancs mais pas tout à fait, de sorte qu'elles ne prennent pas en compte l'effet de transparence. Il y a aussi quelques taches sur le masque / la tasse à café qui sont d'un blanc pur, de sorte qu'elles deviennent transparentes et donc grises.

Votre meilleur choix est de contacter le webmaster du site d'origine et de voir s'il peut vous envoyer les images d'origine, éventuellement dans Photoshop ou dans un autre format dans lequel les calques d'origine sont conservés séparément. Vous pouvez ensuite recréer les images dans un format préservant la transparence d'origine (PNG ou quelque chose du genre) ou utiliser votre dégradé comme arrière-plan (il serait très difficile d'obtenir ce droit, car vous ne savez pas exactement où le dégradé de l'image sera rendu).

Je choisirais une sorte de bordure autour des images, comme vous l'avez suggéré.

Merci à tous pour vos réponses .. toutes vos bonnes suggestions ..

Voici ce que j'ai concocté hier:

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));
}

Ce n'est pas le plus beau code du livre, mais ce qu'il fait est que, pour chaque pixel de la région de pixels, il calcule une distance entre Color.White et ma propre couleur. Lorsqu'il est inférieur à la distance prédéfinie, il être coloré transparent ..

Ce morceau de code produit le résultat suivant:

TB-5404 transformé v 2.0

Ce n’est pas mauvais, vous .. mais ça craint toujours:)

J'ai un tour de magie que je partagerai avec vous aussi si ça réussit ...

Voici une autre tentative infructueuse;)

J'ai eu une idée .. les films utilisent un écran vert, pourquoi ne pas utiliser une superposition verte pour mon image .. Alors, voici la superposition:

Superposition verte avec un élément interne transparent

Et les résultats:

Des trucs verts partout

Alors j’ai appris une autre leçon précieuse sur la compression PNG .. Je l’ai aussi essayé avec bmp, mais je finis toujours par avoir une bordure verte .. Je peux essayer de l’effacer, mais je devrais peut-être la laisser en blanc arrière-plan, placez une bordure idiote autour et faites-le avec vous ...

Ah ben ..;)

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top