2013-09-03 12 views
6

Ho una fotocamera digitale che scatta foto 24x7 e spesso a seconda del tempo restituisce le immagini con una dominante di colori sgradevole. Generalmente blu.Rimozione del colore dominante dall'immagine

enter image description here

ho cercato di trovare qualche codice sorgente o libreria che posso chiamare da C# per ridurre la dominante di colore delle immagini.

Photoshop ha una caratteristica che funziona abbastanza bene sulle immagini che ho provato che è:

  1. Aprire l'immagine
  2. Seleziona Immagine -> Regolazioni -> Corrispondenza colore
  3. Selezionare la casella di controllo Neutralizzare

Questo funziona bene, ma non so cosa stia facendo.

enter image description here

io non sono bravo alla matematica così è stato alla ricerca di idee su codice o librerie che ho potuto solo usare esistente.

Ho cercato sul Web ma non ho trovato nulla di utile: mi sarebbe piaciuto un po 'di aiuto.

+0

Sembra dai post che ImageMagick è l'unico modo per la comunità .NET di regolare le immagini. Peccato davvero come mi sarebbe piaciuto vedere una soluzione nativa C# al problema. Come ho detto, è una "fotocamera digitale" (Not a WebCam) in esecuzione 24 ore su 24, 7 giorni su 7, quindi non elaborare una serie di immagini alla fine della giornata ma in tempo reale. Abbastanza onestamente mi sarei aspettato alcune soluzioni di codice sorgente e non tutte le risposte refeering a ImageMagik che è un buon prodotto ma non è davvero una soluzione di codice sorgente. – Chris

+0

Comprendere. Ho aggiunto un collegamento ad un codice sorgente, ma sinceramente non l'ho testato. Gli esempi sul sito sembrano proprio come la tua immagine originale, però. – beroe

risposta

2

Sembra che il bilanciamento del bianco sia impostato per interni (con luce rossastra), ma con luce diurna (blu). GIMP ha un cursore della temperatura del colore che cambierà il cast delle immagini. Stai parlando di prevenire questo in futuro, o di elaborare in batch un mucchio di immagini esistenti. Anche le fotocamere semplici (ma forse non i telefoni cellulari) hanno controlli sul bilanciamento del bianco, per gli scatti futuri.

Sembra una webcam collegata al computer?Quindi è probabilmente un bersaglio mobile, il che significa che il WB viene rivalutato ogni volta che scatta una foto e potresti non essere in grado di applicare la stessa correzione a ciascuna immagine.

Here è uno script imagemagick che può elaborare in batch la temperatura del colore su un gruppo di immagini. Penso che un approccio che utilizza la temperatura sarebbe migliore di uno che sta solo normalizzando i livelli, perché se si sta scattando il cielo o l'oceano e lo dovrebbe essere blu?? Vuoi solo assicurarti che sia il blu giusto.

Modifica: per codice C# specifico, è possibile check here. L'esempio in basso a sinistra nel primo set di immagini a colori bilanciati assomiglia molto alla tua immagine sorgente. C'è anche una funzione di bilanciamento del bianco nel codice sorgente di paint.net

4

sintonia bilanciamento del bianco automatico non può sempre lavorare bello, perché l'algoritmo avrà meno informazioni come dati di input (nessuna vera misurazione della luce, solo i valori dei pixel registrati da matrice, alcuni dei quali possono essere ritagliati). Quindi questo può aiutare quando le impostazioni della fotocamera sono assurdamente sbagliate (come nella foto), ma non possono rendere WB giusto. Faresti meglio ad acquistare una fotocamera decente (ce ne sono anche di meno costose che possono ancora fare belle foto)

BTW, se vuoi inventare una ruota, l'idea è di scalare i canali di colore per rendere il loro livello medio uguale. Puoi provare diverse definizioni di "media" qui, e puoi anche provare a escludere i pixel con i valori ritagliati dalla misurazione. Ma non c'è divertimento nel farlo di nuovo, in quanto ci sono buoni modi per farlo menzionato nella risposta di @ mickro.

1

È possibile utilizzare OpenCV per sviluppare un algoritmo che soddisfi le proprie esigenze. Quando ho cercato di trovare una soluzione per il tuo problema, ho capito che il problema del "bilanciamento del colore poteva essere risolto in un modo molto diverso.

