# Kontrast in einem Bild erzeugen



## beastofchaos (5. Mai 2012)

Hallo Leute, ich hänge gerade daran, wie ich diese Funktion funktionsfähig mache 
- Img ist das zu ändernde Bild
- value ist ein Wert zwischen -100 und 100
- Return ist das Ergebnis


```
@Override
	public BufferedImage adopt(BufferedImage img, int value) {
		
            // Der Bereich "-100 bis 100" wird zu "0 bis 2"
            float intensive = 1 + (value / 100F);
	
	
	    BufferedImage biDest = new BufferedImage(img.getWidth(),
	                               img.getHeight(),
	                               BufferedImage.TYPE_INT_ARGB);
		RescaleOp rescale = new RescaleOp(intensive, 10, null);
		
//		Passiert nichts:
	        rescale.filter(img, biDest);
	    
//		Auch hier passiert nichts:
//		biDest = rescale.filter(img, null);
	    
	    return biDest;
	}
```


Freu mich auf Hilfe.

Gruß, Thomas


PS: Hier ein Beispiel-Programm, wo das ganze funktioniert: 
java2s.com - ImageProcessingBrightnessandContrast


----------



## Fab1 (5. Mai 2012)

Hallo,

grundsätzlich kenne ich mich damit leider nicht aus, aber vielleicht hilft dir ja auch dieser Thread aus der FAQ http://www.java-forum.org/bilder-gu...18-helligkeit-kontrast-bildes-veraendern.html

Schöne Grüße

Fabi


----------



## Marco13 (5. Mai 2012)

Am geposteten sehe ich spontan nichts falsches. Was kommt denn bei

