Domanda

Sto costruendo un sito Web per un club che fa parte di un'organizzazione madre. Sto scaricando (leeching;)) le immagini che sono state messe sulle pagine del profilo dell'organizzazione madre da mostrare sulla mia pagina. Ma il loro sito web ha un bel sfondo bianco e il mio sito web ha un gradiente grigio sullo sfondo. Questo non corrisponde bene. Quindi la mia idea era quella di modificare le immagini prima di salvarle sul mio server.

Sto usando GDI + per migliorare le mie immagini e quando uso il metodo MakeTransparent di Bitmap, funziona e fa quello che dovrebbe fare, ma ho ancora questi artefatti jpeg bianchi ovunque. I manufatti rendono l'immagine così brutta, sto meglio non rendere l'immagine trasparente e lasciarla bianca, ma è davvero brutta sul mio sito web. Posso sempre avere un bel bordo con uno sfondo bianco ovviamente, ma preferisco cambiare lo sfondo in trasparente.

Quindi mi chiedevo se e come posso rimuovere alcuni semplici artefatti JPEG in C #. Qualcuno lo ha mai fatto prima?

Grazie per il tuo tempo.

Immagine di esempio:

TB-5404

Immagine trasformata:

TB-5404 trasformato

È stato utile?

Soluzione

Beh, ho tentato qualcosa di tutt'altro che perfetto, ma immagino che potrebbe essere utile a qualcun altro.

Devo:

alt text

Problemi riscontrati: le ombre sono abbastanza lontane dal 'bianco sporco' che è difficile convertirle automaticamente, e anche se lo facessi l'ombra rimarrebbe comunque nell'immagine stessa. L'abbagliamento della parte superiore ... dell'hub, è anche più vicino al bianco sporco rispetto ai bit anti-alias. Ci sono da tre a sette punti di bianco nell'immagine che non sono collegati a nessuno degli angoli primari; e infine c'è ancora un po 'di bianco sui bordi (probabilmente potresti liberartene modificando il codice, ma non senza togliere parte della parte superiore dell'abbagliamento.

Codice inefficiente 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;
    }

Il codice funziona iniziando con due punti; impostandoli sul nero, quindi controllando se ci sono vicini vicini a loro; se lo sono vengono aggiunti a un elenco che viene quindi eseguito su. Alla fine si esauriscono i pixel bianchi per cambiare.

In alternativa, potresti prendere in considerazione l'idea di riprogettare il sito per utilizzare uno sfondo bianco.

Altri suggerimenti

Passa attraverso ogni pixel dell'immagine, se R, G e B sono più alti, diciamo, di 230, quindi sostituisci il colore con il colore desiderato (o trasparente). Forse anche pesare il nuovo colore a seconda di quanto sia lontano dal bianco "vero" il vecchio colore.

Aspettati di avere problemi anche se l'immagine reale è bianca, altrimenti finirai con uno stormtrooper grigio :)

Non sarai in grado di farlo automaticamente con una precisione del 100%.

La ragione di ciò è che l'unica informazione che hai è il colore con cui sai che alcuni pixel dell'immagine stanno tentando di fondersi perfettamente. Solo alcuni pixel nell'immagine utilizzeranno effettivamente i colori vicini o vicini a questo valore ai fini dell'ombreggiatura sullo sfondo, altri useranno (nel caso del bianco) perché l'oggetto reale rappresentato è in realtà bianco (dannazione per la precisione di queste truppe d'assalto imperiali).

Il tipo di sofisticato machine learning per rilevare quale sia un dominio problematico interessante e potrebbe essere un progetto divertente per te, ma sicuramente non costituirà una soluzione rapida al tuo problema immediato.

L'altro problema che hai è che, anche se riuscissi a rilevare con buona affidabilità quelle aree dell'immagine che stanno tentando di confondersi con lo sfondo, avrai problemi a "non piegarle" e poi ricondurle nel tuo nuovo colore di sfondo a meno che i colori non siano ragionevolmente compatibili. In questo caso il tuo grigio potrebbe funzionare poiché è un colore ad ampio spettro come il bianco.

La tecnica che si desidera utilizzare è la seguente:

  • Utilizza un algoritmo di riempimento per selezionare, dai bordi dell'immagine verso l'interno, tutti i pixel entro x% (1) del colore di sfondo noto.
  • Per quei pixel imposta il loro canale alfa su un valore in proporzione alla loro corrispondenza con il colore originale ed elimina la dominante di colore ad esso associata.
    • Quindi se lo sfondo ha un valore RGB a, b, c e il pixel è a + 5, b, c-7, il risultato è RGBA 5,0,0, ((a + b + c-7) / (a ??+ b + c) * 256) (1)
  • componi questa immagine di fusione alfa su un quadrato di dolore del nuovo colore di sfondo.
  • visualizza il risultato senza canale alfa come nuova immagine.