Ho scelto di mostrarti come codificare un algoritmo molto semplice che non sarà completamente -creare l'immagine "perfetta" che ottieni con photoshop ma qualcosa di meglio dell'originale.Poi puoi cercare questi argomenti in openCV su google e provare un approccio diverso.Per codificare questo, ho usato il nuovo pacchetto OpenCV NuGet, che tu .! può ottenere here Basta aggiungere il binario da OpenCV nella vostra directory di output (cartella di debug) e si è installato e funzionante

Poi ecco il codice:

public Form1() 
{ 
    InitializeComponent(); 

    NamedWindow windowsOriginal = new NamedWindow("Original"); 
    NamedWindow windowsModified = new NamedWindow("Modified"); 

    IplImage img = OpenCV.Net.CV.LoadImage(@"D:\hZpWG.jpg", LoadImageFlags.Color); 
    IplImage imgDest = equalizeIntensity(img); 


    windowsOriginal.ShowImage(img); 
    windowsModified.ShowImage(imgDest); 
} 

IplImage equalizeIntensity(IplImage inputImage) 
{ 
    if(inputImage.Channels >= 3) 
    { 
     IplImage ycrcb = new IplImage(inputImage.Size, inputImage.Depth, inputImage.Channels); 

     OpenCV.Net.CV.CvtColor(inputImage, ycrcb, ColorConversion.Bgr2YCrCb); 

     IplImage Y = new IplImage(ycrcb.Size, IplDepth.U8, 1); 
     IplImage Cr = new IplImage(ycrcb.Size, IplDepth.U8, 1); 
     IplImage Cb = new IplImage(ycrcb.Size, IplDepth.U8, 1); 
     OpenCV.Net.CV.Split(ycrcb, Y, Cr, Cb, null); 

     OpenCV.Net.CV.EqualizeHist(Y, Y); 

     IplImage result = new IplImage(inputImage.Size, IplDepth.U8, inputImage.Channels); 
     OpenCV.Net.CV.Merge(Y, Cr, Cb, null, ycrcb); 

     OpenCV.Net.CV.CvtColor(ycrcb, result, ColorConversion.YCrCb2Bgr); 

     return result; 
    } 

    return null; 
} 

L'ho inserito in un modulo ma è possibile utilizzarlo anche in un'applicazione console.

Ecco il risultato Original enter image description here

Speranza che aiuta!

0

Creare un istogramma, generare automaticamente i livelli corretti (max, min e gamma), applicare i livelli all'immagine. Supponendo di aver in qualche modo raccogliere i tuoi dati dei pixel in una matrice di tipo Color ...

public static Color[] AutoLevel(Color[] input) { 
    var histogram = new Histogram(); 
    foreach(var _ in input) histogram.Add(_); 
    var levels = histogram.GetAutoLevels(); 
    var ret = new Color[input.Length]; 
    for(int _ = 0; _ < input.Length; _++) { 
     ret[_] = levels.Apply(input[_]).ToColor(); 
    } 
    return ret; 
} 

... e qui sta la classe ...

public class Histogram { 
    private long[,] _values = new long[3, 256]; 

    public void AddColor(Color color) { 
     AddColor(color.R, color.G, color.B); 
    } 

    public void AddColor(RGB color) { 
     AddColor(color.R, color.G, color.B); 
    } 

    public void AddColor(byte r, byte g, byte b) { 
     _values[0, b]++; 
     _values[1, g]++; 
     _values[2, b]++; 
    } 

    public long this[int channel, int index] { 
     get { return _values[channel, index]; } 
    } 

    public long GetMaxValue() { 
     var ret = long.MinValue; 
     foreach(var _ in _values) if(_ > ret) ret = _; 
     return ret; 
    } 

    public RGB GetMeanColor() { 
     var total = new long[3]; 
     var count = new long[3]; 
     var value = new byte[3]; 
     for(var _ = 0; _ < 3; _++) { 
      for(var __ = 0; __ < 256; __++) { 
       total[_] += (_values[_, __] * __); 
       count[_] += _values[_, __]; 
      } 
      value[_] = (byte)Math.Round((double)total[_]/count[_]); 
     } 
     return new RGB(value[2], value[1], value[0]); 
    } 

    public RGB GetPercentileColor(double percentile) { 
     var ret = new RGB(); 
     for(var _ = 0; _ < 3; _++) { 
      var total = 0L; 
      for(var __ = 0; __ < 256; __++) total += _values[_, __]; 
      var cutoff = (total * percentile); 
      var count = 0L; 
      for(var __ = 0; __ < 256; __++) { 
       count += _values[_, __]; 
       if(count > cutoff) { 
        ret[_] = (byte)__; 
        break; 
       } 
      } 
     } 
     return ret; 
    } 

