# Wie kann ich Greyscale Bilder aus ushort binary files erzeugen?



## Bergtroll (30. Apr 2009)

Hallo werte Java Gemeinde,

ich hänge seit einigen Tagen an einem Problem. Und zwar wollte ich aus den RAW Dateien, die es hier zu finden gibt: The Stanford volume data archive Grayscale Bilder erzeugen. Die Binary Dateien enthalten, wie der Beschreibung zu entnehmen ist 16-bit integers. Außerdem liegen entsprechende TIFF-Files bei, die einem ja erfreulicherweise eine Bestätigung geben könnten, dass man keinen völligen Blödsinn fabriziert. Dummerweise habe ich bisher weder noch, nämlich garnix produziert!

Aaaaalso, meine Idee war, einen UShortBuffer irgendwie auf die Binaries loszulassen, der sich immer quasi "bildzeilenweise" durch die Datei frisst. Erst ein Array aufzubauen wollte ich vermeiden, da die Raw Images, mit denen das später funktionieren soll, ziemlich groß sein können. Und das wäre ja doof, das Bild als Array und BufferedImage im Speicher zu haben.... oder gibt es doch Gründe dafür? Sodann wollte ich irgendwie durch die UShort Elemente iterieren und jedes als Farbwert an BufferedImage.setGrayscalePixel oder eine Ähnliche Funktion übergeben. Aber da hängts, nix mit ähnlicher Funktion, es gibt setRGB für 8-bit, und das ist doof! Jemand eine Idee?

Achja, für die Leute, die es per Array erledigen wollen, hilft vielleicht der Thread weiter, aber ich habe es noch nicht ausprobiert, weil ich ja eine andere Lösung suche:
Java 2D - create iamge from pixels

Vielen Dank für das Bemühen


----------



## Marco13 (1. Mai 2009)

Bergtroll hat gesagt.:


> Aber da hängts, nix mit ähnlicher Funktion, es gibt setRGB für 8-bit, und das ist doof! Jemand eine Idee?



Jo, mehr als 8 bit gibt's soweit ich weiß(!) erstmal nicht - aber es kann gut sein, dass sich dieses (ja selten, d.h. fast nur im Medizinbereich benötigte) Format irgendwo in den Tiefen der BufferedImage, ColorSpace und ColorModel-Klassen verbirgt - kannst ja ggf. nochmal schauen. Ich glaube zwar, dass man sich notfalls ein eigenes ColorModel dafür schreiben könnte, aber ... das wäre (wenn es nötig wäre) wohl etwas aufwändiger.


Ansonsten wäre die pragmatische Lösung: Mit irgendeinem InputStream die Datei byte- oder blockweise lesen, jeweils 2 bytes zu einem short zusammenfassen, und den dann mit
int s = die zwo bytes
float f = (float)s / Short.MAX_VALUE;
int b = (int)(f * 255);
in ein byte umwandeln, und das dann für die R,G und B-Werte eines Pixels im BufferedImage verwenden...


----------



## Bergtroll (1. Mai 2009)

Hallo Marco, erstmal vielen Dank für dein Feedback, aber ich denke, ich werde eine Lösung finden müssen, welche die 16-bit Auflösung erhält. Ich habe nun an dieser Stelle Imaging gelesen, dass das Immediate Mode Image Buffer Model, also das mit BufferedImage, auf Imagedaten die komplett im Speicher liegen, ausgelegt ist. Aaaaaaber, ich habe eine Software gefunden, die mit den Eingabedaten umgehen kann und OpenSource mit Copyleft ist, diese findet sich hier: ImageJ. Leider ist der Quellcode eher kryptisch gehalten und relativ unkommentiert, aber immerhin. Ich melde mich, wenn ich was hinbekomme, bin aber weiters für jeden Hinweis dankbar.


----------



## Bergtroll (3. Mai 2009)

Aaaaalso, ich benutze jetzt doch erstmal die "Alles in den Speicher" Lösung. Man braucht ein 1D-Pixelarray, in diesem Fall exemplarisch vom Typ short. Scheinbar frisst das Ding auch negative Werte, bzw. behandelt der DataBufferUShort die Shorts als unsigned Shorts. Allerdings ist mir nicht klar, wie die klasse die farben skaliert, z.B. ist -10.000 heller 10.000. 


