31 votes

Une amélioration automatique des images numérisées

Je suis de développer une routine pour une amélioration automatique de numérisées de diapositives 35 mm. Je suis à la recherche d'un bon algorithme pour augmenter le contraste et la suppression de la dominante de couleur. L'algorithme devra être complètement automatique, car il y aura des milliers d'images à traiter. Ce sont quelques exemples d'images directement à partir du scanner, seulement recadrée et réduit pour le web:

A_CroppedB_Cropped

Je suis à l'aide de la AForge.NET bibliothèque et ont essayé les deux, l' HistogramEqualization et ContrastStretch filtres. HistogramEqualization est bon pour maximiser le contraste local, mais ne produisent pas de résultats satisfaisants dans l'ensemble. ContrastStretch est bien mieux, mais depuis il s'étend de l'histogramme de chaque bande de couleur individuellement, il produit parfois une forte dominante de couleur:

A_Stretched

Afin de réduire le décalage de couleur, j'ai créé un UniformContrastStretch filtre à moi-même à l'aide de l' ImageStatistics et LevelsLinear les classes. Il utilise la même gamme pour toutes les bandes de couleur, en préservant les couleurs à charge de moins de 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);

A_UniformStretched

L'image est encore assez bleu, donc j'ai créé un ColorCorrection filtre qui calcule d'abord la moyenne de la luminance de l'image. Une correction gamma valeur est ensuite calculé pour chaque canal de couleur, de sorte que la valeur moyenne de chaque canal de couleur sera égale à la luminance moyenne. L'uniforme contraste de l'image redimensionnée a valeurs moyennes R=70 G=64 B=93, la luminance moyenne étant (70 + 64 + 93) / 3 = 76. Le gamma valeurs sont calculées à l' R=1.09 G=1.18 B=0.80 et le résultat, très neutre, l'image a des valeurs moyennes de R=76 G=76 B=76 comme prévu:

A_UniformStretchedCorrected

Maintenant, arriver à la véritable problème... je suppose que la correction de la moyenne de la couleur de l'image en gris est un peu trop drastique et faire quelques images assez terne en apparence, comme le second échantillon (première image est uniforme tendu, côté est de la même couleur de l'image corrigée):

B_UniformStretchedB_UniformStretchedCorrected

Une façon de corriger les couleurs manuellement dans un programme d'édition de photos est à l'échantillon de la couleur d'une couleur neutre (blanc/gris/noir) et de régler le reste de l'image pour que. Mais depuis cette routine doit être complètement automatique, qui n'est pas une option.

Je suppose que je pourrais ajouter une force de réglage à ma ColorCorrection filtre, de sorte qu'une force de 0.5 déplacer la moyenne des valeurs de la moitié de la distance à la luminance moyenne. Mais d'un autre côté, certaines images peuvent faire de mieux, sans aucune correction de la couleur à tous.

Des idées pour un meilleur algorithme? Ou une méthode pour détecter si une image a une dominante de couleur ou tout simplement a beaucoup de couleur, comme le deuxième exemple?

2voto

orrollo Points 201
  • traduit de hsv
  • V-couche est corrigée par la mise à l'échelle les valeurs de (min,max) plage de (0,255) gamme
  • assemblé en arrière rvb
  • la correction de R,G,B des couches de suite par la même idée que le V-couche de la deuxième étape

il n'y a pas de aforge.net code, car il traité par php prototype de code, mais autant que je sache, il n'y a aucun problème à faire avec aforge.net. les résultats sont les suivants:

enter image description hereenter image description here

2voto

Shreyas Kapur Points 298

Convertir votre RVB à la LGV, en utilisant ceci:

    System.Drawing.Color color = System.Drawing.Color.FromArgb(red, green, blue);
    float hue = color.GetHue();
    float saturation = color.GetSaturation();
    float lightness = color.GetBrightness();

Régler votre la Saturation et la luminosité en conséquence

Convertir LGV retour à RVB par:

/// <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
  // mundhenk@usc.edu
  // 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;
}

Code D'Origine:

1voto

Alceu Costa Points 3045

Afin d'éviter de changer la couleur de votre image lors de l'étirement du contraste, de le convertir d'abord à HSV/espace de couleurs TSL. Ensuite, appliquez régulièrement contraste qui s'étend dans la L ou V canal, mais ne pas chagen H ou S canaux.

1voto

Jim Lindstrom Points 206

J'avais besoin de faire la même chose sur une grande bibliothèque de vignettes vidéo. Je voulais une solution qui pourrait être conservateur, alors que je n'ai pas de contrôle sur place pour les vignettes se faire complètement saccagé. Voici le désordre, piraté ensemble la solution que j'ai utilisée.

J'ai d'abord utilisé cette classe pour calculer la répartition des couleurs dans une image. J'ai fait un dans le HSV-palette, mais a trouvé un niveau de gris à base de l'un a été plus rapide et presque aussi bon:

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

Ensuite, j'ai créé cette catégorie à utiliser l'histogramme de la figure comment corriger une image:

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

J'ai couru comme suit:

origThumbs = `find thumbs | grep jpg`.split("\n")
origThumbs.each do |origThumb|
  newThumb = origThumb.gsub(/thumb/, "newthumb")
  imgCorrector = ImgCorrector.new(origThumb)
  imgCorrector.writeCorrected(newThumb)
end

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X