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:
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
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