2012-03-26 18 views
3

Ich versuche zu tun, was ich ursprünglich dachte, wäre ziemlich einfach. Nämlich:Wiederholte Aufrufe von image.png.Decode() führt zu Fehlern, die nicht genügend Speicher haben

Für jede Datei in der Liste der Eingabedateien:

  1. die Datei mit png.Decode öffnen()
  2. scannen jedes Pixel in der Datei und Test, um zu sehen, ob es „grau ist ".
  3. Gibt den Prozentsatz der "grauen" Pixel im Bild zurück.

Dies ist die Funktion, die ich anrufen bin:

func greyLevel(fname string) (float64, string) { 
    f, err := os.Open(fname) 
    if err != nil { 
      return -1.0, "can't open file" 
    } 
    defer f.Close() 

    i, err := png.Decode(f) 
    if err != nil { 
      return -1.0, "unable to decode" 
    } 

    bounds := i.Bounds() 

    var lo uint32 = 122 // Low grey RGB value. 
    var hi uint32 = 134 // High grey RGB value. 
    var gpix float64 // Grey pixel count. 
    var opix float64 // Other (non-grey) pixel count. 
    var tpix float64 // Total pixels. 

    for x := bounds.Min.X; x < bounds.Max.X; x++ { 
      for y := bounds.Min.Y; y < bounds.Max.Y; y++ { 
        r, g, b, _ := i.At(x, y).RGBA() 
        if ((r/255)-1 > lo && (r/255)-1 < hi) && 
          ((g/255)-1 > lo && (g/255)-1 < hi) && 
          ((b/255)-1 > lo && (b/255)-1 < hi) { 
          gpix++ 
        } else { 
          opix++ 
        } 
        tpix++ 
      } 
    } 
    return (gpix/tpix) * 100, "" 
} 

func main() { 
    srcDir := flag.String("s", "", "Directory containing image files.") 
    threshold := flag.Float64("t", 65.0, "Threshold (in percent) of grey pixels.") 
    flag.Parse() 

    dirlist, direrr := ioutil.ReadDir(*srcDir) 
    if direrr != nil { 
      log.Fatalf("Error reading %s: %s\n", *srcDir, direrr) 
    } 

    for f := range dirlist { 
      src := path.Join(*srcDir, dirlist[f].Name()) 

      level, msg := greyLevel(src) 

      if msg != "" { 
        log.Printf("error processing %s: %s\n", src, msg) 
        continue 
      } 

      if level >= *threshold { 
        log.Printf("%s is grey (%2.2f%%)\n", src, level) 
      } else { 
        log.Printf("%s is not grey (%2.2f%%)\n", src, level) 
      } 
    } 
} 

Die Dateien relativ klein sind (960x720, 8-bit RGB)

I ioutil.ReadDir nenne(), um eine Liste zu erzeugen, von Dateien, Schleifen über die Scheibe und Aufruf von GreyLevel().

Nach etwa 155 Dateien (aus einer Liste von> 4000) das Skript panics mit:

runtime: memory allocated by OS not in usable range 
runtime: out of memory: cannot allocate 2818048-byte block (534708224 in use) 
throw: out of memory 

ich meine, es ist etwas einfach mir fehlt. Ich dachte, Go würde den Speicher freigeben, der in greyLevels() zugewiesen wurde, aber ich denke nicht?

Follow up:

Nach dem Einlegen runtime.GC() nach jedem Aufruf an Graustufen, gleicht die Speichernutzung. Letzte Nacht habe ich ungefähr 800 Bilder gemacht und dann aufgehört. Heute lasse ich es über den gesamten Eingabesatz laufen, ca. 6800 Bilder.

Nach 1500 Bilder sieht oben wie folgt aus:

top - 10:30:11 up 41 days, 11:47, 2 users, load average: 1.46, 1.25, 0.88 
Tasks: 135 total, 2 running, 131 sleeping, 1 stopped, 1 zombie 
Cpu(s): 49.8%us, 5.1%sy, 0.2%ni, 29.6%id, 15.0%wa, 0.0%hi, 0.3%si, 0.0%st 
Mem: 3090304k total, 2921108k used, 169196k free,  2840k buffers 
Swap: 3135484k total, 31500k used, 3103984k free, 640676k cached 

    PID USER  PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 
28474 mtw  20 0 2311m 1.8g 412 R 99 60.5 16:48.52 8.out 

Und blieb stabil nach dem anderen 5000 die Bearbeitung von Bildern.

+0

Fügen Sie Go-Version, OS-Plattform und CPU-Architekturinformationen hinzu. Auch der vollständige Code, der das Problem reproduziert, wäre nützlich. – zzzz

+0

@jnml Sie können von "nicht genügend Arbeitsspeicher ... (534708224 in Verwendung)" auf CPU-Architektur schließen. –

+0

@Atom: Intel oder ARM? ;-) – zzzz

Antwort

1

Es scheint, dass Sie eine 32-Bit-Maschine verwenden. Es ist wahrscheinlich, dass das Programm nicht genügend Arbeitsspeicher zur Verfügung hat, da Go's Garbage Collector konservativ ist. Ein konservativer Speicherbereiniger kann möglicherweise nicht erkennen, dass ein bestimmter Speicherbereich nicht mehr verwendet wird. Derzeit gibt es für diese keine Abhilfe in Go anderen Programmen als Datenstrukturen zu vermeiden, dass der Garbage Collector nicht (wie zB: struct {...; binaryData [256]byte}) verarbeiten kann

Try runtime.GC() in jeder Iteration der Schleife zu nennen, in dem Sie anrufen, Funktion greyLevel. Vielleicht hilft es dem Programm, mehr Bilder zu verarbeiten.

Wenn das Aufrufen von runtime.GC() die Situation nicht verbessert, möchten Sie möglicherweise Ihre Strategie so ändern, dass das Programm eine kleinere Anzahl von PNG-Dateien pro Lauf verarbeitet.

+0

runtime.GC() macht den Trick. Der residente Speicher steigt auf 568M und bleibt dann stabil. Schade, dass ein solcher Workaround erforderlich ist, aber ich bin zuversichtlich, dass die Go-Entwickler einen Fix erstellen werden. Vielen Dank! – mtw

+0

Ich glaube nicht, dass die Behauptung, dass GC nicht in der Lage ist, die obige Struktur zu handhaben, und dass einige spezifische Daten in Go vermieden werden müssen, wahr ist (zumindest heutzutage). – zzzz

+0

@mtw: GC sollte automatisch einsteigen. Wenn das Programm erfordert, dass runtime.GC() jemals manuell ausgeführt wird, vermute ich, dass ein Fehler in der Laufzeit auftreten könnte. – zzzz

0

Scheint wie Ausgabe 3173, die vor kurzem behoben wurde. Könntest du es bitte mit der letzten Woche versuchen? (Angenommen, Sie verwenden jetzt eine Version vor 2012-03-07).

+0

OK, cool Ich laufe Version wochenweise gehen 2012-03-22 und jetzt den Prozess reserviert Speicher normalerweise, aber es scheint wie Müll nicht gesammelt wird. – mtw

Verwandte Themen