Ciò avrà ancora problemi per gli oggetti il ??cui colore è vicino al colore di sfondo.   * nel caso dell'originale, è possibile che l'ombreggiatura sia utilizzata per implicare le presenze dell'oggetto, in quanto tale riempimento dell'inondazione "invaderà" l'interno dell'immagine.   * nel caso di quest'ultimo l'immagine risultante perderà la definizione dell'oggetto e non saranno presenti ombre sottili, luci o solo linee semplici per indicare dove termina l'oggetto e finisce il fondo.

Questa è una prima approssimazione molto approssimativa, ma può coprire una percentuale ragionevole del tuo obiettivo. Quelle immagini con fori trasparenti completamente chiusi (come gli spazi vuoti nell'arco esterno nel tuo esempio) non dovrebbero mai funzionare bene in modo automatico poiché l'algoritmo non sarà in grado di distinguere tra buchi bianchi e stormtrooper bianco.
Potresti voler mettere in evidenza il tuo algoritmo per evidenziare le regioni dell'immagine che prevede di ribaltare e consentire la semplice selezione delle regioni da includere / escludere (usando lo strumento di selezione bacchetta magica di Pain.Net come esempio di come fare questo se lo desideri per essere fantasiosi, consentendo una semplice selezione per pixel per un minor sforzo iniziale.


  1. il valore per x sarà qualcosa che puoi sintonizzare - potrebbe essere che, in base ad alcuni aspetti dell'immagine (diciamo la proporzione dell'immagine che è vicina al colore di fondo) puoi modificarlo automaticamente.
  2. Nota che queste formule assumono un colore vicino al bianco, per il nero vicino che vorresti invertire

Un altro approccio basato sulla finestra di dialogo dei commenti:

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

Maschera fornita:

alt text

Ottieni il risultato:

alt text

Con il bordo della maschera leggermente ripulito in Paint.NET con l'anti-aliasing disabilitato. Ancora una volta, è applicabile solo se riesci a discernere quale bordo viene utilizzato ... ma si è rivelato ben fatto ... ad eccezione del Verde ...

Dovrai gestire anche sfumature di bianco sporco. Probabilmente quando inizialmente hanno creato gli iamge e impostato il colore di sfondo, c'era un po 'di anti-aliasing, e quindi quando si salvava come jpg, non tutti i colori verranno conservati perfettamente. Quindi, se stai rendendo trasparente un particolare colore, non otterrà tutte le sfumature di quel colore che lasceranno molti artefatti. Hai bisogno di qualcosa che garantisca una trasparenza proporzionale a quanto è vicino un colore al tuo colore chiave. Potrebbe essere qualcosa di più facile da fare come script batch in qualcosa come Photoshop, ma non so se questo è qualcosa che devi fare in tempo reale.

Non esiste un modo (remoto remoto) per affrontare questo problema a livello di codice. Le aree di artefatto bianco attorno al bordo dell'immagine sono il risultato di pixel quasi bianchi ma non del tutto, quindi non rilevano l'effetto di trasparenza. Ci sono anche un paio di punti sulla maschera / tazza da caffè che sono bianchi puri, quindi diventano trasparenti e quindi grigi.

La soluzione migliore è contattare il webmaster del sito originale e vedere se possono inviarti le immagini originali, si spera in Photoshop o in qualche altro formato in cui i livelli originali siano conservati separatamente. Potresti quindi rigenerare le immagini in un formato che preservi la trasparenza originale (PNG o qualcosa del genere) oppure usa il tuo gradiente per lo sfondo (sarebbe molto difficile farlo bene, dal momento che non sai esattamente dove all'interno il gradiente dell'immagine verrà renderizzata).

Vorrei andare con una sorta di bordo attorno alle immagini, come hai suggerito.

Grazie a tutti per le risposte .. tutti i buoni suggerimenti ..

Ecco cosa ho preparato ieri:

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

Non è il codice più bello del libro, ma ciò che fa è per ogni pixel nella regione dei pixel che calcola una distanza tra Color.White e il mio colore, e quando è inferiore alla distanza predefinita lo farà essere colorato trasparente ..

Questo pezzo di codice produce il seguente risultato:

TB-5404 Transformed v 2.0

Non è male bad you .. ma fa ancora schifo :)

Ho un asso nella manica che condividerò anche con voi se riuscirà ...

Ecco un altro tentativo fallito;)

Ho avuto un'idea .. i film usano lo schermo verde, perché non usare un overlay verde per la mia immagine .. Quindi ecco la sovrapposizione:

Sovrapposizione verde con cosa interna trasparente

E i risultati:

Roba verde ovunque

Quindi ho imparato un'altra lezione preziosa sulla compressione PNG .. L'ho provato anche con bmp, ma in qualche modo finisco sempre con un bordo verde .. Posso provare a cancellarlo, ma forse dovrei lasciarlo sul bianco sullo sfondo, posiziona un bordo stupido attorno e falla finita ...

Ah bene ..;)

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top