package Histogram; import java.awt.Dimension; import java.awt.FlowLayout; import java.awt.Image; import java.awt.Toolkit; import java.awt.event.*; import java.awt.event.MouseEvent; import javax.swing.*; import javax.swing.border.BevelBorder; import javax.swing.event.*; /** * Dieses Applet wurde geschrieben,um es dem Benutzer zu ermoeglichen * Bildhistogramme manuell bzw. automatisiert zu skalieren. * Auf der Oberflaeche sind sechs Bilder zu sehen.Zuerst das Eingangsbild,dann * das Histogramm des Eingangsbildes,seine kumulative Verteilungsfunktion und * das gleiche noch einmal fuer das manipulierte Bild. In dem Histogramm * des Eingangsbildes ist eine blaue Linie zu sehen,welche die Skalierungs- * funktion darstellt. Zu Anfang ist dies natuerlich die Identitaet,da jedem * Grauwert sein eigener Wert als Funktionswert zugewiesen werden soll. * Diese Linie kann vom Benutzer mit gedrueckter Maustaste verzogen weden,um somit * das Bild neu zu skalieren. Weisst man z.B. der 0 den Funktionswert 255 bzw. * umgekehrt zu,erhaelt man das negativ des Bildes.Wobei dies hier nur naeherungs- * weise moeglich ist, da immer f(0) = 0 und f(255)=255 gilt. * Die Bedingung,das der erste und letzte Wert nicht veraenderbar sind,wurde * eingefuehrt,um eine surjektive Funktion zu garantieren. * Ziel sollte es sein,das Bild moeglichst so zu skalieren, dass die Inten- * sitaetswerte naeherungsweise gleich ueber das Bild verteilt sind. * Mathematisch geschieht dies mit Hilfe der kumulativen Verteilungsfunktion. * Klickt der Benutzer auf den 'cumulativeButton' (siehe Deklaration) * nimmt die Skalierungsfunktion automatisch dir Form dieser verteilungs- * funktion an. * * Fuer die Berechnung der Histogramme,bzw. der Skalierung der Bilder steht * eine Instanz der Klasse HistogramGenerator zur Verfuegung. Die Skalierungs- * funktion ist in dem Array grayValueFunction gespeichert. Mit Hilfe dieses * Arrays wird die Skalierung auch im Generator berechnet. * * @author Rene Iser, Simon Winkelbach * */ public class HistogramApplet extends JApplet implements MouseInputListener{ protected iRPImage histogramImage,inputImage,inputCopy,scaledInputImage,scaledHistogramImage; protected ImageWindow histogram,input,scaledInput,scaledHistogram; protected histogramViewer viewer; protected HistogramGenerator generator; protected JPanel operationPanel,messagePanel; protected JDialog pictureDialog; protected JButton[] pictureButtons; protected JButton reset,cumulativeButton,choosePicture; protected JLabel message1,message2; protected int[] grayValueFunction; protected int[] histogramData,inputCumulativeFunction,scaledCumulativeFunction; protected boolean firstMousePress=true,deleted=false; protected boolean drawCumulativeFunction=false,irp=false,bigben=false; protected String plaf; int oldX,oldY,newX,newY; protected ImageIcon dialogIcon; protected DialogImageProvider imgProv; protected Image[] dialogImg; protected String[] dialogImgNames; protected String imageToLoad; public void init(){ Image dialogImage = Toolkit.getDefaultToolkit().getImage(getClass().getResource("dialogIcon.gif")); dialogIcon=new ImageIcon(dialogImage); imgProv = new DialogImageProvider(); dialogImg = imgProv.getImages(); dialogImgNames = imgProv.getImageNames(); this.initializeButtons(); this.initializeImages(); this.initializeDialog(); message1 = new JLabel("Drag blue line over histogram or press"); message2 = new JLabel("second button to get cumulative function"); messagePanel=new JPanel(); messagePanel.setPreferredSize(new Dimension(230,45)); messagePanel.setBorder(BorderFactory.createEtchedBorder()); messagePanel.setLayout(new FlowLayout(FlowLayout.LEFT,3,3)); messagePanel.add(message1); messagePanel.add(message2); operationPanel=new JPanel(); Dimension d = new Dimension(this.getWidth(),60); operationPanel.setPreferredSize(d); operationPanel.setBorder(BorderFactory.createBevelBorder(BevelBorder.RAISED)); operationPanel.setLayout(new FlowLayout(FlowLayout.LEFT,10,5)); operationPanel.add(choosePicture); operationPanel.add(cumulativeButton); operationPanel.add(reset); operationPanel.add(messagePanel); /* * grayValueFunction stellt die Skalierungsfunktion dar. * Dabei ist der Funktionswert an der Stelle i * grayValueFunction[i]. Das Array wird auch dem viewer im * Konstruktor uebergeben,da dieser die Funktion zum zeichnen braucht. * Der Generator skaliert mit Hilfe dieses Arrays das Bild. */ grayValueFunction = new int[inputImage.sizeX]; histogramData=new int[256]; inputCumulativeFunction = new int[256]; scaledCumulativeFunction = new int[256]; viewer = new histogramViewer(input,histogram,scaledInput,scaledHistogram,grayValueFunction, inputCumulativeFunction,scaledCumulativeFunction); /* * Berechnung der Histogramme und der kumulativen Verteilungsfunktionen. * inputImage stellt dabei das Eingangsbild dar.Das dazugehoerige Histogramm * wird von histogramImage repraesentiert. * Ausgangsbild ist scaledInputImage,bzw. dessen Histogramm scaledHistogramImage. * Die kumulativen Verteilungsfunktionen werden durch inputCumulativeFunction und * scaledCumulativeFunction dargestellt. Beides sind 1d int-arrays. * */ generator = new HistogramGenerator(grayValueFunction,histogramData); generator.generateHistogram(inputImage,histogramImage); generator.generateHistogram(scaledInputImage,scaledHistogramImage); generator.generateCumulativeDistributionFunction(inputCumulativeFunction); generator.generateCumulativeDistributionFunction(scaledCumulativeFunction); this.initializeActionListeners(); this.changeLookAndFeel(); getContentPane().add("North",operationPanel); getContentPane().add(viewer); addMouseListener(this); addMouseMotionListener(this); } /** * Initialisierung der Dialogboxen,welche aufgerufen werden, * wenn man Bild oder Tool wechselt. */ public void initializeDialog(){ pictureButtons=new JButton[dialogImg.length]; String buttonName; /* *Hier werden die Buttons zur Bildauswahl initialisiert. *In 'buttonName' wird der Name des zu ladenen Bildes gespeichert. */ for(int i=0;i=1 && k<255){ if(grayValueFunction[k]>-1){ if(Math.abs(y-grayValueFunction[k])<=5){ grayValueFunction[k]=-1; if(drawCumulativeFunction){ viewer.userDefCumPoints[k]=-1; } deleted=true; } } } } } /** * Gibt 'true' zurueck,wenn die Maus ueber dem Histogramm des Eingangs- * bildes ist. * * @param x x-Koordinate der aktuellen Mausposition * @param y y-Koordinate der aktuellen Mausposition * @return boolean */ public boolean mouseOverHistogram(int x,int y){ if(x>=320 && x<576 && y>=80 && y<336) return true; else return false; } /** * Gibt 'true' zurueck,wenn die Maus ueber dem Ausgangsbild ist. * * @param x x-Koordinate der aktuellen Mausposition * @param y y-Koordinate der aktuellen Mausposition * @return boolean */ public boolean mouseOverInput(int x,int y){ if(x>=20 && x<276 && y>=80 && y<336) return true; else return false; } /** * Gibt 'true' zurueck,wenn die Maus ueber dem Ausgangsbild ist. * * @param x x-Koordinate der aktuellen Mausposition * @param y y-Koordinate der aktuellen Mausposition * @return boolean */ public boolean mouseOverScaledInput(int x,int y){ if(x>=20 && x<276 && y>=390 && y<646) return true; else return false; } /** * Gibt 'true' zurueck,wenn die Maus ueber dem Histogramm des Ausgangs- * bildes ist. * * @param x x-Koordinate der aktuellen Mausposition * @param y y-Koordinate der aktuellen Mausposition * @return boolean */ public boolean mouseOverScaledHistogram(int x,int y){ if(x>=320 && x<576 && y>=390 && y<646) return true; else return false; } public void mousePressed(MouseEvent e) { } /** * Setzt bei Eingangs-und Ausgangsbild die * Mausposition sowie den aktuellen Grauwert * zwecks Anzeige neu. */ public void mouseMoved(MouseEvent e) { int xPos=e.getX(); int yPos=e.getY(); int value; if(mouseOverInput(xPos,yPos)){ value=inputImage.GetPixel(xPos-20,yPos-80); input.updateMousePosition(xPos-20,yPos-80,value); repaint(); } if(mouseOverHistogram(xPos,yPos)){ value=histogramImage.GetPixel(xPos-320,yPos-80); histogram.updateMousePosition(xPos-320,yPos-80,value); repaint(); } } /** *Diese Methode dient zum Verzerren der Skalierungsfunktion. *Klickt man zum ersten mal(nach dem letzten Loslassen des Mausbuttons) *in das Bild,wird die deleteOldPoint(..) Methode aufgerufen,um eventuell *einen Stuetzpunkt der Skalierungsfunktion,der in der Naehe ist,zu loeschen. *Das Verzerren der Funktion funktioniert,indem die aktuelle Mausposition in *dem Funktionsarray gespeichert,bzw. der letzte Wert wieder geloescht wird. *Alle Stuetzpunkte der Funktion werden optisch durch kleine schwarze Rechtecke *markiert. Da bei der kumulativen Verteilungsfunktion sehr viele von diesen *zu sehen waeren(die Linie waere schwarz), sollen diese nicht mit ausgegeben *werden wenn die Skalierungsfunktion gleich der kumulativen *Verteilungsfunktion ist. In diesem Fall werden nur Stuetzpunkte markiert, die *der Benutzer beim Verzerren der Funktion selbst definiert. Diese Stuetzpunkte *werden in einem extra array im viewer gespeichert. Die boolsche Variable *'drawCumulativeFunction' ist auf 'true' wenn auf den cumulative-Button *geklickt wurde. In diesem Fall werden die selbstdefinierten Stuetzpunkte *in dem Array viewer.userDefCumPoints[newX-320]=newY-80; gespeichert. *Daher die Abfrage if(drawCumulativeFunction).. */ public void mouseDragged(MouseEvent e) { newX=e.getX(); newY=e.getY(); int value; if(mouseOverHistogram(newX,newY) && (newX-320)>0 && (newX-320)<255){ if(firstMousePress){ deleteOldPoint(newX-320,newY-80); oldX=newX;oldY=newY; firstMousePress=false; } else{ if(drawCumulativeFunction){ viewer.userDefCumPoints[newX-320]=newY-80; } grayValueFunction[newX-320]=newY-80; if(oldX != newX){ grayValueFunction[oldX-320]=-1; if(drawCumulativeFunction) viewer.userDefCumPoints[oldX-320]=-1; } repaint(); oldX=newX;oldY=newY; } } } /** * Nach dem draggen der Maus(Verzerren der Skalierungsfunktion) * wird hier das Bild neu skaliert und bzw. Histogramm und * kum. Verteilungsfunktion neu berechnet. */ public void mouseReleased(MouseEvent e) { inputCopy=inputImage.copy(); generator.scaleImage(inputCopy); scaledHistogramImage.LoadImage("histogramm.gif"); scaledHistogram.changeImage(scaledHistogramImage); scaledInput.changeImage(inputCopy); generator.generateHistogram(inputCopy,scaledHistogramImage); generator.generateCumulativeDistributionFunction(scaledCumulativeFunction); repaint(); firstMousePress=true; } /** * Diese Methode funktioniert von der Logik her genau so wie * die mouseDragged-Methode. * @see mouseDragged(MouseEvent e) */ public void mouseClicked(MouseEvent e) { deleted=false; int xPos=e.getX(); int yPos=e.getY(); if(mouseOverHistogram(xPos,yPos)&& (newX-320)>0 && (newX-320)<255){ deleteOldPoint(xPos-320,yPos-80); if(!deleted){ if(drawCumulativeFunction){ viewer.userDefCumPoints[newX-320]=newY-80; } grayValueFunction[xPos-320]=yPos-80; } inputCopy=inputImage.copy(); generator.scaleImage(inputCopy); scaledHistogramImage.LoadImage("histogramm.gif"); scaledHistogram.changeImage(scaledHistogramImage); scaledInput.changeImage(inputCopy); generator.generateHistogram(inputCopy,scaledHistogramImage); generator.generateCumulativeDistributionFunction(scaledCumulativeFunction); repaint(); } } public void mouseEntered(MouseEvent e) {} public void mouseExited(MouseEvent e) {} }