Implementierung eines Ölgemäldefilters für Bilder

Sep 09 2020

Dies ist die Webübung 3.1.14. aus dem Buch Informatik Ein interdisziplinärer Ansatz von Sedgewick & Wayne:

Stellen Sie Pixel (i, j) auf die Farbe des häufigsten Werts unter Pixeln mit dem Manhattan-Abstand W von (i, j) im Originalbild ein.

Hier ist mein Programm:

import java.awt.Color;

public class OilPaintingFilter
{
    public static int findNumberOfPixels(int distance)
    {
        int numberOfPixels = 4;
        if (distance == 1) return numberOfPixels;
        int increment = 8;
        for (int i = 2; i <= distance; i++)
        {
            numberOfPixels += increment;
            increment += 4;
        }
        return numberOfPixels;
    }
    public static Color[] findNeighboringColors(Picture picture, int col, int row, int distance)
    {
        int width = picture.width();
        int height = picture.height();
        Color[] pixels = new Color[findNumberOfPixels(distance)+1];
        int counter = 0;
        for (int j = col-distance; j <= col+distance; j++)
        {
            for (int i = row-distance; i <= row+distance; i++)
            {
                if ((Math.abs(col-j) + Math.abs(row-i)) <= distance)
                {
                    pixels[counter] = picture.get(Math.abs(j)%width,Math.abs(i)%height);
                    counter++;
                }
            }
        }
        return pixels;
    }  
    public static Color findMostFrequentNeighbor(Color[] pixels)
    {
        int[] colors = new int[pixels.length];
        for (int i = 0; i < pixels.length; i++)
        {
            for (int j = 0; j < pixels.length; j++)
            {
                if (pixels[i].equals(pixels[j]))
                {
                    colors[i]++;
                }
            }
        }
        int max = 0;
        int index = 0;
        for (int i = 0; i < pixels.length; i++)
        {
            if (colors[i] > max)
            {
                max = colors[i];
                index = i;
            }
        }
        return pixels[index];
    }
    public static Picture applyPaintingFilter(Picture picture, int distance)
    {
        int width = picture.width();
        int height = picture.height();
        Picture newPicture = new Picture(width,height);
        for (int col = 0; col < width; col++)
        {
            for (int row = 0; row < height; row++)
            {
                Color newColor = findMostFrequentNeighbor(findNeighboringColors(picture, col, row, distance));
                newPicture.set(col,row,newColor);
            }
        }
        return newPicture;
    }
    public static void main(String[] args)
    {
        Picture picture = new Picture(args[0]);
        int distance = Integer.parseInt(args[1]);
        Picture newPicture = applyPaintingFilter(picture, distance);
        newPicture.show();
    }
}

Picture ist eine einfache API, die von den Autoren des Buches geschrieben wurde. Ich habe mein Programm überprüft und es funktioniert. Hier ist eine Instanz davon:

Eingabe (Bild eines Papageientauchers von Pinterest ):

mit Manhattan Entfernung = 10

Ausgabe:

Gibt es eine Möglichkeit, mein Programm zu verbessern?

Danke für Ihre Aufmerksamkeit.

Antworten

2 IEatBagels Sep 10 2020 at 20:22

Das ist eine ziemlich coole Anwendung und es ist schön, die Ausgabe zu sehen!

Es gibt einige Dinge, die ich behandeln möchte:

  • findNumberOfPixelsAngenommen, Sie drucken die Ausgabe jedes Mal, wenn Sie sie ausführen, und sehen, dass immer dieselbe Nummer ausgegeben wird . Warum? Weil sich distancenie ändert und Ihre Funktion von nichts anderem abhängt. Dies bedeutet, dass Sie, anstatt die "Anzahl der Pixel" jedes Mal zu berechnen, wenn Sie die Farbe für die Pixelintensität I (X, Y) finden möchten, findNumberOfPixelsnur einmal aufrufen und das gleiche Ergebnis wiederverwenden können. Damit denke ich auch, dass Ihre Funktion in "One Shot" bewertet werden könnte, was bedeutet, dass es eindeutig eine Gleichung gibt, mit der Sie die Anzahl der Pixel erhalten können, ohne auf irgendetwas zu iterieren.
  • Die Kombination von findNeighboringColorsund findMostFrequentNeighborscheint ein wenig übertrieben. Obwohl der Code tut , was es tun soll ist, iterieren Sie viel Mal etwas zu tun , das ist ziemlich einfach. Was wäre, wenn Sie anstelle einer Funktion, die alle einzelnen Pixelintensitäten in der Nachbarschaft zurückgibt, und einer anderen Funktion, die den häufigsten Wert findet, nur eine Funktion hätten , die die am häufigsten wiederholte Farbe in einer Nachbarschaft gefunden hätte? Sie müssten nur den Code ändern findNeighboringColors, um beispielsweise ein Wörterbuch zurückzugeben, in dem key=color, value=count. Dann könnten Sie die Farbe auswählen, die die höchste Anzahl aufweist (ich überlasse Ihnen die Implementierung dieser Funktion, aber Sie haben findNeighboringColorsbereits so ziemlich alle erforderlichen Werkzeuge in der Funktion!).
  • Schließlich habe ich mich gefragt, warum der untere Rand Ihres Bildes auch blau ist. Es ist nichts Falsches daran, wenn man bedenkt, dass es hübsch aussieht, aber es ist auch nicht so, wie sich der Code verhalten soll. Ich dachte, es hängt mit der Verwendung von Modulos zusammen : picture.get(Math.abs(j)%width,Math.abs(i)%height);. Während dieser Arbeiten konnte man in einer Technik namens interesed werden Polsterung , die oft wieder in Computer Vision kommt , wenn Sie mit Nachbarschaft zu tun haben. Die Idee ist, dass Sie Ihr Bild, bevor Sie mit der Berechnung beginnen, um eine bestimmte Anzahl von Pixeln "vergrößern" (in Ihrem Fall distance). Sie sollten diese Pixel dann auf den Wert einstellen, der dem tatsächlichen Bild am nächsten kommt. Auf diese Weise müssten Sie kein Modulo verwenden und die blaue Linie am unteren Rand wäre wie erwartet schwarz.

Das ist so ziemlich alles, aber ich muss sagen, gute Arbeit, es sieht wirklich gut aus (sowohl die Ausgabe als auch der Code).

2 slepic Sep 09 2020 at 22:47

Nur meine wenigen Cent ...

Es gibt keinen Grund, distance == 1anders zu behandeln :

    public static int findNumberOfPixels(int distance)
    {
        int numberOfPixels = 0;
        int increment = 4;
        for (int i = 1; i <= distance; i++)
        {
            numberOfPixels += increment;
            increment += 4;
        }
        return numberOfPixels;
    }

Darüber hinaus gibt diese Methode jedes Mal dieselbe Ausgabe für dieselbe Eingabe zurück. Aber Sie nennen es einmal für jedes Pixel. Es ist möglicherweise besser, diesen Wert nur einmal zu berechnen.