c# - poner - Mejora automática de imágenes escaneadas
no me funcionan los hashtags en instagram 2018 (4)
Convierta su RGB a HSL usando esto:
System.Drawing.Color color = System.Drawing.Color.FromArgb(red, green, blue);
float hue = color.GetHue();
float saturation = color.GetSaturation();
float lightness = color.GetBrightness();
Ajuste su Saturación y Luminosidad en consecuencia
Convierte HSL a RGB de la siguiente manera:
/// <summary>
/// Convert HSV to RGB
/// h is from 0-360
/// s,v values are 0-1
/// r,g,b values are 0-255
/// Based upon http://ilab.usc.edu/wiki/index.php/HSV_And_H2SV_Color_Space#HSV_Transformation_C_.2F_C.2B.2B_Code_2
/// </summary>
void HsvToRgb(double h, double S, double V, out int r, out int g, out int b)
{
// ######################################################################
// T. Nathan Mundhenk
// [email protected]
// C/C++ Macro HSV to RGB
double H = h;
while (H < 0) { H += 360; };
while (H >= 360) { H -= 360; };
double R, G, B;
if (V <= 0)
{ R = G = B = 0; }
else if (S <= 0)
{
R = G = B = V;
}
else
{
double hf = H / 60.0;
int i = (int)Math.Floor(hf);
double f = hf - i;
double pv = V * (1 - S);
double qv = V * (1 - S * f);
double tv = V * (1 - S * (1 - f));
switch (i)
{
// Red is the dominant color
case 0:
R = V;
G = tv;
B = pv;
break;
// Green is the dominant color
case 1:
R = qv;
G = V;
B = pv;
break;
case 2:
R = pv;
G = V;
B = tv;
break;
// Blue is the dominant color
case 3:
R = pv;
G = qv;
B = V;
break;
case 4:
R = tv;
G = pv;
B = V;
break;
// Red is the dominant color
case 5:
R = V;
G = pv;
B = qv;
break;
// Just in case we overshoot on our math by a little, we put these here. Since its a switch it won''t slow us down at all to put these here.
case 6:
R = V;
G = tv;
B = pv;
break;
case -1:
R = V;
G = pv;
B = qv;
break;
// The color is not defined, we should throw an error.
default:
//LFATAL("i Value error in Pixel conversion, Value is %d", i);
R = G = B = V; // Just pretend its black/white
break;
}
}
r = Clamp((int)(R * 255.0));
g = Clamp((int)(G * 255.0));
b = Clamp((int)(B * 255.0));
}
/// <summary>
/// Clamp a value to 0-255
/// </summary>
int Clamp(int i)
{
if (i < 0) return 0;
if (i > 255) return 255;
return i;
}
Código original:
Estoy desarrollando una rutina para la mejora automática de diapositivas escaneadas de 35 mm. Estoy buscando un buen algoritmo para aumentar el contraste y eliminar el matiz de color. El algoritmo tendrá que ser completamente automático, ya que habrá miles de imágenes para procesar. Estas son un par de imágenes de muestra directamente del escáner, solo recortadas y reducidas para la web:
Estoy usando la biblioteca AForge.NET y he probado los filtros HistogramEqualization
y ContrastStretch
. HistogramEqualization
es buena para maximizar el contraste local, pero no produce resultados agradables en general. ContrastStretch
es mucho mejor, pero como estira el histograma de cada banda de color individualmente, a veces produce un fuerte tono de color:
Para reducir el cambio de color, creé un filtro LevelsLinear
utilizando las clases ImageStatistics
y LevelsLinear
. Utiliza el mismo rango para todas las bandas de color, preservando los colores a expensas de menos contraste.
ImageStatistics stats = new ImageStatistics(image);
int min = Math.Min(Math.Min(stats.Red.Min, stats.Green.Min), stats.Blue.Min);
int max = Math.Max(Math.Max(stats.Red.Max, stats.Green.Max), stats.Blue.Max);
LevelsLinear levelsLinear = new LevelsLinear();
levelsLinear.Input = new IntRange(min, max);
Bitmap stretched = levelsLinear.Apply(image);
La imagen sigue siendo bastante azul, así que creé un filtro ColorCorrection
que primero calcula la luminancia media de la imagen. A continuación, se calcula un valor de corrección gamma para cada canal de color, de modo que el valor medio de cada canal de color sea igual a la luminancia media. La imagen estirada de contraste uniforme tiene valores medios R=70 G=64 B=93
, siendo la luminancia media (70 + 64 + 93) / 3 = 76
. Los valores gamma se calculan para R=1.09 G=1.18 B=0.80
y la imagen resultante, muy neutral, tiene valores medios de R=76 G=76 B=76
como se esperaba:
Ahora, llegando al problema real ... Supongo que corregir el color medio de la imagen al gris es un poco demasiado drástico y hará que algunas imágenes sean bastante aburridas en apariencia, como la segunda muestra (la primera imagen se estira uniformemente, luego está el mismo color de imagen corregido):
Una forma de realizar la corrección de color manualmente en un programa de edición de fotografías es muestrear el color de un color neutro conocido (blanco / gris / negro) y ajustar el resto de la imagen a eso. Pero como esta rutina debe ser completamente automática, no es una opción.
Supongo que podría agregar una configuración de resistencia a mi filtro ColorCorrection
, de modo que una fuerza de 0.5 moverá los valores medios la mitad de la distancia a la luminancia media. Pero, por otro lado, algunas imágenes podrían funcionar mejor sin ninguna corrección de color.
¿Alguna idea para un algoritmo mejor? ¿O algún método para detectar si una imagen tiene un tinte de color o simplemente tiene mucho color, como la segunda muestra?
Necesitaba hacer lo mismo en una gran biblioteca de miniaturas de videos. Quería una solución que fuera conservadora, para que no tuviese que controlar que las miniaturas quedaran completamente destrozadas. Aquí está la solución desordenada y hackeada que utilicé.
Primero utilicé esta clase para calcular la distribución de colores en una imagen. Primero hice uno en el espacio de color HSV, pero encontré que uno basado en escala de grises era mucho más rápido y casi tan bueno:
class GrayHistogram
def initialize(filename)
@hist = hist(filename)
@percentile = {}
end
def percentile(x)
return @percentile[x] if @percentile[x]
bin = @hist.find{ |h| h[:count] > x }
c = bin[:color]
return @percentile[x] ||= c/256.0
end
def midpoint
(percentile(0.25) + percentile(0.75)) / 2.0
end
def spread
percentile(0.75) - percentile(0.25)
end
private
def hist(imgFilename)
histFilename = "/tmp/gray_hist.txt"
safesystem("convert #{imgFilename} -depth 8 -resize 50% -colorspace GRAY /tmp/out.png")
safesystem("convert /tmp/out.png -define histogram:unique-colors=true " +
" -format /"%c/" histogram:info:- > #{histFilename}")
f = File.open(histFilename)
lines = f.readlines[0..-2] # the last line is always blank
hist = lines.map { |line| { :count => /([0-9]*):/.match(line)[1].to_i, :color => /,([0-9]*),/.match(line)[1].to_i } }
f.close
tot = 0
cumhist = hist.map do |h|
tot += h[:count]
{:count=>tot, :color=>h[:color]}
end
tot = tot.to_f
cumhist.each { |h| h[:count] = h[:count] / tot }
safesystem("rm /tmp/out.png #{histFilename}")
return cumhist
end
end
Luego creé esta clase para usar el histograma para descubrir cómo corregir una imagen:
def safesystem(str)
out = `#{str}`
if $? != 0
puts "shell command failed:"
puts "/tcmd: #{str}"
puts "/treturn code: #{$?}"
puts "/toutput: #{out}"
raise
end
end
def generateHist(thumb, hist)
safesystem("convert #{thumb} histogram:hist.jpg && mv hist.jpg #{hist}")
end
class ImgCorrector
def initialize(filename)
@filename = filename
@grayHist = GrayHistogram.new(filename)
end
def flawClass
if !@flawClass
gapLeft = (@grayHist.percentile(0.10) > 0.13) || (@grayHist.percentile(0.25) > 0.30)
gapRight = (@grayHist.percentile(0.75) < 0.60) || (@grayHist.percentile(0.90) < 0.80)
return (@flawClass="low" ) if (!gapLeft && gapRight)
return (@flawClass="high" ) if ( gapLeft && !gapRight)
return (@flawClass="narrow") if ( gapLeft && gapRight)
return (@flawClass="fine" )
end
return @flawClass
end
def percentileSummary
[ @grayHist.percentile(0.10),
@grayHist.percentile(0.25),
@grayHist.percentile(0.75),
@grayHist.percentile(0.90) ].map{ |x| (((x*100.0*10.0).round)/10.0).to_s }.join('', '') +
"<br />" +
"spread: " + @grayHist.spread.to_s
end
def writeCorrected(filenameOut)
if flawClass=="fine"
safesystem("cp #{@filename} #{filenameOut}")
return
end
# spread out the histogram, centered at the midpoint
midpt = 100.0*@grayHist.midpoint
# map the histogram''s spread to a sigmoidal concept (linearly)
minSpread = 0.10
maxSpread = 0.60
minS = 1.0
maxS = case flawClass
when "low" then 5.0
when "high" then 5.0
when "narrow" then 6.0
end
s = ((1.0 - [[(@grayHist.spread - minSpread)/(maxSpread-minSpread), 0.0].max, 1.0].min) * (maxS - minS)) + minS
#puts "s: #{s}"
safesystem("convert #{@filename} -sigmoidal-contrast #{s},#{midpt}% #{filenameOut}")
end
end
Lo ejecuté así:
origThumbs = `find thumbs | grep jpg`.split("/n")
origThumbs.each do |origThumb|
newThumb = origThumb.gsub(/thumb/, "newthumb")
imgCorrector = ImgCorrector.new(origThumb)
imgCorrector.writeCorrected(newThumb)
end
Para evitar cambiar el color de su imagen al estirar el contraste, conviértalo primero a un espacio de color HSV / HSL. Luego, aplique un estiramiento de restricción regular en el canal L o V pero no en los canales H o S de chagen.
- traducido a hsv
- La capa V se corrige al escalar valores del rango (mínimo, máximo) al rango (0,255)
- ensamblado de nuevo a rgb
- corrigiendo las capas de resultado R, G, B por la misma idea que la capa V en el segundo paso
no hay un código de aforge.net, porque procesado por el código prototipo php, pero afaik no hay ningún problema para hacer eso con aforge.net. los resultados son: