Implementierung eines Glasfilters für Bilder

Sep 07 2020

Dies ist Übung 3.1.39. aus dem Buch Informatik Ein interdisziplinärer Ansatz von Sedgewick & Wayne:

Schreiben Sie ein Programm, das den Namen einer Bilddatei als Befehlszeilenargument verwendet und einen Glasfilter anwendet: Setzen Sie jedes Pixel p auf die Farbe eines zufälligen benachbarten Pixels (dessen Pixelkoordinaten sich beide um höchstens 5 von den Koordinaten von p unterscheiden).

Hier ist mein Programm:

import java.awt.Color;

public class test 
{
    public static int[] chooseRandomNeighbor(int i, int j)
    {
        int[] chosenNeighbor = new int[2];
        double r = Math.random();
        if      (r < 1.0/8.0)
        {
            chosenNeighbor[0] = i-1;
            chosenNeighbor[1] = j-1;
        }
        else if (r < 2.0/8.0)
        {
            chosenNeighbor[0] = i-1;
            chosenNeighbor[1] = j; 
        }
        else if (r < 3.0/8.0)
        {
            chosenNeighbor[0] = i-1;
            chosenNeighbor[1] = j+1; 
        }
        else if (r < 4.0/8.0)
        {
            chosenNeighbor[0] = i;
            chosenNeighbor[1] = j+1; 
        }
        else if (r < 5.0/8.0)
        {
            chosenNeighbor[0] = i+1;
            chosenNeighbor[1] = j+1; 
        }
        else if (r < 6.0/8.0)
        {
            chosenNeighbor[0] = i+1;
            chosenNeighbor[1] = j; 
        }
        else if (r < 7.0/8.0)
        {
            chosenNeighbor[0] = i+1;
            chosenNeighbor[1] = j-1; 
        }
        else if (r < 8.0/8.0)
        {
            chosenNeighbor[0] = i;
            chosenNeighbor[1] = j-1; 
        }
        return chosenNeighbor;
    }
    public static Picture filter(Picture picture)
    {
        int width = picture.width();
        int height = picture.height();
        Picture filteredPicture = new Picture(width,height);
        // the following four for-loops make the dead frame
        for (int row = 0; row < height; row++)
        {
            Color color = picture.get(0,row);
            filteredPicture.set(0,row,color);
        }
        for (int row = 0; row < height; row++)
        {
            Color color = picture.get(width-1,row);
            filteredPicture.set(width-1,row,color);
        }
        for (int col = 0; col < width; col++)
        {
            Color color = picture.get(col,0);
            filteredPicture.set(col,0,color);
        }
        for (int col = 0; col < width; col++)
        {
            Color color = picture.get(col,height-1);
            filteredPicture.set(col,height-1,color);
        }
        // the real filtering takes place here
        for (int col = 1; col < width-1; col++)
        {
            for (int row = 1; row < height-1; row++)
            {
                int[] chosenNeighbor = chooseRandomNeighbor(row,col);
                Color color = picture.get(chosenNeighbor[1],chosenNeighbor[0]);
                filteredPicture.set(col,row,color);
            }
        }
        return filteredPicture;
    }
    public static void main(String[] args)
    {
        Picture picture = new Picture(args[0]);
        Picture filteredPicture = filter(filter(filter(filter(picture))));
        filteredPicture.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:

Input (Bild von James McAvoy aus Wikipedia , der im Film Glass gespielt hat ):

Ausgabe:

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

Danke für Ihre Aufmerksamkeit.

Antworten

3 Marc Sep 07 2020 at 22:32

Interessante Übung und nette Umsetzung, einige Vorschläge auf meiner Seite:

  • Die Methode chooseRandomNeighborwird nur von verwendet filterund kann daher auf festgelegt werdenprivate
  • Der Name filterist zu allgemein für eine Methode, ein besserer Name könnte seinapplyGlassFilter
  • In Java beginnt der Name einer Klasse mit einem Großbuchstaben

Optimierung

zufälliges benachbartes Pixel (dessen Pixelkoordinaten sich beide um höchstens 5 von den p-Koordinaten unterscheiden).

Die Methode chooseRandomNeighborwählt einen benachbarten zufälligen Nachbarn aus (der sich um höchstens 1 unterscheidet) und wird dann viermal aufgerufen. Das bedeutet, dass 4 vollständige Bilder im Speicher erstellt werden. Wäre es nicht besser, direkt einen Nachbarn mit maximaler Entfernung 5 auszuwählen?

Dazu muss die Methode chooseRandomNeighbordie Breite oder Höhe akzeptieren:

int neighborColIndex = chooseRandomNeighbor(col,width);
int neighborRowIndex = chooseRandomNeighbor(row,height);

Und das wird chooseRandomNeighborüberarbeitet:

private static int chooseRandomNeighbor(int index, int max) {
    // Random delta between -5 and +5
    int randomDelta = (int) ((Math.random() * (10)) - 5);
    // Add delta to index without overflowing the limit
    int neighborIndex = (index + randomDelta) % max;
    // If index is negative return 0
    return neighborIndex < 0 ? 0 : neighborIndex;
}

Jetzt kann die Methode filternur noch einmal aufgerufen werden und nur das neue Bild wird im Speicher erstellt.

Randnotiz : Sie werden immer besser, machen Sie weiter so!

Code überarbeitet

public class Test {
    
    public static Picture applyGlassFilter(Picture inputPicture) {
        int width = inputPicture.width();
        int height = inputPicture.height();
        Picture outputPicture = new Picture(width, height);
        for (int col = 0; col < width; col++) {
            for (int row = 0; row < height; row++) {
                int neighborColIndex = chooseRandomNeighbor(col,width);
                int neighborRowIndex = chooseRandomNeighbor(row,height);
                Color c = inputPicture.get(neighborColIndex,neighborRowIndex);
                outputPicture.set(col, row, c);
            }
        }
        return outputPicture;
    }
    
    private static int chooseRandomNeighbor(int index, int max) {
        // Random delta between -5 and +5
        int randomDelta = (int) ((Math.random() * (10)) - 5);
        // Add delta to index without overflowing the limit
        int neighborIndex = (index + randomDelta) % max;
        // If index is negative return 0
        return neighborIndex < 0 ? 0 : neighborIndex;
    }
    
    public static void main(String[] args) {
        Picture inputPicture = new Picture(args[0]);
        Picture outputPicture = applyGlassFilter(inputPicture);
        outputPicture.show();
    }
}