System.out.println(Integer.toHexString(img.getRGB(0,0));
System.out.println(Integer.toHexString(biDest .getRGB(0,0));

Bzw. wie äußert sich das "nichts passieren"?


----------



## beastofchaos (6. Mai 2012)

Fab1 hat gesagt.:


> Hallo,
> 
> grundsätzlich kenne ich mich damit leider nicht aus, aber vielleicht hilft dir ja auch dieser Thread aus der FAQ http://www.java-forum.org/bilder-gu...18-helligkeit-kontrast-bildes-veraendern.html
> 
> ...



Dieses Tutorial arbeitet jeden Pixel für sich durch und führt also unglaublich oft die Berechnung durch. Mir ist klar, dass es anders nicht gehen kann, aber ich dachte, eine von Java vorgegebene Klasse, würde das am leichtesten/ schnellsten bzw. optimalsten erledigen können. Was meint ihr dazu?





Marco13 hat gesagt.:


> Am geposteten sehe ich spontan nichts falsches. Was kommt denn bei
> 
> System.out.println(Integer.toHexString(img.getRGB(0,0));
> System.out.println(Integer.toHexString(biDest .getRGB(0,0));
> ...



Deinen Vorschlag werde ich später mal anschauen, wenn ich weiter schreibe, aber das "nichts passiert" meint, dass das Bild genauso wie vorher aussieht.

Was mich an meinem Code verwirrt ist diese Zeile:

```
rescale.filter(img, biDest);
```
In der Beschreibung steht, dass er das Ergebnis zurückgibt, gleichzeitig kann man dass Ziel aber auch als Parameter angeben (darf auch null sein).


----------



## beastofchaos (6. Mai 2012)

Marco13 hat gesagt.:


> System.out.println(Integer.toHexString(img.getRGB(0,0));
> System.out.println(Integer.toHexString(biDest .getRGB(0,0));



Hab es doch schon jetzt ausprobiert. Bei einem value-Wert von 50, kommen diese beiden Zeilen raus:

ffffffff
ffff00

In der zweiten Zeile fehlt iwie ein Wert (Der Alpha-Wert?) 

Hier mal ein zweites Beispiel, als ich die linke obere Ecke mal hellrot angemalt habe:

ffff6666 (Voller Alphawert + helles Rot)
ffa300   (helles Orange)



Bei Paint.NET wird bei Kontrast 50 die Farbe "FF6666" zu "FF7A7A".


----------



## Marco13 (6. Mai 2012)

beastofchaos hat gesagt.:


> Was mich an meinem Code verwirrt ist diese Zeile:
> 
> ```
> rescale.filter(img, biDest);
> ...



Etwas vereinfacht: Man kann dort ein Image übergeben, in das reingemalt werden soll - und wenn man keins übergibt, wird eins angelegt. Das ist, damit man selbst bestimmen kann, ob dort ein neues Bild erzeugt oder ein schon vorhandenes verwendet werden soll. (Das macht aber noch mehr - z.B. die Kompatimilitätschecks die in der Doku stehen)







beastofchaos hat gesagt.:


> Hab es doch schon jetzt ausprobiert. Bei einem value-Wert von 50, kommen diese beiden Zeilen raus:
> 
> ffffffff
> ffff00
> ...



Zumindest sieht man schonmal, das sich was ändert. Auch wenn man nicht viel erwarten darf, wenn man versucht, "Weiß" heller zu machen ....

Wie zeichnest du das Ergebnisbild? Poste ein KSKB!



beastofchaos hat gesagt.:


> Bei Paint.NET wird bei Kontrast 50 die Farbe "FF6666" zu "FF7A7A".



RescaleOp macht mehr als eine Kontrasterhöhung. Nämlich Kontrast UND Helligkeit auf einmal (je nach Parametern)


----------



## beastofchaos (6. Mai 2012)

Marco13 hat gesagt.:


> Zumindest sieht man schonmal, das sich was ändert. Auch wenn man nicht viel erwarten darf, wenn man versucht, "Weiß" heller zu machen ....



Was wäre denn der Standardwert, dass die Helligkeit sich nicht ändert? Im Tutorial war das 10 und beim Kontrast 1.




Marco13 hat gesagt.:


> Wie zeichnest du das Ergebnisbild? Poste ein KSKB!



Na, das wird jetzt toll mit dem "wie zeichne ich das". Ich müsste dich in mein 75 Klassen schweres Projekt einführen 

An einer Stelle wird das Bild gemalt, welches aber andere Klassen (als Zeichenobjekte ) aufruft... und allein die Kontrast-Einstellung läuft über ein Always-On-Top-Dialog. Hatte schon viele Probleme bei meinem Programm wegen Malen, aber das ist alles behoben. Deswegen zeige ich erstmal nur meinen Dialog. Vorweg verwende ich für alle solche Dialog die abstrakte Oberklasse "EDModel" und diese Unterklasse heißt "EDContrast". Kommentare fehlen zur Zeit leider noch, aber ich kenn mich mit meinem Code bestens aus und erkläre gerne im Nachhinein:

EDModell:


```
public abstract class EDModel extends JDialog{
    private int value;
    private JTextField fieldValue;
    private JScrollBar scroller;
    private boolean imageAdded;
    
    private BufferedImage oldImg;
    private Main main;
    
    public EDModel(Main target, String title){
        super(target);
        setModal(false);
        setTitle(title);
        setSize(132, 500);
        main = target;
        imageAdded = false;
        setVisible(false);
        setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE);
        addWindowListener(new WindowAdapter(){
            @Override
            public void windowClosing(WindowEvent evt){
                reset();
                setVisible(false);
            }
        });
        layoutPanel();
    }
    
    private void layoutPanel(){
        fieldValue = new JTextField();
        fieldValue.setHorizontalAlignment(SwingConstants.CENTER);
        fieldValue.setToolTipText("IntensivitÃ¤t");
        fieldValue.addKeyListener(new KeyAdapter(){
            @Override
            public void keyReleased(KeyEvent e) {
                if (fieldValue.getText().length() == 0){
                    return;
                }
                int newValue;
                try{
                    newValue = Integer.parseInt(fieldValue.getText());
                }catch (NumberFormatException exc){
                    newValue = scroller.getValue();
                }
                newValue = Math.min(newValue, scroller.getMaximum() - 1);
                newValue = Math.max(newValue, scroller.getMinimum());
                value = newValue;
                scroller.setValue(newValue);
                fieldValue.setText(newValue + "");
            }
        });
        scroller = new JScrollBar();
        scroller.setPreferredSize(new Dimension(40, 100));
        scroller.setOrientation(JScrollBar.VERTICAL);
        scroller.setToolTipText(getTitle());
        scroller.addAdjustmentListener(new AdjustmentListener(){
            public void adjustmentValueChanged(AdjustmentEvent e) {
                fieldValue.setText(scroller.getValue() + "");
                value = scroller.getValue();
                adoptValue();
            }
        });
        scroller.setValues(0, 1, -100, 101);

        JButton buttonReset = new JButton("");
        buttonReset.setToolTipText(Languages.get("Reset"));
        buttonReset.setIcon(new ImageIcon(getClass().getResource("/images/reset.png")));
        buttonReset.addActionListener(new ActionListener(){
            public void actionPerformed(ActionEvent e) {
                reset();
            }
        });
        JButton buttonOk = new JButton("OK");
        buttonOk.addActionListener(new ActionListener(){
            public void actionPerformed(ActionEvent e) {
                adoptAndClose();
            }
        });

        JPanel panel = new JPanel(new GridBagLayout());
        ((GridBagLayout)panel.getLayout()).columnWidths = new int[]{1, 2, 1};
        GridBagConstraints cons = new GridBagConstraints();
        cons.fill = GridBagConstraints.VERTICAL;
        cons.gridwidth = GridBagConstraints.REMAINDER;
        cons.gridheight = 5;
        cons.weightx = 0.5;
        cons.weighty = 1.0;
        panel.add(scroller, cons);
        cons.gridheight = 1;
        cons.weighty = 0.0;
        cons.fill = GridBagConstraints.BOTH;
        panel.add(fieldValue, cons);
        panel.add(buttonReset, cons);

        JPanel panelLow = new JPanel(new GridBagLayout());
        panelLow.add(buttonOk, cons);

        add(panel, BorderLayout.CENTER);
        add(panelLow, BorderLayout.SOUTH);
    }

    private Point getSpecialLocation(Center centerPanel) {
        Point loc = getLocation();
        int xRightPos = centerPanel.getLocationOnScreen().x +
                         centerPanel.getWidth();
        loc.y = centerPanel.getLocationOnScreen().y;
        loc.x = xRightPos - getWidth();
        return loc;
    }

    //------------------------------------------------------------------//

    public void actualize(){
        reset();
        oldImg = ((ImageDesign)Saves.properties.process.getPaints().clone()).getImage();
        setLocation(getSpecialLocation(main.center));
    }
    
    public void reset(){
        scroller.setValue(0);
        if (imageAdded){
        	Saves.properties.process.goBackward();
        	imageAdded = false;
        }
        Saves.properties.process.repairProcess();
    }
    
    
    public void close(){
    	reset();
    	dispose();
    }

    private void adoptAndClose(){
        setVisible(false);
        adoptValue();
        dispose();
    }

    private void adoptValue(){
        if (Saves.properties.process == null){
            return;
        }
    	if (Saves.properties.process.getActual().getMode() == Design.IMAGE){
    		Saves.properties.process.goBackward();
    	}
        Saves.properties.process.add(new ImageDesign(getAdoptedImage()));
        imageAdded = true;
    }
    
    private BufferedImage getAdoptedImage(){
    	BufferedImage newImg = new BufferedImage(oldImg.getWidth(), oldImg.getHeight(), BufferedImage.TYPE_INT_ARGB);
        Graphics2D g2d = newImg.createGraphics();
        try{
        	BufferedImage biDist = new BufferedImage(oldImg.getWidth(), oldImg.getHeight(), BufferedImage.TYPE_INT_ARGB);
        	Graphics2D g2d_2 = biDist.createGraphics();
        	try{
        		g2d_2.drawImage(oldImg, 0, 0, null);
        	}finally{
        		g2d_2.dispose();
        	}
            g2d.drawImage(adopt(biDist, value), 0, 0 , null);
        } finally{
            g2d.dispose();
        }
    	return newImg;
    }

    public abstract BufferedImage adopt(BufferedImage img, int newValue);
}
```

EDContrast:

```
public class EDContrast extends EDModel{
	public Main main;

	public EDContrast(Main target){
	    super(target, Languages.get("Contrast"));
	    this.main = target;
	}
	
	@Override
	public BufferedImage adopt(BufferedImage img, int value) {
		
		float intensive = 1 + (value / 100F);
	
	
	    BufferedImage biDest = new BufferedImage(img.getWidth(),
	                               img.getHeight(),
	                               BufferedImage.TYPE_INT_ARGB);
		RescaleOp rescale = new RescaleOp(intensive, 10, null);
		
//		Passiert nichts:
	    rescale.filter(img, biDest);
	    
	    System.out.println(Integer.toHexString(img.getRGB(0, 0)));
	    System.out.println(Integer.toHexString(biDest .getRGB(0,0)));
	    
//		Auch hier passiert nichts:
//		biDest = rescale.filter(img, null);
	    
	    return biDest;
	}
}
```


Ich erwarte keineswegs, dass ihr jetzt alles da durchschaut, aber wichtig ist in EDModell eig. nur "getAdoptedImage()" - das findet ihr ganz unten. 
EDContrast sollte man alles verstehen können 

Gruß, Thomas


----------



## Marco13 (6. Mai 2012)

Hmja, bei sowas wie

```
if (Saves.properties.process.getActual().getMode() == Design.IMAGE){
            Saves.properties.process.goBackward();
        }
```
rollen sich mir zwar pauschal erstmal die Fußnägel hoch, aber unabhängig davon: Erstelle ein KSKB, wo zwei Panels oder ImageIcons in einem Frame liegen, das eine zeigt das "vorher" Bild, und das andere das "Nachher" Bild. Wenn man dann sieht, dass die RescaleOp genau das tut, was sie tun soll, wird man feststellen, dass der Fehler vermutlich in Zeilen wie 
Saves.properties.process.add(new ImageDesign(getAdoptedImage()));
liegt.


----------



## beastofchaos (6. Mai 2012)

Marco13 hat gesagt.:


> Hmja, bei sowas wie
> 
> ```
> if (Saves.properties.process.getActual().getMode() == Design.IMAGE){
> ...



Ich denke mal du meckerst, weil ich da etwas in ner statischen Klasse speicher, aber das für mich bisher die einzige Lösung, wenn ich nicht in jedem Konstruktor der vielen Klassen in meinem Projekt ein Verweis auf meine Oberklasse einbauen will. Ich werd den ganzen Code eh noch durcharbeiten und kommentieren, bevor ich meine Abi-BLL darüber schreibe 
Ich schreibe morgen mal das Testprogramm und mach euch n ScreenShot, aber eigentlich ist das Testprogramm schon gegeben (siehe Link im ersten Beitrag)

Thomas


----------



## Marco13 (6. Mai 2012)

beastofchaos hat gesagt.:


> Ich schreibe morgen mal das Testprogramm und mach euch n ScreenShot, aber eigentlich ist das Testprogramm schon gegeben (siehe Link im ersten Beitrag)



Genau darauf will ich ja hinaus: In einem Beispiel macht die RescaleOp genau das, was sie soll. Bei dir angeblich nicht. Dann liegt der Fehler vermutlich bei dir. Die Frage, wie dieses Bild (das da erzeugt wird) denn angezeigt wird, hast du noch nicht beantwortet.


----------



## beastofchaos (6. Mai 2012)

Also ist so, wie ich es schon länger befürchtet habe. In meinem Programm funktioniert alles, solange ich ein BufferedImage mit "BufferedImage.TYPE_INT_RGB" erzeuge. Wenn ich stattdessen "ARGB" angebe, kommt ein "leeres" Bild bei mir raus.
Glaubt ihr, RescaleOP kann echt Bild mit speziellen alpha-Werten verarbeiten? Wäre schade, dann müsste ich doch zu der anderen Idee übergehen.

Hier mein Test-Programm (mit RGB -> funktionierend):

```
package tests;

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.MediaTracker;
import java.awt.Toolkit;
import java.awt.image.BufferedImage;
import java.awt.image.RescaleOp;
import java.io.File;

import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JPanel;

public class ContrastTest {
	
	public static void main(String[] args){
		JFileChooser searchPath = new JFileChooser();
        searchPath.setFileSelectionMode(JFileChooser.FILES_ONLY);
	    searchPath.setDialogTitle("Bilds laden...");
	    int result = searchPath.showOpenDialog(null);
	    if (result == JFileChooser.CANCEL_OPTION){
            return;
	    }
	    File file = searchPath.getSelectedFile();
	    
	    MediaTracker tracker = new MediaTracker(new JPanel());
	    
	    Image img = Toolkit.getDefaultToolkit().getImage(file.getPath());
	    tracker.addImage(img, 1);
	    try {
			tracker.waitForAll();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	    
		JFrame frame = new JFrame();
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		
		ClearTest test = new ClearTest();
		frame.getContentPane().add(new Painter(img));
		
		frame.pack();
		frame.setLocationRelativeTo(null);
		frame.setVisible(true);
	}
	
	public static class Painter extends JPanel{
		public BufferedImage img;
		public BufferedImage img2;
		public Dimension size;
		
		public Painter(Image img){
			super();
			setOpaque(false);
			setPreferredSize(new Dimension(1020, 310));
		    size = new Dimension(500, 300);
			this.img = drawOnBufferedImg(img);
		    this.img2 = adopt(this.img, 50);
		}
		
		@Override
		public void paintComponent(Graphics g){
			super.paintComponent(g);
		    g.drawImage(img, 5, 5, size.width, size.height, null);
		    g.drawImage(img2, size.width + 15, 5, size.width, size.height, null);
			g.drawLine(size.width + 10, 0, size.width + 10, size.height + 10);
		}
		
		//-----------------------------------------------------------------//
		
		public BufferedImage drawOnBufferedImg(Image img){
			BufferedImage bImg = new BufferedImage(size.width, size.height, BufferedImage.TYPE_INT_RGB);
			Graphics g2d = bImg.createGraphics();
			try{
				g2d.drawImage(img, 0, 0, null);
			} finally{
				g2d.dispose();
			}
			return bImg;
		}
		
		public BufferedImage adopt(BufferedImage img, int value) {
			float intensive = 1 + (value / 100F);
			BufferedImage biDest = new BufferedImage(img.getWidth(),
		                               img.getHeight(),
		                               BufferedImage.TYPE_INT_RGB);
			RescaleOp rescale = new RescaleOp(intensive, 10, null);
			
//			Funktioniert!!
			biDest = rescale.filter(img, null);

		    System.out.println(Integer.toHexString(img.getRGB(0, 0)));
		    System.out.println(Integer.toHexString(biDest.getRGB(0,0)));		    
		    return biDest;
		}
	}
}
```


Gruß, thomas


----------



## Marco13 (6. Mai 2012)

Also bei mir funktioniert das...


```
import java.awt.*;
import java.awt.image.*;
import java.io.*;
import javax.imageio.*;
import javax.swing.*;

public class ContrastTest {

    public static void main(String[] args) throws Exception{

        BufferedImage image = ImageIO.read(new File("image01.jpg"));

        JFrame frame = new JFrame();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        frame.getContentPane().add(new Painter(image));

        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }

    public static class Painter extends JPanel{
        public BufferedImage img;
        public BufferedImage img2;
        public Dimension size;

        public Painter(BufferedImage img){
            super();
            setOpaque(false);
            setPreferredSize(new Dimension(1020, 310));
            size = new Dimension(500, 300);
            this.img = img;
            this.img2 = adopt(this.img, 50);
        }

        @Override
        public void paintComponent(Graphics g){
            super.paintComponent(g);
            g.drawImage(img, 5, 5, size.width, size.height, null);
            g.drawImage(img2, size.width + 15, 5, size.width, size.height, null);
            g.drawLine(size.width + 10, 0, size.width + 10, size.height + 10);
        }

        public BufferedImage adopt(BufferedImage img, int value) {
            float intensive = 1 + (value / 100F);
            //RescaleOp rescale = new RescaleOp(new float[]{intensive,intensive,intensive, 1}, new float[]{10,10,10,0}, null);
            RescaleOp rescale = new RescaleOp(intensive, 10, null);

//          Funktioniert!!
            BufferedImage biDest = rescale.filter(img, null);

            System.out.println(Integer.toHexString(img.getRGB(0, 0)));
            System.out.println(Integer.toHexString(biDest.getRGB(0,0)));
            return biDest;
        }
    }
}
```


Wenn du den Typ von Ein/Ausgabebild ändern oder selbst bestimmen willst, musst du halt aufpassen. Bei ARGB könntest du auch mal schauen, ob die auskommentierte RescaleOp-Zeile nicht das tut, was du willst (aber was das ist, ist mir nicht ganz klar...)


----------



## beastofchaos (6. Mai 2012)

Welche auskommentierte zile meinst du jtzt. die in deinem code oder in meinem. bei mir hatte ich schon alles ausptobiert. ARGB ist fuer mein zeichenprogramm wichtig weshalb ich ja frage ob es auch anders ginge. ich mach mal morgn noch n paar test und spiel n bissl rum. vll kommt mir ja ein einfall 

Gruss Thomas


----------



## Marco13 (7. Mai 2012)

Ich sehe in deinem (letzten) Code keine auskommentierte Zeile. Ich meinte
RescaleOp rescale = new RescaleOp(new float[]{intensive,intensive,intensive, 1}, new float[]{10,10,10,0}, null);
aber sag' ggf. mal genau welchen Typ die Bilder haben sollen. (Sofern das letzte (wo es funktionierte) nur ein Test sein sollte)


----------



## beastofchaos (7. Mai 2012)

Naja im konstruktor soll als letzter parameter "BufferedImage.TYPE_INT_ARGB" uebergeben werden


----------



## beastofchaos (8. Mai 2012)

Okay, Großes Dankeschön für deine Hilfe. Ich weiß, ehrlich gesagt jetzt nicht was ich genau falsch gemacht habe, aber bei deinem Test-Programm (ohne die auskommentierte Zeile) funktioniert alles. Ich werde es jetzt in mein Programm einbauen und wenn dann immer noch was nicht funktioniert, meld ich mich nochmal.

Was genau hast du jetzt bei dir nochmal anders gemacht, abgesen davon, dass du das Bild mit ImageIO öffnest und ich es einfach auf ein BufferedImage male.

Gruß, Thomas


PS: Deine auskommentierte Zeile funktioniert nicht, aber ist ja jetzt auch egal - Trotzdem hier die Fehlermeldung:
Exception in thread "main" java.lang.IllegalArgumentException: 
Number of scaling constants does not equal the number of of color or color/alpha  components


PS2: Wenn ich die Funktion "adopt()" in meinem Programm einbaue, ändert sich immer noch nicht viel. Beim Punkt [0;0] bei Kontrast -100 wird "ffffffff" zu "a0a00".


----------



## Marco13 (9. Mai 2012)

Auch das lag noch in meiner TODO-Liste... 

Man muss wohl eine RescaleOp erstellen, die zu dem zu verarbeitenden Bild passt - d.h. es wäre gut, wenn man müßte, dass das reinkommende Bild immer den gleichen Typ hat (ansonsten muss man den Abfragen und ensprechend reagieren). Wenn's nicht klappt, schau ich (irgendwann) nochmal.


----------



## beastofchaos (9. Mai 2012)

Deswegen hatte ich die Idee, das Bild auf eines mit Type "ARGB" zu zeichnen, damit es einheitlich ist, bevor es reskaliert wird.


----------



## Marco13 (9. Mai 2012)

Da spricht auch aus anderen Gründen vieles dafür ( www.java-forum.org/spiele-multimedia-programmierung/120318-performance-bufferedimages.html ) aber mir war/ist nicht ganz klar, was die Anforderungen waren, und was nicht funktionierte (nachdem du gschreiben hast, wie es DOCH funktioniert........)


----------



## beastofchaos (9. Mai 2012)

Ja es hat im Test funktioniert, aber wohlmöglich gibt ImageIO nicht mit dem von mir gewollten Typ wieder. Performance-mäßig ist es natürlich besser, aber ich brauch das hier im Test erstmal, um herauszufinden, wie es am besten geht beim Typ ARGB.
Denkste, ich muss es jetzt die Art nehmen, jeden Pixel einzeln zu bearbeiten? Wäre auch ne Alternative, solange es schnell zu verarbeiten ist.

Gruß, Thomas


----------



## Marco13 (9. Mai 2012)

Nochmal als KSKB. Genaue Problembeschreibung posten

```
import java.awt.*;
import java.awt.image.*;
import java.io.*;

import javax.imageio.*;
import javax.swing.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

public class ContrastTest
{

    public static void main(String[] args) throws Exception
    {
        BufferedImage image = readAsARGB(new File("lena512color.png"));

        JFrame frame = new JFrame();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.getContentPane().setLayout(new BorderLayout());

        final Painter painter = new Painter(image);
        frame.getContentPane().add(painter, BorderLayout.CENTER);
        final JSlider s = new JSlider(-100, 100, 0);
        s.addChangeListener(new ChangeListener()
        {
            @Override
            public void stateChanged(ChangeEvent e)
            {
                painter.setContrast(s.getValue());
            }
        });
        frame.getContentPane().add(s, BorderLayout.SOUTH);

        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }

    private static BufferedImage readAsARGB(File file) throws IOException
    {
        BufferedImage tempImage = ImageIO.read(file);
        BufferedImage image = new BufferedImage(
            tempImage.getWidth(), tempImage.getHeight(), 
            BufferedImage.TYPE_INT_ARGB);
        Graphics2D g = image.createGraphics();
        g.drawImage(tempImage, 0, 0, null);
        g.dispose();
        return image;
    }

    public static class Painter extends JPanel
    {
        public BufferedImage img;
        public BufferedImage img2;
        public Dimension size;

        public Painter(BufferedImage img)
        {
            super();
            setOpaque(false);
            setPreferredSize(new Dimension(1020, 310));
            size = new Dimension(500, 300);
            this.img = img;
            this.img2 = adopt(this.img, 50);
        }

        public void setContrast(int contrast)
        {
            this.img2 = adopt(this.img, contrast);
            repaint();
        }

        @Override
        public void paintComponent(Graphics g)
        {
            super.paintComponent(g);
            g.drawImage(img, 5, 5, size.width, size.height, null);
            g.drawImage(img2, size.width + 15, 5, size.width, size.height, null);
            g.drawLine(size.width + 10, 0, size.width + 10, size.height + 10);
        }

        private BufferedImage adopt(BufferedImage img, int value)
        {
            float intensive = 1 + (value / 100F);
            RescaleOp rescale = new RescaleOp(new float[]
            { intensive, intensive, intensive, 1 }, new float[]
            { 10, 10, 10, 0 }, null);

            BufferedImage biDest = rescale.filter(img, null);

            System.out.println(Integer.toHexString(img.getRGB(0, 0)));
            System.out.println(Integer.toHexString(biDest.getRGB(0, 0)));
            return biDest;
        }
    }
}
```


----------



## beastofchaos (9. Mai 2012)

Funktioniert das bei dir. bin grad unterwega uns kanns erst morgn testen. Siwht naemlich funktionierend aua a

thomas


----------



## beastofchaos (10. Mai 2012)

Also im Test-Programm, im Tutorial und in meinem Programm wird das Bild bei niedrigem Kontrast schwarz und bei hohen scheint es eher eine größere Sättigung zu bekommen bzw. Ich es wird nicht wirklich intensiv kontrastreicher. Bei maximalen Kontrast müsste das Bild eigentlich schwarz-weiß sein. Wieso nicht? Egal, wie ich hoch es stelle - es funktioniert nicht wie ich es mir vorstelle. Aber  trotzdem danke, weil sich jetzt in meinem Pogramm wenigstens was ändert 

Ich werd mich wohl an diese Variante anpassen müssen: Pixel für Pixel (LuT)

Da kann ich gegebenenfalls auch die Berechnungen ändern/ anpassen 

Gruß, Thomas

PS: Ich werde das dann erst morgen machen und mich nochmal melden, wenn es Probleme gibt.


----------



## beastofchaos (14. Mai 2012)

As o bei Wikipedia zeigen an Bildern schonmal, wie Kontrast funktionieren sollte. Denn weder das LuT noch das was bisher hier gezeigt wurde, entspricht dem Verständnis von "Kontrast". Nun will ich mit Hilfe des Aufbaus der LUT (siehe zwei Beitrag) meine "eigene" Berechnung einbauen. Also das Gerüst für die Formel ist auch schon da. Auch wenn das LUT die Farbbereiche (R, G & B) einzelnd bearbeitet und ich das ganze wohl umbauen muss, dass jeder einzelne Pixel bearbeitet wird, was natürlich wiederum sehr ressourcenlastig ist, aber Java selbst bietet halt keine Hilfe hierbei... :/

Bei Wiki wird das ganze so formal definiert:

"Der Michelson-Kontrast Km (min. 0, max. 1) wird definiert als:
K_M = (L_max - L_min) / (L_max + L_min)
Dabei bezeichnet L die Leuchtdichte."

-> Wikipedia - Kontrast

Hier wird halt die Leuchtedichte für den Kontrast-Wert hergenommen, wobei ich hier weder weiß, wie ich die Leuchtdichte in einem Pixel berechnen, noch wie ich dann danach mit dem Kontrastwert umgehen muss.

Weiß jemand Rat?

Gruß, Thomas

PS: Ein Kontakt zum Hersteller von Paint.NET wäre wohl ein Geschenk haha


----------



## beastofchaos (14. Mai 2012)

Hab jetzt auch eine "Formel" zur Leuchtidchte bei Wiki rausgesucht. Bloß ist die schließlich nicht auf das RGB-Modell ausgerichtet und ist auch etlich lang, als dass sie anwendbar wäre... :/

(-> Wikipedia - Leuchtdichte)

Gruß, Thomas


----------



## Marco13 (14. Mai 2012)

Dass du nicht das machst, was du willst, ist ein anderer Sachverhalt, als dass der Computer nicht macht, was du willst  Ich dachte auch erst, dass du einen Histogrammabgleich willst, wie etwa in http://www.java-forum.org/codeschni...jcuda-java-bindings-fuer-cuda.html#post886332 - und NUR mit einer RescaleOp wäre das schwierig geworden. Wenn es nur darum geht, so einen Kontrast-Slider wie in den gängigen Bildverarbeitungsprogrammen nachzubauen... ja, zugegeben, ich bin da auch erst drüber gestolpert, weil die Rechnungen etwas un-intuitiv sind, aber ... schau halt mal...


```
import java.awt.*;
import java.awt.image.*;
import java.io.*;

import javax.imageio.*;
import javax.swing.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

public class ContrastTest
{

    public static void main(String[] args) throws Exception
    {
        BufferedImage image = readAsARGB(new File("799px-Male_Anolis_carolinensis.jpg"));

        JFrame frame = new JFrame();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.getContentPane().setLayout(new BorderLayout());

        final Painter painter = new Painter(image);
        frame.getContentPane().add(painter, BorderLayout.CENTER);
        final JSlider s = new JSlider(-99, 99, 0);
        s.addChangeListener(new ChangeListener()
        {
            @Override
            public void stateChanged(ChangeEvent e)
            {
                painter.setContrast(s.getValue() / 100.0f);
            }
        });
        frame.getContentPane().add(s, BorderLayout.SOUTH);

        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }

    private static BufferedImage readAsARGB(File file) throws IOException
    {
        BufferedImage tempImage = ImageIO.read(file);
        BufferedImage image = new BufferedImage(
            tempImage.getWidth(), tempImage.getHeight(), 
            BufferedImage.TYPE_INT_ARGB);
        Graphics2D g = image.createGraphics();
        g.drawImage(tempImage, 0, 0, null);
        g.dispose();
        return image;
    }

    public static class Painter extends JPanel
    {
        public BufferedImage inputImage;
        public BufferedImage outputImage;

        public Painter(BufferedImage inputImage)
        {
            super();
            setOpaque(false);
            setPreferredSize(new Dimension(1020, 310));
            this.inputImage = inputImage;
            this.outputImage = adopt(this.inputImage, 0);
        }

        public void setContrast(float contrast)
        {
            this.outputImage = adopt(this.inputImage, contrast);
            repaint();
        }

        @Override
        public void paintComponent(Graphics g)
        {
            super.paintComponent(g);
            g.drawImage(inputImage, 5, 5, getWidth() / 2 - 10, getHeight() - 5, null);
            g.drawImage(outputImage, getWidth() / 2 + 5, 5, getWidth() / 2 - 10, getHeight(), null);
            g.drawLine(getWidth() / 2, 0, getWidth() / 2, getHeight());
        }

        private static BufferedImage adopt(BufferedImage image, float value)
        {
            float scaleFactor = 1;
            float offset = 0;
            if (value > 0)
            {
                scaleFactor = 1 / (1 - value);
                offset = -0.5f * (scaleFactor - 1);
            }
            else if (value < 0)
            {
                scaleFactor = 1 + value;
                offset = 0.5f * (1 - scaleFactor);
            }
            RescaleOp rescale = new RescaleOp(
                new float[]{ scaleFactor, scaleFactor, scaleFactor, 1 }, 
                new float[]{ offset * 255.0f, offset * 255.0f, offset * 255.0f, 0 }, null);

            return rescale.filter(image, null);
        }
    }
}
```


----------



## beastofchaos (15. Mai 2012)

Also wenn ich das richtig sehe bei der Ausführung deines Programms, funktioniert dein Beispiel wie vorher schon - bloß bei einem negativ-bild und im Bereich von -1 bis 1 funktioniert es nicht bzw. seht man ein graues/ schwarzes Bild.

Gruß, Thomas


----------



## Marco13 (15. Mai 2012)

Das Bild sieht genauso aus, wie wenn man in GIMP den Kontrast zwischen -127 und 127 ändert: Bei -1 (also "kein Kontrast") ist alles grau. Bei +1 (also maximaler Kontrast) ist es praktisch schwarz-weiß (bzw. schwarz oder volle helligkeit/saturation).

Ich werd' jetzt nicht weiter rum-raten. :bahnhof:


----------



## beastofchaos (15. Mai 2012)

Also wenn es wirklich keinen hier gibt, der sich damit auskennt, muss ich es woh laufgeben, aber hast du dir angeschaut, was ich meine? Bei Wikipedia gibt es ein Beispiel (siehe mein Wikipedia-Link vor zwei Beiträgen) und sonst kann man das auch mal bei Paint.NET mal ausprobieren - es ist kostenlos und echt wirklich leicht zu verstehen.
Da ist es auch so wie Kontrast sein soll. Um so stärker der Kontrast um farbschwächer und mehr schwarz-weiß-farbend wird das Bild bzw am Ende ist das Bild ganz schwarz-weiß. Bei schwächeren bildet sich ein grauer Dunst.
Bei den ganzen Test hier wird bei stärkeren Kontrast die Farbintensität sogar stärker (also kein maximaler Kontrast von schwarz-weiß) und bei schwächerem Kontrast sieht es einfach so aus, als würde jemand ein graues Blatt drüberlegen, was wohl kein braucht, wenn er an seinem Bild rumbastelt mit einem Grafikprogramm.

Gruß, Thomas


----------



## Marco13 (15. Mai 2012)

Starte das gepostete Programm (das verwendete Bild ist das von Wikipedia). 
Schiebe den Slider in die Mitte des rechten Bildes: Das bild sieht aus wie das kontrastreiche Bild von Wikipedia. 
Schiebe den Slider ins hintere Viertel des linken Bildes: Das Bild sieht aus wie das kontrastarme Bild von Wikipedia. 

Starte GIMP (auch frei, nicht ganz so leicht zu verwenden, aber besser ), lade das Bild und wähle die Kontrasteinstellungen. 
Schiebe den Slider ganz nach links: Das Bild wird komplett grau. 
Schiebe den Slider ganz nach rechts: Das Bild wird "schwarz-weiß".
Verwende irgendeine Slider-Einstellung von GIMP und vergleiche sie mit der entsprechenden Slider-Einstellung im geposteten Program: Die Ausgabebilder sehen gleich aus.

:bahnhof:


----------



## beastofchaos (15. Mai 2012)

Könntest du vll ein ScrnShot davon machen, denn bei mir sieht es sowohl bei GIMP als auch m Testprogramm nicht schwarz-weiß aus bei starkem Kontrast.


----------



## Marco13 (15. Mai 2012)

Das "schwarz-weiß" stand in Anführungzeichen, weil es z.B. beim Testbild auch grüne Bereiche gibt, die eben schon von Anfang an volle Helligkeit hatten, und diesen auch beim höchsten Kontrast behalten.


----------



## beastofchaos (15. Mai 2012)

Das ist aber bei jedem Bild so dass es nicht schwarz-weiss wird. wie schon gesagt schau dir paint.net an. da geht es bei egal welchem bild.

thomas


----------



## Marco13 (15. Mai 2012)

Ich schaue mir Paint.NET nicht an. Vielleicht macht das jemand anderes, der sich dann aus den Fingern nuckelt, was für eine willkürliche Implementierung da wohl dahinterstecken mag. Warum soll durch eine Erhöhung des Kontrastes aus Grün auf einmal Weiß werden? Denk' dir eine Formel aus, bei der das passiert, und implementier' sie dann als RescaleOp oder irgendwie anders. Du hast für jeden einzelnen Pixel die Wahl zwischen 16777216 Farben, da ist für jeden was dabei.


----------



## beastofchaos (15. Mai 2012)

also es ist bei jedem bild so dass ers ei hohem kontrast schwarz-weiss. das ist doch rein theoretisch immer so. ich denke ich find keine formel. man muesste iwie die leuchtdichte einer farbe errechnen. wenn sie ueber 0.5 ist, strebt sie gegen 1 bzw. dunkel/schwarz. und unter 0.5 halt gegen weiss....

gruss, thomas


----------



## Marco13 (16. Mai 2012)

Die Helligkeit einer Farbe (im HSB-Sinn) hängt vom Maximum der R,G,B-Werte ab. Und bei G=255 ist die Helligkeit 1, und kann auch nicht höher werden. Wenn irgendein Malprogramm das irgendwie anders sieht, musst du irgendein anderes Verfahren implementieren.


----------



## beastofchaos (16. Mai 2012)

Hab jetzt doch einen Ansatz gefunden, der meinen Verdacht bestädigt. Werde mich mal damit rumärgern 

-> PHP-de - Helligkeit aus Hex-Wert bestimmen

bzw.

-> Wikipedia - Luminanz

Gruß, Thomas


----------



## beastofchaos (18. Mai 2012)

Das PHP-Forum konnte helfen und ich habe jetzt die Berechnungen von Paint.NET.
Wer sich den ganzen Ärger sparen will, hier sind zwei Methoden:


Den farblichen Kontrast verändern durch die Klasse RescaleOp

```
private BufferedImage adopt_ColorContrast(BufferedImage img, int value)
        {
        	value = (value == 100) ? 99 : value;
            float Dvalue = value / 100f;
            float scaleFactor = 1;
            float offset = 0;
            if (Dvalue > 0)
            {
                scaleFactor = 1 / (1 - Dvalue);
                offset = -0.5f * (scaleFactor - 1);
            }
            else if (Dvalue < 0)
            {
                scaleFactor = 1 + Dvalue;
                offset = 0.5f * (1 - scaleFactor);
            }
            RescaleOp rescale = new RescaleOp(
                new float[]{ scaleFactor, scaleFactor, scaleFactor, 1 }, 
                new float[]{ offset * 255.0f, offset * 255.0f, offset * 255.0f, 0 }, null);
 
            return rescale.filter(img, null);
        }
```

Den richtigen Kontrast, der anhand der Luminanz (Helligkeit) berechnet und auch am Ende ein Schwarz-Weiß-Bild liefert, beeinflussen

```
private BufferedImage adopt_LuminanceContrast(BufferedImage imgSrc, int contrast)
        {
        	BufferedImage imgDst = new BufferedImage(size.width, size.height, BufferedImage.TYPE_4BYTE_ABGR);
        	
        	Raster src = imgSrc.getRaster();
        	WritableRaster dst = imgDst.getRaster();

            int sX = 0;
            int sY = 0;
        	int absValue = Math.abs(contrast);
        	int brightness = 0;
        	
            int[] RGBA = null;
            int luminance;
            float[] YUV = new float[3];
            int[][] calculations = new int[256][256];
            
            int multiply;
            int divide;
            
            if (contrast < 0) {
				multiply = contrast + 100;
				divide = 100;
			} else if (contrast > 0) {
				multiply = 100;
				divide = 100 - contrast;
			} else {
				multiply = 1;
				divide = 1;
			}
            
            if (divide == 0) {
				for (int lum = 0; lum < 256; lum++) {
					for (int val = 0; val < 256; val++) {
						if ((lum + brightness) < 128)
							calculations[lum][val] = 0;
						else
							calculations[lum][val] = 255;
					}
				}
			} else if (divide == 100) {
				for (int lum = 0; lum < 256; lum++) {
					int shift = (lum - 127) * multiply / divide + 127 - lum + brightness;
					for (int val = 0; val < 256; val++) {
						calculations[lum][val] = correctColorBand(val + shift);
					}
				}
			} else {
				for (int lum = 0; lum < 256; lum++) {
					int shift = (lum - 127 + brightness) * multiply / divide + 127 - lum;
					for (int val = 0; val < 256; val++) {
						calculations[lum][val] = correctColorBand(val + shift);
					}
				}
			}
            
            for (int y=0; y < size.height; y++, sY++) {
            	sX = 0;
            	for (int x = 0; x < size.width; x++, sX++) {
                    RGBA = src.getPixel(sX, sY, RGBA);
                    luminance = Math.round(0.299F * RGBA[0] + 0.587F * RGBA[1] + 0.114F * RGBA[2]);
                    RGBA[0] = calculations[luminance][RGBA[0]];
                    RGBA[1] = calculations[luminance][RGBA[1]];
                    RGBA[2] = calculations[luminance][RGBA[2]];
                    dst.setPixel(sX, sY, RGBA);
            	}
            }
        return imgDes;
    }
```


Gruß, Thomas


----------

