ImageJ: Mit PlugIn Weichzeichnungsfilter schreiben

masul

Mitglied
Hallo alle zusammen!

Habe im Studium folgende Aufgabe erhalten:
Schreiben Sie einen Filter, der ein Bild unscharf zeichnet. Ein mögliches Verfahren hierfür ist,
dass der Pixel im neuen Bild der Mittelwert aus dem Pixel im alten Bild und allen seinen
Nachbarn ist.

Das ganze soll mit ImageJ gemacht werden.

Ich habe mir bisher einen Kernel erzeugt,

Java:
double[][] kernel = {
			{3, 5, 3},
			{5, 8, 5},
			{3, 5, 3}
};

dann eine Methode geschrieben, indem alle Rot, Grün und Blau-Anteile aus allen 9 betroffenen Pixeln gebaut werden und sie später alle zu einem int-Wert geschiftet.

Java:
public int getInterpolatedPixel(Point point, double [][] kernel, ImageProcessor ipOld) {
		
		int x = point.x;
		int xLinks = x - 1;
		int xRechts = x + 1;
		int y = point.y;
		int yOben = y - 1;		
		int yUnten = y + 1;
		
		// Neun Color-Objekte fuer alle 9 betroffenen Pixel
		final Color c1 = new Color(ipOld.getPixel(xLinks, yOben));
		final Color c2 = new Color(ipOld.getPixel(x, yOben));
		final Color c3 = new Color(ipOld.getPixel(xRechts, yOben));
		
		final Color c4 = new Color(ipOld.getPixel(xLinks, y));
		final Color c5 = new Color(ipOld.getPixel(x, y));
		final Color c6 = new Color(ipOld.getPixel(xRechts, y));
		
		final Color c7 = new Color(ipOld.getPixel(xLinks, yUnten));
		final Color c8 = new Color(ipOld.getPixel(x, yUnten));
		final Color c9 = new Color(ipOld.getPixel(xRechts, yUnten));
		
		// Interpolation der Rot-Anteile der jeweiligen Pixel
		int red = (int) ((kernel[0][0] * c1.getRed()) + (kernel[0][1] * c2.getRed()) + (kernel[0][2] * c3.getRed()) +
						(kernel[1][0] * c4.getRed()) + (kernel[1][1] * c5.getRed()) + (kernel[1][2] * c6.getRed()) +
						(kernel[2][0] * c7.getRed()) + (kernel[2][1] * c8.getRed()) + (kernel[2][2] * c9.getRed())) / 40;

		
		// Interpolation der Gruen-Anteile der jeweiligen Pixel
		int green = (int) ((kernel[0][0] * c1.getGreen()) + (kernel[0][1] * c2.getGreen()) + (kernel[0][2] * c3.getGreen()) +
				(kernel[1][0] * c4.getGreen()) + (kernel[1][1] * c5.getGreen()) + (kernel[1][2] * c6.getGreen()) +
				(kernel[2][0] * c7.getGreen()) + (kernel[2][1] * c8.getGreen()) + (kernel[2][2] * c9.getGreen()))/ 40;
		
		// Interpolation der Blau-Anteile der jeweiligen Pixel
		int blue = (int) ((kernel[0][0] * c1.getBlue()) + (kernel[0][1] * c2.getBlue()) + (kernel[0][2] * c3.getBlue()) +
				(kernel[1][0] * c4.getBlue()) + (kernel[1][1] * c5.getBlue()) + (kernel[1][2] * c6.getBlue()) +
				(kernel[2][0] * c7.getBlue()) + (kernel[2][1] * c8.getBlue()) + (kernel[2][2] * c9.getBlue()))/ 40;
		
		// errechneter int-Wert aus allen umliegenden Pixeln
		return (0xff<<24) | (red << 16) | (green << 8) | blue;	
			
	}


Als letztes habe ich in der run()- Methode einmal eine geschachtelte for-Schleife, um über die Höhe und Breite des Ausgangsbildes zu iterieren,
und eine zweite geschachtelte for-Schleife, um über den Kernel zu iterieren.