```
/**
	 * This function returns a 16-bit GrayScale BufferedImage out of an unsigned short array 
	 * @param width the intended width for the image in pixels
	 * @param height the intended height for the image in pixels
	 * @param pixelarray an array of pixels, where every pixel is completely characterized by its short value
	 * @return Returns a copy of this image as a TYPE_USHORT_GRAY BufferedImage 
	 */
	public static BufferedImage get16BitBufferedImage(int width, int height, short[] pixelarray) {
        BufferedImage bi = new BufferedImage(width, height, BufferedImage.TYPE_USHORT_GRAY);
        Raster raster = bi.getData();
        DataBufferUShort db = (DataBufferUShort)raster.getDataBuffer();
        System.arraycopy(pixelarray, 0, db.getData(), 0, db.getData().length);
        bi.setData(raster);
        return bi;
	}
```

Testen könnt ihrs schnell mal damit:

```
short[] pixelarray = {-10000,-10000,-10000,-10000,
							10000,10000,10000,10000,
							20000,20000,20000,20000,
							25000,25000,25000,25000};
image = get16BitBufferedImage(4, 4, pixelarray);
```

Übrigens spuckt er mir als intern verwendetes ColorModel folgendes aus
"ColorModel: #pixelBits = 16 numComponents = 1 color space = java.awt.color.ICC_ColorSpace@1100d7a transparency = 1 has alpha = false isAlphaPre = false"

Kann mir jemand sagen, was es mit dem @1100d7a auf sich hat? Bitte möglichst ohne Verweis auf INTERNATIONAL COLOR CONSORTIUM, da gibt es ziemlich viel, zu viel für die Kürze der Zeit, gerade, leider...

Mfg
Bergtroll


----------



## Bergtroll (8. Mai 2009)

So ich habe jetzt zwei Versionen zum Bilder erzeugen, beide erzeugen mir auch prima meine Bilder, aber im Vergleich zu ImageJ einfach zu dunkel, jemand eine Idee warum?

Die direkte Erzeugung

```
public static BufferedImage create16bitBufferedImage(DataBufferUShort buffer, int width, int height) {
		ColorSpace cs = ColorSpace.getInstance(ColorSpace.CS_GRAY);
		ColorModel ccm = new ComponentColorModel(cs,false,true,Transparency.OPAQUE,DataBuffer.TYPE_USHORT);
		ComponentSampleModel csm = new ComponentSampleModel(DataBuffer.TYPE_USHORT,width,height,1,width,new int[] {0});
		WritableRaster wr = WritableRaster.createWritableRaster(csm, buffer, new Point(0,0));
		BufferedImage img = new BufferedImage(ccm,wr,false,new Properties());
		return img;
	}
```

und mit vorgebautem BufferedImage als Ersatzteilelager

```
public static BufferedImage create16bitBufferedImage(DataBufferUShort buffer, int width, int height) {
	BufferedImage spare_parts = new BufferedImage(width,height,BufferedImage.TYPE_USHORT_GRAY);
	ColorModel cm = spare_parts.getColorModel();
	SampleModel sm = spare_parts.getSampleModel();
	WritableRaster wr = WritableRaster.createWritableRaster(sm, buffer, new Point(0,0));
	BufferedImage img = new BufferedImage(cm,wr,false,new Properties());
	return img;
}
```

Damit kann mans ausprobieren:

```
/**
	 * This function tries to write the given image to disk in the given format
	 * @param image The BufferedImage, that shall be written to disk
	 * @param format The format, die image should have one disk, currently only jpeg, gif and png are available
	 * @param file The file, the image should be written to
	 */
	public static void writeImageToDisk(BufferedImage image, String format, File file) {
		try {
			System.out.println("ColorModel: " + image.getColorModel());
			ImageIO.write(image, format, file);
			System.out.print("File: " + file.getName() + " has been written");
		} catch (IOException e) {
			System.out.print("File: " + file.getName() + " could not have been written");
		}
	}
```

und die Main Methode, angepasst auf die Stanford Volume Data MRBrain Raw Dateien.

