2017-07-22 1 views
1

Ich habe tagelang gehunkelt, um herauszufinden, was mit meiner nächsten Nachbarinterpolation nicht stimmt, wenn ich ein Bild halb so groß skaliere. Im mit Rust, aber ich glaube nicht, dass so viel wie der Algorithmus zählt:Was ist falsch mit diesem Algorithmus für den nächsten Nachbarn zur Bildskalierung?

/// Interpolate using nearest neighbor. 
pub fn nearest(mut src: &mut Image, w2: i32, h2: i32) -> RasterResult<()> { 

    // Compute scale 
    let x_scale: f64 = src.width as f64/w2 as f64; 
    let y_scale: f64 = src.height as f64/h2 as f64; 

    // Create destination image 
    let mut dest = Image::blank(w2, h2); 

    // Loop thru destination image's pixels 
    for y2 in 0..h2 { 
     for x2 in 0..w2 { 

      // Get location from src using nearest neighbor 
      let x1: i32 = (x2 as f64 * x_scale).floor() as i32; 
      let y1: i32 = (y2 as f64 * y_scale).floor() as i32; 
      let pixel = try!(src.get_pixel(x1, y1)); 

      // Apply the pixels 
      try!(dest.set_pixel(x2, y2, pixel)); 
     } 
    } 

    // Update the source image 
    src.width = dest.width; 
    src.height = dest.height; 
    src.bytes = dest.bytes; 

    Ok(()) 
} 

Meine Telefonvorwahl:

// Open 
let mut image = raster::open(r"test.png").unwrap(); 

// Resize to half 
raster::interpolate::nearest(&mut image, 256, 128).unwrap(); 

// Save 
raster::save(&image, r"out.png").unwrap(); 

Es funktioniert gut für normale Bilder aber nicht auf Sonderfälle. Hier ist das Testbild: test image

zur Hälfte mit meinem Code Scaled:

scaled to half with my code

Zum Vergleich hier ist Photoshop:

Scaled to half with Photoshop's

ich mit GD getestet und es ergibt ähnliche Ergebnisse wie Photoshop.

Meine Untersuchung führt mich zu glauben, dass meine Berechnung der Quellpixel (rote Quadrate) von einem aus ist im Vergleich zu den anderen (grünen Quadraten):

zoom of problem area

Ich habe auch versucht into GD's source code die Suche nach bekomme einen Hinweis, aber ich verstehe nicht ganz, was es tut.

Um zu verdeutlichen, spreche ich nicht über eine andere Interpolationsmethode wie Bilinear und Bikubic. Ich vergleiche meinen nächsten Nachbaralgorithmus und sein Ergebnis mit dem Ergebnis von Photoshop und dem nächsten Nachbaralgorithmus von GD. Ich frage nicht, warum meine Ergebnisse in Photoshop hässlich sind, weil ich den nächsten Nachbaralgorithmus verwendet habe.

(Testbild mit freundlicher Genehmigung von Jonas Berlin über Eric Brasseur)

+1

Sie haben ein Testbild verwendet, das zeigen soll, dass einfache Skalierungsalgorithmen wie der nächste Nachbar unzureichend sind. Und das hat sich tatsächlich gezeigt. Alles funktioniert wie beabsichtigt. Wenn Sie bessere Ergebnisse wünschen, verwenden Sie einen besseren Skalierungsalgorithmus. – interjay

+0

@interjay - Nein. Ich denke, du verstehst die Frage falsch. Ich habe den nächsten Nachbarn in Photoshop und GD verwendet und die Ergebnisse unterscheiden sich mit meinem nächsten Nachbaralgorithmus. Ich frage nicht nach einer anderen Interpolationsmethode. – kosinix

Antwort

1

Ein etwas besseren Weg nächste Nachbar-Interpolation zu tun ist, um die Interpolation über die Zentren des Zielpixels statt ihrer linken oberen Ecke gehen zu machen. In Ihrem Code bedeutet dies 0.5 zu x2 und y2 bei der Berechnung x1 und y1 hinzuzufügen.