Java:
	public void run(String arg) {
		imp.show();
		
		final ImageProcessor ipOld = imp.getProcessor();
		final ImagePlus newImage = NewImage.createRGBImage( "Blurred Image", ipOld.getWidth(), ipOld.getHeight(), 1, NewImage.FILL_BLACK );
		final ImageProcessor ipNew = newImage.getProcessor();
		
		int w = ipOld.getWidth();
		int h = ipOld.getHeight();
		
		final int[] pixNew = (int[])ipNew.getPixels();
		
		double[][] kernel = {
				{3, 5, 3},
				{5, 8, 5},
				{3, 5, 3}
		};

		for (int v = 1; v <=  - 2; v++ ) {
			for (int u = 1; u <= w - 2; u++) {
				double sum = 0;
				for (int i = 0; i < kernel.length; ++i) {
					for (int j = 0; j < kernel[0].length; ++j) {
						int pix = (int) (getInterpolatedPixel(new Point(u, v), kernel,  ipOld));
						pixNew[v + u ] = pix;
						
					}
				}
			
			}
		}	
		newImage.show();
	}

Nur leider komme ich partout nicht weiter: Ich weiß, dass da wahrscheinlich irgendwo ein Denkfehler ist, komme aber leider nicht von alleine drauf. Es wäre vielleicht deshalb besser, wenn ein anderer Mal über meinen Code schauen könnte. Bitteee.
Ich sitze schon seit Tagen daran und komme nicht weiter.
 