```
public static void main(String[] args) {
		for (int i = 1; i <= 109; i++) {
			FileInputStream file_input;
			try {
				DataBufferUShort image_buffer = new DataBufferUShort(256*256);
				file_input = new FileInputStream("src/ct_brain/MRbrain."+i);
				DataInputStream image_data = new DataInputStream(file_input);
				int j=0;
				while (true) {
			        try {
			          image_buffer.setElem(j, image_data.readShort());
			          j++;
			          
			        } catch (EOFException eof) {
			          System.out.println ("End of File");
			          break;
			          
			        } catch (IOException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
				BufferedImage image = create16bitBufferedImage(image_buffer, 256, 256);
				writeImageToDisk(image, "png", new File("src/Brain"+i+".png"));
			} catch (FileNotFoundException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
```


----------



## Bergtroll (13. Mai 2009)

Sodalla, nach langem Suchen endlich des Rätsels fast schon banale Lösung. Sowohl die beiligenden TIFFs als auch ImageJ skalieren die Messwerte über den ganzen Zahlenraum, den Short zur Verfügung stellt. Dazu sollte man erstmal das Minimum und Maximum in seinen Messerten finden und dieses dann auf die Shorts abbilden. Der Vollständigkeit halber hier die Codes

Minimum und Maximum finden

```
private static int[] findMinMax(short[] pixels){
		int[] minmax = new int[2];
		int min = 65535;
		int max = 0;
		int value;
		
		for (int i = 0; i < pixels.length; i++) {
			value = Array.getShort(pixels, i);
			//if value is negative, invert it to positive and add Short.MinValue to make in unsigned
			value = (value<0)? ~value - Short.MIN_VALUE : value;
			if (value<min)
				min = value;
			if (value>max)
				max = value;
		}
		Array.setInt(minmax, 0, min);
		Array.setInt(minmax, 1, max);
		return minmax;
	}
```

Skalierungsverhältnis berechnen

```
private static int calculateRatio(int[] minmax) {
	return 65535/(Array.getInt(minmax, 1)-Array.getInt(minmax, 0));
}
```

und zu guter letzt ein Minimalbeispiel der main Funktion

```
public static void main(String[] args) {
		
		for (int i = 1; i <= 109; i++) {
		//FileInputStream file_input;
			try {
				
				short[] pixels = new short[256*256];
				File f = new File("src/mr_brain/MRbrain."+i);
				FileImageInputStream image_data = new FileImageInputStream(f);
				int j=0;
				
				while (true) {
			        try {
			          Array.setShort(pixels, j, image_data.readShort());
			          j++;
			        } catch (EOFException eof) {
			          System.out.println ("End of File");
			          break;
			        } catch (IOException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
				}
				
			int[] minmax	= findMinMax(pixels);
			int ratio		= calculateRatio(minmax);
			int min			= Array.getInt(minmax, 0);
			DataBufferUShort image_buffer = new DataBufferUShort(256*256);
			
			for (j = 0; j < pixels.length; j++) {
				int value = (Array.getInt(pixels, j) - min)*ratio;
				image_buffer.setElem(j, value);
			}
			
			BufferedImage image = create16bitBufferedImage(image_buffer, 256, 256);
			writeImageToDisk(image, "png", new File(String.format("src/Brain%03d.png", i)));
							
			} catch (FileNotFoundException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
```

Viel Freude damit


----------



## Marco13 (14. Mai 2009)

Tut mir leid dass ich dir dann nicht mehr weiterhelfen konnte, aber ich find's immer super, wenn jemand selbst eine Lösung findet, und die dann noch im Forum postet: :toll: :applaus:


----------



## Bergtroll (14. Mai 2009)

Danke für die Lorbeeren *g*, nja, so gehört das doch, sonst isses mit dem Forennutzen nicht weit her, und außerdem, wenn mir mal meine ganze veraltete PC Infrastruktur mal abnippelt, finde ich mein Zeug wenigstens online wieder


----------



## Marco13 (14. Mai 2009)

Frei nach Linus Torvalds: "Only wimps use tape backup: real men just upload their important stuff on ftp, and let the rest of the world mirror it."


----------