let x1: i32 = ((x2 as f64 + 0.5) * x_scale).floor() as i32; 
let y1: i32 = ((y2 as f64 + 0.5) * y_scale).floor() as i32; 

Wenn Sie von 1/2 skaliert nicht zwangsläufig ein besseres Ergebnis geben: jedes Zielpixel aus einer 2x2-Source-Bereich gewählt wird, so dass es keine Rolle spielt, welche Quelle Pixel Sie wählen. Wenn Sie jedoch 1/3 skalieren, bedeutet dies, dass Sie die Mitte eines 3x3-Bereichs anstelle von oben links wählen, was besser ist.

Photoshop verwendet wahrscheinlich eine Methode wie diese, weshalb Sie unterschiedliche Ergebnisse sehen.

Beachten Sie, dass auch wenn das Testbild dadurch besser skaliert erscheint, ein anderes Testbild erstellt werden kann, das mit dem neuen Algorithmus schlecht aussieht. Es wird wahrscheinlich ausreichen, Ihr Testbild um (1,1) zu versetzen.

0

Ihr Algorithmus ist nicht falsch, unbedingt. Es hängt davon ab, wie Sie Ihre Koordinaten definieren.Die geforderte Skalierung ist eine Halbierung der Bildauflösung, was bedeutet, dass für jedes Zielpixel vier Pixel zu berücksichtigen sind; Sie sind äquidistant und decken ein Viertel jedes Zielpixels ab. Daher ist "am nächsten" keine nützliche Metrik, um die Wahl zu treffen. Sie könnten immer noch den nächsten Nachbarn beanspruchen, selbst wenn die Auswahl zufällig pro Pixel wäre, aber das wäre eine schlechte Implementierung, weil es eine verrauschte Verteilung verwenden würde.

Das Bild in diesem Fall soll zeigen, welche Wahl tatsächlich getroffen wurde. Einige Neuskalierungsprozeduren haben bemerkenswerte Entscheidungen getroffen, insbesondere einige Versionen von Microsoft Internet Explorer, und es kann zu seltsamen Verzerrungen führen. Zum Beispiel, in VIPS, wenn ich den nächsten Nachbarn Downscaling auf das Bild anwenden, bekomme ich Ihr Ergebnis (voreingestellt auf die dunkleren Pixel), aber wenn ich zuerst einen Ein-Pixel-Diagonalversatz anwende (durch Auswahl einer ungeraden Offset-Region) Holen Sie sich das Photoshop-Ergebnis (voreingestellt auf die helleren Pixel). Dies ist lediglich ein Ergebnis der Rundung um 0,5 nach oben oder unten. Sie können entweder korrekter sein, indem Sie definieren, wo die Subpixelkoordinaten eintreffen, dh ob die Koordinaten eines gegebenen Pixels in einer Ecke oder in der Mitte dieses Pixels liegen.

Das Bild scheint nicht für den nächsten Nachbarn geeignet zu sein, sondern für lineare Interpolation. Viele Bildskalierer nehmen fälschlicherweise an, dass der Farbraum linear ist und ein gerader Mittelwert von Pixelwerten verwendet werden kann, aber unsere Sicht ist grob logarithmisch, so dass beliebte Farbräume wie sRGB eine Gammakurve verwenden, um sie besser zu approximieren.

+0

ich möchte das algo ähnlich wie andere bildbearbeitungsprogramme, vorzugsweise mit photoshop und imagemagick, arbeiten lassen. Dies soll die Benutzer nicht überraschen, da die Ergebnisse anders sind als das, was diese anderen Bibliotheken tun. Ja, das Testbild ist für Gamma, aber ich entschied mich, es hier zu verwenden, da es gut funktioniert, dieses subtile Problem hervorzuheben. Danke für die Antwort. – kosinix

Verwandte Themen