    public Levels GetAutoLevels() { 
     var low = GetPercentileColor(0.005); 
     var middle = GetMeanColor(); 
     var high = GetPercentileColor(0.995); 
     return Levels.GetAdjusted(low, middle, high); 
    } 


    public class Levels { 
     private RGB _inputLow = new RGB(0, 0, 0); 
     private RGB _inputHigh = new RGB(255, 255, 255); 
     private RGB _outputLow = new RGB(0, 0, 0); 
     private RGB _outputHigh = new RGB(255, 255, 255); 
     private double[] _gamma = { 1, 1, 1 }; 

     public RGB InputLow { 
      get { return _inputLow; } 
      set { 
       for(var _ = 0; _ < 3; _++) { 
        if(value[_] == 255) value[_] = 254; 
        if(_inputHigh[_] <= value[_]) _inputHigh[_] = (byte)(value[_] + 1); 
       } 
       _inputLow = value; 
      } 
     } 

     public RGB InputHigh { 
      get { return _inputHigh; } 
      set { 
       for(var _ = 0; _ < 3; _++) { 
        if(value[_] == 0) value[_] = 1; 
        if(_inputLow[_] >= value[_]) _inputLow[_] = (byte)(value[_] - 1); 
       } 
       _inputHigh = value; 
      } 
     } 

     public RGB OutputLow { 
      get { return _outputLow; } 
      set { 
       for(var _ = 0; _ < 3; _++) { 
        if(value[_] == 255) value[_] = 254; 
        if(_outputHigh[_] <= value[_]) _outputHigh[_] = (byte)(value[_] + 1); 
       } 
       _outputLow = value; 
      } 
     } 

     public RGB OutputHigh { 
      get { return _outputHigh; } 
      set { 
       for(var _ = 0; _ < 3; _++) { 
        if(value[_] == 0) value[_] = 1; 
        if(_outputLow[_] >= value[_]) _outputLow[_] = (byte)(value[_] - 1); 
       } 
       _outputHigh = value; 
      } 
     } 

     public double GetGamma(int channel) { 
      return _gamma[channel]; 
     } 

     public void SetGamma(int channel, double value) { 
      _gamma[channel] = SetRange(value, 0.1, 10); 
     } 

     public RGB Apply(int r, int g, int b) { 
      var ret = new RGB(); 
      var input = new double[] { b, g, r }; 
      for(var _ = 0; _ < 3; _++) { 
       var value_ = (input[_] - _inputLow[_]); 
       if(value_ < 0) { 
        ret[_] = _outputLow[_]; 
       } else if((_inputLow[_] + value_) >= _inputHigh[_]) { 
        ret[_] = _outputHigh[_]; 
       } else { 
        ret[_] = (byte)SetRange((_outputLow[_] + ((_outputHigh[_] - _outputLow[_]) * Math.Pow((value_/(_inputHigh[_] - _inputLow[_])), _gamma[_]))), 0, 255); 
       } 
      } 
      return ret; 
     } 

     internal static Levels GetAdjusted(RGB low, RGB middle, RGB high) { 
      var ret = new Levels(); 
      for(var _ = 0; _ < 3; _++) { 
       if((low[_] < middle[_]) && (middle[_] < high[_])) { 
        ret._gamma[_] = SetRange(Math.Log(0.5, ((double)(middle[_] - low[_])/(high[_] - low[_]))), 0.1, 10); 
       } else { 
        ret._gamma[_] = 1; 
       } 
      } 
      ret._inputLow = low; 
      ret._inputHigh = high; 
      return ret; 
     } 
    } 

    private static double SetRange(double value, double min, double max) { 
     if(value < min) value = min; 
     if(value > max) value = max; 
     return value; 
    } 



    public struct RGB { 
     public byte B; 
     public byte G; 
     public byte R; 

     public RGB(byte r, byte g, byte b) { 
      B = b; 
      G = g; 
      R = r; 
     } 

     public byte this[int channel] { 
      get { 
       switch(channel) { 
        case 0: return B; 
        case 1: return G; 
        case 2: return R; 
        default: throw new ArgumentOutOfRangeException(); 
       } 
      } 
      set { 
       switch(channel) { 
        case 0: B = value; break; 
        case 1: G = value; break; 
        case 2: R = value; break; 
        default: throw new ArgumentOutOfRangeException(); 
       } 
      } 
     } 

     public Color ToColor() { 
      return Color.FromArgb(R, G, B); 
     } 
    } 
} 

Risultati:
Results

Problemi correlati