Hallo Freunde!
Ich hoffe ihr könnt mir bei folgendem Problem helfen.
Es geht darum, ein beliebiges 8Bit-Grauwert-Bild in ImageJ einzulesen und ohne Hilfe von Filtern folgendes zu tun:
- Erkennen aller GEFÜLLTEN Kreise
- Berechnen der Kreismittelpunkte
- Abstände der Kreismittelpunkte zueinander berechnen
Folgendes habe ich getan:
1.) Das Bild mit einem Threshold-Filter bearbeitet um ein Binärbild mit den Grauwerten 0 und 255 zu erhalten.
2.) Jedes Objektpixel (Grauwert 255), welches alleine im Bild liegt (keine Nachbarn hat) wird um einen Nachbarn vergrößert.
3.) Das Bild invertiert.
4.) Kantendetektion und Speicherung der einzelnen Punkte in einem Polygon. Dieses wiederum gespeichert in einem Vektor.
5.) Einen Hough-Raum (Hough Transformation) von allen gefundenen Objekten erstellt.
6.) Ermitteln der höchsten Hough-Werte und löschen der Nachbarwerte um Doppelung zu vermeiden.
Das Problem ist, dass er nicht die richtigen Hough-Werte berechnet/findet, wie auch immer. Auf jeden Fall heißt es: "Dort wo ein Maxima der Hough-Werte auftaucht, befindet sich der (Kreis)Mittelpunkt. Nur diese stimmen bei mir nicht mit den ausgegebenen Werten überein.
Ich weiß, dass das ganze Programm ganz schön aufwendig ist, aber vllt. hat ja jemand irgendeine Idee oder einen Ansatz.
Vielen Dank und schönen Sonntag Abend!
Hier ein Beispielbild: http://www.wasd.de/circ.tif
Hier der Quellcode (einige Teile sind noch nicht in Funktionen untergliedert, was aber kein Problem beim Verständnis darstellen sollte):
Ich hoffe ihr könnt mir bei folgendem Problem helfen.
Es geht darum, ein beliebiges 8Bit-Grauwert-Bild in ImageJ einzulesen und ohne Hilfe von Filtern folgendes zu tun:
- Erkennen aller GEFÜLLTEN Kreise
- Berechnen der Kreismittelpunkte
- Abstände der Kreismittelpunkte zueinander berechnen
Folgendes habe ich getan:
1.) Das Bild mit einem Threshold-Filter bearbeitet um ein Binärbild mit den Grauwerten 0 und 255 zu erhalten.
2.) Jedes Objektpixel (Grauwert 255), welches alleine im Bild liegt (keine Nachbarn hat) wird um einen Nachbarn vergrößert.
3.) Das Bild invertiert.
4.) Kantendetektion und Speicherung der einzelnen Punkte in einem Polygon. Dieses wiederum gespeichert in einem Vektor.
5.) Einen Hough-Raum (Hough Transformation) von allen gefundenen Objekten erstellt.
6.) Ermitteln der höchsten Hough-Werte und löschen der Nachbarwerte um Doppelung zu vermeiden.
Das Problem ist, dass er nicht die richtigen Hough-Werte berechnet/findet, wie auch immer. Auf jeden Fall heißt es: "Dort wo ein Maxima der Hough-Werte auftaucht, befindet sich der (Kreis)Mittelpunkt. Nur diese stimmen bei mir nicht mit den ausgegebenen Werten überein.
Ich weiß, dass das ganze Programm ganz schön aufwendig ist, aber vllt. hat ja jemand irgendeine Idee oder einen Ansatz.
Vielen Dank und schönen Sonntag Abend!
Hier ein Beispielbild: http://www.wasd.de/circ.tif
Hier der Quellcode (einige Teile sind noch nicht in Funktionen untergliedert, was aber kein Problem beim Verständnis darstellen sollte):
Code:
import ij.*;
import ij.gui.*;
import java.awt.*;
import java.util.Vector;
import ij.plugin.filter.PlugInFilter;
import ij.process.*;
import java.io.*;
public class kontur implements PlugInFilter {
private Vector<Polygon> polyvec;
double hough[][][];
int maxcircles = 20;
/** Initialisierung in ImageJ */
public int setup(String arg, ImagePlus imp) {
if (arg.equals("about"))
{ showAbout(); return DONE; }
// Zugelassen nur für 8-Bit Graubilder
return DOES_8G + NO_CHANGES;
}
/** About Message zu diesem Plug-In */
void showAbout() {
IJ.showMessage("Graubildtest",
"Testprogramm"
);
}
/** Ausfuehrende Funktion
* @param ip Image Processor. Klasse in ImageJ, beinhaltet das Bild und
* zugehörige Metadaten.
*/
public void run(ImageProcessor ip) {
// Setze Breite, Hoehe und markierte Region
int w = ip.getWidth();
int h = ip.getHeight();
int cirles = 0; // Anzahl gefundener Kreise
//int table[][][]; // Virtuelle Tabelle fuer Winkelberechnung
//int angles = 0; // Anzahl der Winkel fuer jeden Radius
Rectangle roi = ip.getRoi();
// Erstelle neue Bilder mit selber Groesse und kopiere Pixel von Original
// Kontur-Bild
ImagePlus corrected = NewImage.createByteImage("hough image", w, h, 1, NewImage.FILL_BLACK);
ImageProcessor cor_ip = corrected.getProcessor();
// Bild zur Bearbeitung von Threshold, Delate und Invertion
ImagePlus thimg = new ImagePlus("debug image", ip.duplicate());
ImageProcessor th = thimg.getProcessor();
// Bild als Vorgabe fuer Kontur auf Debug-Image
ImagePlus copypix = new ImagePlus("dump image", ip.duplicate());
ImageProcessor cpix = copypix.getProcessor();
//Pixel-Array des Eingabebildes
byte[] pixelsin = (byte[])ip.getPixels();
//Pixel-Array des Debug-Bildes
byte[] pixelsth = (byte[])th.getPixels();
//Pixelarray des neues Bildes
byte[] pixels = (byte[])cor_ip.getPixels();
//Pixelarray des Dump-Bildes
byte[] pix = (byte[])cpix.getPixels();
// Vorarbeit zur Detektion der Kreise
threshold(pixelsth, pix, roi, w, h); // Division in S/W
cpix.copyBits(th, 0, 0, Blitter.COPY); // Bildwerte kopieren
delate(pixelsth, pix, roi, w, h); // Schwarzwerte verstaerken
cpix.copyBits(th, 0, 0, Blitter.COPY); // Bildwerte kopieren
invert(pixelsth, pix, roi, w, h); // Bild invertieren
contour(pixelsth, pixels, roi, w, h); // Kontur erstellen
double radiusMax[];
radiusMax = new double[this.polyvec.size()];
double len;
double xpt, ypt;
double a, b;
Polygon poly;
for (int i = 0; i < this.polyvec.size(); i++) {
poly = (Polygon)this.polyvec.elementAt(i);
xpt = poly.xpoints[0];
ypt = poly.ypoints[0];
for (int j = 1; j < poly.npoints; j++) {
a = poly.xpoints[j] - xpt;
b = poly.ypoints[j] - ypt;
len = Math.sqrt((a * a) + (b * b));
if (len > radiusMax[i])
{
radiusMax[i] = len;
}
}
}
int depth = 10;
double schrittweite = 2*Math.PI / depth;
int r, d;
double angle ;
int rsin, rcos;
hough = new double[w][h][depth];
int iter;
for (int i = 0; i < this.polyvec.size(); i++) {
poly = (Polygon)this.polyvec.elementAt(i);
for (int j = 0; j < poly.npoints; j++) {
iter = 0;
for (double k = 0; k < 2*Math.PI; k+=schrittweite) {
rcos = (int)Math.round((double)radiusMax[i]/2 * Math.cos(k));
rsin = (int)Math.round((double)radiusMax[i]/2 * Math.sin(k));
int xc = poly.xpoints[j] + rcos;
int yc = poly.ypoints[j] + rsin;
if ((yc >= 0) & (yc < h) & (xc >= 0) & (xc < w))
{
hough[xc][yc][iter]++;
pixels[xc + yc * w] = (byte)(hough[xc][yc][iter] + 20);
}
iter++;
}
}
}
double cmax;
double centerpoint[][];
double xmax = 0;
double ymax = 0;
// Binaerbaum in n.tes Polygon mit x an 0 und y an 1
centerpoint = new double[this.polyvec.size()][2];
for (int c = 0; c < this.polyvec.size(); c++) {
cmax = -1;
for (int k = 0; k < depth; k++) {
for (int i = 0; i < h; i++) {
for (int j = 0; j < w; j++) {
if (hough[i][j][k] > cmax) {
cmax = hough[i][j][k];
xmax = i;
ymax = j;
}
}
}
}
centerpoint[ c][0] = xmax;
centerpoint[ c][1] = ymax;
clearneighbours(xmax, ymax, radiusMax[ c], w, h, depth);
}
// Tabelle
IJ.write("Anzahl der gefundenen Objekte: " + String.valueOf(this.polyvec.size()));
for (int i = 0; i < this.polyvec.size(); i++) {
drawcross(pixels, (int) centerpoint[i][0], (int) centerpoint[i][1], w, h);
IJ.write("Kreismittelpunkt von Kreis " + (i+1) + ": [x] " + centerpoint[i][0] + " | [y] " + centerpoint[i][1]);
}
thimg.show();
thimg.updateAndDraw();
corrected.show();
corrected.updateAndDraw();
}
private void drawcross(byte[] pixels, int x, int y, int w, int h) {
for (int k = -3; k <= 3; k++) {
pixels[(y + k) * w + x] = (byte)200;
}
for (int k = -3; k <= 3; k++)
{
pixels[y * w + x + k] = (byte)200;
}
}
private void clearneighbours(double xmax, double ymax, double rmax, int w, int h, int d) {
int lx, rx;
int uy, dy;
lx = (int) (xmax - rmax - 1);
rx = (int) (xmax + rmax + 1);
uy = (int) (ymax - rmax - 1);
dy = (int) (ymax + rmax + 1);
if (lx < 0) lx = 0;
if (rx > w) rx = w;
if (uy < 0) uy = 0;
if (dy > w) dy = h;
for (int i = uy; i < dy; i++) {
for (int j = lx; j < rx; j++) {
for (int k = 0; k < d; k++) {
hough[j][i][k] = 0;
}
}
}
}
private void threshold(byte[] pixelsth, byte[] pix, Rectangle roi, int w, int h)
{
// Grauwerte, welche nicht weiss sind, muessen schwarz gemacht werden
int offset, pos;
int gw; // aktueller Grauwert an Position pos
for (int i = roi.y; i < roi.y + roi.height; i++) {
offset = i * w;
for (int j = roi.x; j < roi.x + roi.width; j++) {
pos = offset + j;
gw = pix[pos];
gw = gw & 0xff; // Verschiebung nach rechts
if (gw < 200) gw = 0;
else gw = 255;
pixelsth[pos] = (byte) gw;
}
}
}
private void delate(byte[]pixelsth, byte[] pix, Rectangle roi, int w, int h) {
for (int i=roi.y+1; i<roi.y+roi.height-1; i++) {
for (int j=roi.x+1; j<roi.x+roi.width-1; j++) {
int pix1 = pix[(i-1)*w+(j-1)];
pix1 = (pix1&0x0000ff);
int pix2 = pix[(i-1)*w+j];
pix2 = (pix2&0x0000ff);
int pix3 = pix[(i-1)*w+(j+1)];
pix3 = (pix3&0x0000ff);
int pix4 = pix[i*w+(j-1)];
pix4 = (pix4&0x0000ff);
int pix5 = pix[i*w+j];
pix5 = (pix5&0x0000ff);
int pix6 = pix[i*w+(j+1)];
pix6 = (pix6&0x0000ff);
int pix7 = pix[(i+1)*w+(j-1)];
pix7 = (pix7&0x0000ff);
int pix8 = pix[(i+1)*w+j];
pix8 = (pix8&0x0000ff);
int pix9 = pix[(i+1)*w+(j+1)];
pix9 = (pix9&0x0000ff);
if(pix5 == 255 && (pix1==0||pix2==0||pix3==0||pix4==0||pix6==0||pix7==0||pix8==0||pix9==0)) pixelsth[i*w+j] = (byte) 0;
}
}
}
private void invert(byte[] pixelsth, byte[] pix, Rectangle roi, int w, int h) {
for (int i=roi.y; i<roi.y+roi.height; i++) {
int offset =i*w;
for (int j=roi.x; j<roi.x+roi.width; j++) {
int pos = offset+j;
pixelsth[pos] = (byte)(255-pix[pos]);
}
}
}
private Vector<Polygon> contour(byte[] pixelsth, byte[] pixels, Rectangle roi, int w, int h) {
// Erstelle neuen Vektor vom Typ Polygon
polyvec = new Vector<Polygon>();
for (int i = roi.y + 1; i < roi.y + roi.height - 1; i++) {
for (int j = roi.x + 1; j < roi.x + roi.width - 1; j++) {
if (this.checkcontains(j, i)) {
//pixels[i*w+j] = (byte) 100; //Innere Pixel grau setzen
} else {
int pix1 = pixelsth[i * w + j];
pix1 = (pix1 & 0x0000ff);
if (pix1 == 255) {
createObject(pixelsth, pixels, j, i, w);
}
}
}
}
return polyvec;
}
/** Erstellt ein Polygon mit Hilfe der detektierten Kontur
* @param Neues Bild
* @param Threshold-Bild
* @param x-Koordinate, wo begonnen wird
* @param y-Koordinate, wo begonnen wird
* @param Breite des Bereiches
*/
private void createObject(byte[] pixelsth, byte[] pixels, int x, int y, int w) {
int xcoo = x;
int ycoo = y;
int holdx = x;
int holdy = y;
int pixvalue;
int direction = 1;
int rightcount = 0;
Polygon polygon = new Polygon();
do {
switch (direction) {
case 1: ycoo -= 1; break; //up
case 2: xcoo += 1; break; //right
case 3: ycoo += 1; break; //down
case 4: xcoo -= 1; break; //left
}
pixvalue = (int)pixelsth[ycoo * w + xcoo];
pixvalue = (pixvalue & 0xff); // Verschiebung
if (pixvalue == 255) { // Punkt Hindergrund oder Objektpixel?
if (xcoo == holdx && ycoo == holdy) ; // Punkt bereits abgearbeitet?
else {
pixels[ycoo * w + xcoo] = (byte) 155; // Aendere Grauwert von neuem Punkt
holdx = xcoo;
holdy = ycoo;
polygon.addPoint(xcoo, ycoo);
}
direction -= 1; // gehe eins nach links
rightcount = 0;
} else {
direction += 1; // gehe eins nach rechts
rightcount += 1;
}
if (rightcount == 4) { // wenn 4 mal nach rechts, wieder nach links gehen
direction -= 2;
rightcount = 0;
}
if (direction == 5) direction = 1;
if (direction == 0) direction = 4;
} while (ycoo != y || xcoo != x); // Ende, wenn Endpunkt = Startpunkt
this.polyvec.addElement(polygon);
}
/** Ueberprueft, ob Punkt bereits in Polygon liegt oder nicht.
* @param x x-Koordinate des Punktes
* @param y y-Koordinate des Punktes
* @return True, wenn der Punkt im Polygon liegt, sonst False
*/
public boolean checkcontains(int x, int y) {
for (int i = 0; i < this.polyvec.size(); i++) {
Polygon poly = (Polygon)this.polyvec.elementAt(i);
if (poly.contains(x, y)) return true;
for (int j = 0; j < poly.npoints; j++) {
if (x == poly.xpoints[j] && y == poly.ypoints[j]) return true;
}
}
return false;
}
}