Ist etwas unübersichtlich und natürlich schwer zu testen. Ein paar System.out.println's eingestreut könnten klarer machen, was er dort eigentlich rechnet. Eine genauere Problembeschreibung wäre auch nicht schlecht. Beim ersten Überfliegen fiel mir nur auf, dass er bei
for (int v = 1; v <= - 2; v++ ) {
die Schleife nicht durchläuft, und es wohl
for (int v = 1; v <= h - 2; v++ ) {
lauten sollte.
 
Ja, du hast Recht. Natürlich muss es h - 2 heißen..Ändert aber leider nichts am Ergebnis🙁

Das Problem ist, dass ich nicht weiß, wie ich mit den einzelnen Farbkanälen umgehen und sie mit dem Kernel kombinieren soll..Das Foto wird immer schwarz..
 
Leider kenne ich mich mit ImageJ nicht so gut aus, wie ich gerne würde (und auch sollte), aber ... da würde ich erstmal schauen, ob nicht mit dem "pixNew" noch irgendwas gemacht werden muss, damit es im Zielbild ankommt. Pragmatisch:
Java:
//pixNew[v + u ] = pix; // Testwiese raus
pixNew[v + u ] = 0xFFFFFFFF; // Tesweise rein: Sollte weißes Bild ergeben!
Wenn das ein weißes Bild ergibt, stimmt wohl mit den Pixeln was nicht. Wie gesagt, die Rechnung ist sehr, sehr unübersichtlich (und wenn dein Lehrer dich Ärgern will, sagt er am Ende: So, und das ganze jetzt mal mit einem 5x5-Kernel 😀 (
warning.png
Überleg' mal, wie du das verallgemeinern könntest!
warning.png
)

Ich dachte noch kurz, dass da durch die Konvertierung zu "int" was verloren geht, aber das sollte so schon passen. Gib am Ende von getInterpolatedPixel halt mal aus
Java:
System.out.println("r "+red);
System.out.println("g "+green);
System.out.println("b "+blue);
und taste dich dann vor, wo der Fehler herkommen könnte.
 
Hmm..ich glaube, da ist wirklich etwas falsch: Es ergibt leider kein Weiß, sondern einen mittleren Grauton.
Und die r, g und b Ausgaben sind oft auch die gleichen Werte..wie z.B.

r 80 g 63 b 35
r 80 g 63 b 35
r 80 g 63 b 35
r 80 g 63 b 35
r 80 g 63 b 35
r 80 g 63 b 35
r 79 g 66 b 36
r 79 g 66 b 36
r 79 g 66 b 36
r 79 g 66 b 36
r 79 g 66 b 36
r 79 g 66 b 36
r 79 g 66 b 36
r 79 g 66 b 36
r 79 g 66 b 36
r 74 g 66 b 34
r 74 g 66 b 34
r 74 g 66 b 34
r 74 g 66 b 34
r 74 g 66 b 34
r 74 g 66 b 34
r 74 g 66 b 34
r 74 g 66 b 34
r 74 g 66 b 34
r 66 g 65 b 31
r 66 g 65 b 31
r 66 g 65 b 31
r 66 g 65 b 31
r 66 g 65 b 31
r 66 g 65 b 31
r 66 g 65 b 31
r 66 g 65 b 31
r 66 g 65 b 31
r 58 g 65 b 28
r 58 g 65 b 28
r 58 g 65 b 28
r 58 g 65 b 28
r 58 g 65 b 28
 
Zuletzt bearbeitet:
Dass es nicht weiß ist, ist zwar seltsam, aber zumindest kommen die Werte schon im Bild an (vielleicht falsch, aber sie kommen an). Soweit ich weiß gibt es ja da etliche eigene Bildformate, vielleicht stimmt da irgendwas nicht...? Die RGB-Werte können schon so oft gleich sein, wenn das Bild relativ große, gleichfarbige Flächen enthält. Aber ... auch mit diesen Werten (r 80 g 63 b 35) ist alles schwarz? Das sollte eigentlich einen deutlichen Dunkelbraunton ergeben ...
 
Ja, insgesamt ist da viel, viel Unfug dabei, aber ... poste das nächste mal ein KSKB.

An welche Stelle im Bild sollen denn die neuen Pixel gesetzt werden, wenn da
pixNew[v + u ] = pix;
steht?!


Java:
import ij.IJ;
import ij.ImagePlus;
import ij.gui.NewImage;
import ij.plugin.filter.PlugInFilter;
import ij.process.ImageProcessor;

import java.awt.Color;
import java.awt.Point;

public class Simple_Test_Plugin implements PlugInFilter
{
    ImagePlus imp;

    @Override
    public void run(ImageProcessor imageProcessor)
    {
        run("Whatever");
    }

    public void run(String arg)
    {
        imp.show();

        final ImageProcessor ipOld = imp.getProcessor();
        final ImagePlus newImage = NewImage.createRGBImage("Blurred Image", ipOld.getWidth(), ipOld.getHeight(), 1, NewImage.FILL_BLACK);
        final ImageProcessor ipNew = newImage.getProcessor();

        int w = ipOld.getWidth();
        int h = ipOld.getHeight();

        final int[] pixNew = (int[])ipNew.getPixels();

        double[][] kernel =
        {
        { 3, 5, 3 },
        { 5, 8, 5 },
        { 3, 5, 3 } };

        for (int v = 1; v <= h-2; v++)
        {
            for (int u = 1; u <= w - 2; u++)
            {
                double sum = 0;
                for (int i = 0; i < kernel.length; ++i)
                {
                    for (int j = 0; j < kernel[0].length; ++j)
                    {
                        int pix = (int)(getInterpolatedPixel(new Point(u, v), kernel, ipOld));
                        pixNew[u+v*w] = pix;

                    }
                }

            }
        }
        IJ.log("Done");
        newImage.show();
        newImage.updateAndDraw();
    }

    public int getInterpolatedPixel(Point point, double[][] kernel, ImageProcessor ipOld)
    {

        int x = point.x;
        int xLinks = x - 1;
        int xRechts = x + 1;
        int y = point.y;
        int yOben = y - 1;
        int yUnten = y + 1;

        // Neun Color-Objekte fuer alle 9 betroffenen Pixel
        final Color c1 = new Color(ipOld.getPixel(xLinks, yOben));
        final Color c2 = new Color(ipOld.getPixel(x, yOben));
        final Color c3 = new Color(ipOld.getPixel(xRechts, yOben));

        final Color c4 = new Color(ipOld.getPixel(xLinks, y));
        final Color c5 = new Color(ipOld.getPixel(x, y));
        final Color c6 = new Color(ipOld.getPixel(xRechts, y));

        final Color c7 = new Color(ipOld.getPixel(xLinks, yUnten));
        final Color c8 = new Color(ipOld.getPixel(x, yUnten));
        final Color c9 = new Color(ipOld.getPixel(xRechts, yUnten));

        // Interpolation der Rot-Anteile der jeweiligen Pixel
        int red = (int)((kernel[0][0] * c1.getRed()) + (kernel[0][1] * c2.getRed()) + (kernel[0][2] * c3.getRed()) + (kernel[1][0] * c4.getRed()) + (kernel[1][1] * c5.getRed()) + (kernel[1][2] * c6.getRed()) + (kernel[2][0] * c7.getRed()) + (kernel[2][1] * c8.getRed()) + (kernel[2][2] * c9.getRed())) / 40;

        // Interpolation der Gruen-Anteile der jeweiligen Pixel
        int green = (int)((kernel[0][0] * c1.getGreen()) + (kernel[0][1] * c2.getGreen()) + (kernel[0][2] * c3.getGreen()) + (kernel[1][0] * c4.getGreen()) + (kernel[1][1] * c5.getGreen()) + (kernel[1][2] * c6.getGreen()) + (kernel[2][0] * c7.getGreen()) + (kernel[2][1] * c8.getGreen()) + (kernel[2][2] * c9.getGreen())) / 40;

        // Interpolation der Blau-Anteile der jeweiligen Pixel
        int blue = (int)((kernel[0][0] * c1.getBlue()) + (kernel[0][1] * c2.getBlue()) + (kernel[0][2] * c3.getBlue()) + (kernel[1][0] * c4.getBlue()) + (kernel[1][1] * c5.getBlue()) + (kernel[1][2] * c6.getBlue()) + (kernel[2][0] * c7.getBlue()) + (kernel[2][1] * c8.getBlue()) + (kernel[2][2] * c9.getBlue())) / 40;

        if (point.y < 10 && point.x < 10)
        {
            IJ.log("At "+point);
            IJ.log("red "+red);
            IJ.log("green "+green);
            IJ.log("blue "+blue);
        }
        // errechneter int-Wert aus allen umliegenden Pixeln
        return (0xff << 24) | (red << 16) | (green << 8) | blue;

    }

    @Override
    public int setup(String arg, ImagePlus imagePlus)
    {
        if (arg != null && arg.equals("about"))
        {
            IJ.showMessage("About Simple Test Plugin...", "An example of an ImageJ plugin\n");
            return DOES_RGB;
        }
        this.imp = imagePlus;
        return DOES_RGB;
    }

}
 
Also ich hab jetzt alles umgestellt und es erstmal ohne Kernel ausprobiert. Jetzt klappt es, allerdings nur bei kleinen Bildern.
Hier ist meine neue run()-Methode:

Java:
@Override	
	public void run(String arg) {
		imp.show();
		
		final ImageProcessor ipOld = imp.getProcessor();
		final ImagePlus newImage = NewImage.createRGBImage( "Blurred Image", ipOld.getWidth(), ipOld.getHeight(), 1, NewImage.FILL_BLACK );
		final ImageProcessor ipNew = newImage.getProcessor();
		final int[] pixNew = (int[])ipNew.getPixels();
	
		for (int u = 1; u < ipOld.getWidth() - 1; u++) {
			for (int v = 1; v < ipOld.getHeight() - 1; v++) {
				// Neun Color-Objekte fuer alle 9 betroffenen Pixel
				final Color c1 = new Color(ipOld.getPixel(u - 1, v - 1));
				final Color c2 = new Color(ipOld.getPixel(u, v - 1));
				final Color c3 = new Color(ipOld.getPixel(u + 1, v - 1));
				
				final Color c4 = new Color(ipOld.getPixel(u - 1, v));
				final Color c5 = new Color(ipOld.getPixel(u, v));
				final Color c6 = new Color(ipOld.getPixel(u + 1, v));
				
				final Color c7 = new Color(ipOld.getPixel(u - 1, v + 1));
				final Color c8 = new Color(ipOld.getPixel(u, v + 1));
				final Color c9 = new Color(ipOld.getPixel(u + 1, v + 1));
					
				// Rot-Anteile der Pixel
				int r1 = c1.getRed();
				int r2 = c2.getRed();
				int r3 = c3.getRed();
				int r4 = c4.getRed();
				int r5 = c5.getRed();
				int r6 = c6.getRed();
				int r7 = c7.getRed();
				int r8 = c8.getRed();
				int r9 = c9.getRed();
				int r = (r1 + r2 + r3 + r4 + r5 + r6 + r7 + r8 + r9) / 9;
				
				// Gruen-Anteile der Pixel
				int g1 = c1.getGreen();
				int g2 = c2.getGreen();
				int g3 = c3.getGreen();
				int g4 = c4.getGreen();
				int g5 = c5.getGreen();
				int g6 = c6.getGreen();
				int g7 = c7.getGreen();
				int g8 = c8.getGreen();
				int g9 = c9.getGreen();
				int g = (g1 + g2 + g3 + g4 + g5 + g6 + g7 + g8 + g9) / 9;
				
				// Blau-Anteile der Pixel
				int b1 = c1.getBlue();
				int b2 = c2.getBlue();
				int b3 = c3.getBlue();
				int b4 = c4.getBlue();
				int b5 = c5.getBlue();
				int b6 = c6.getBlue();
				int b7 = c7.getBlue();
				int b8 = c8.getBlue();
				int b9 = c9.getBlue();
				int b = (b1 + b2 + b3 + b4 + b5 + b6 + b7 + b8 + b9) / 9;
				
				int pix = (0xff << 24) | r << 16 | g << 8 | b;
				pixNew[u + v * ipNew.getHeight()] = pix;
			}
			
		}	
		newImage.show();
	}
 
Nicht viel besser, aber ... hattest du das angesehen, was ich gepostet habe? (Manchmal frag' ich mich, warum ich das hier mache, und finde keine Antwort :bahnhof: )
 

Zurück
Oben