# OpenGL Screenshot in iMove (als Greenscreen) => Farben invertiert?



## Friedhelm (27. Aug 2010)

Ich habe ein Problem. Wenn ich ein OpenGL Screenshot mache und das Bild in iMovie als Greenscreen Overlay einlade, dann sieht es so komisch aus. Bitte einfach mal die Bilder vorher/nachher ansehen.

Dazu benutze ich folgende Routine:

[Java]

	int[] nBits = {8, 8, 8};
		int[] bOffs = {0, 1, 2};


		ByteBuffer pixels;			
		DataBuffer db;
		WritableRaster raster;

		ColorSpace cs = ColorSpace.getInstance(ColorSpace.CS_sRGB);
		ColorModel cm = new ComponentColorModel(cs, nBits, false, false, Transparency.TRANSLUCENT, DataBuffer.TYPE_BYTE);

		byte [] byteA=new byte[callbackLFEngine.getScreenWidth()*callbackLFEngine.getScreenHeight()*3];	

		pixels = ByteBuffer.allocateDirect(callbackLFEngine.getScreenWidth()*callbackLFEngine.getScreenHeight()*3);		
		gl.glReadPixels(x, y, callbackLFEngine.getScreenWidth(), callbackLFEngine.getScreenHeight(), GL2.GL_RGB, GL2.GL_UNSIGNED_BYTE, pixels);

		pixels.get(byteA);		
		db = new DataBufferByte(byteA, callbackLFEngine.getScreenWidth()*callbackLFEngine.getScreenHeight(), 0);

		raster = Raster.createInterleavedRaster(db, callbackLFEngine.getScreenWidth(), callbackLFEngine.getScreenHeight(), callbackLFEngine.getScreenWidth()*3, 3, bOffs, null);		
		screenshotImage = new BufferedImage(cm, raster, false, null);//new java.util.Hashtable());

		// * Flip Image Y Axis *
		AffineTransform tx = AffineTransform.getScaleInstance(1, -1); 
		tx.translate(0, -screenshotImage.getHeight(null)); 
		AffineTransformOp op = new AffineTransformOp(tx, AffineTransformOp.TYPE_NEAREST_NEIGHBOR); 
		screenshotImage = op.filter(screenshotImage, null); 

[/Java]

Weiß jemand was da falsch ist?


----------



## Marco13 (28. Aug 2010)

"Komisch" ist sehr zurückhaltend formuliert. Es wird ... nicht invertiert ... und nicht schwarzweiß ... aber beides ein bißchen ???:L Wenn es NUR "invertiert" aussehen würde (das wäre es nicht, aber zumindest würden die Farben vertauscht aussehen) dann würde ich auf
int[] bOffs = {2, 1, 0};
tippen, aber da sind die Farben ja ganz weg...!? Vielleicht mal an einem 5x1 Pixel großen Bild mit Pixeln in den Farben Schwarz,R,G,B,Weiß testen - da kann man sich alle Pixel mal im Debugger oder mit System.out.println ansehen, und erkennt vielleicht wo was schiefgeht.


----------



## Friedhelm (28. Aug 2010)

Vieleicht hilft die Information...

Ich lade Bilder ein (können auch Gif-Animationen sein) und zeige die dann auf dem OpenGL Screen als Sprite an. Mache einen Screenshot und dann kommt in iMovie sowas bei raus (1. Post).

Mein Image-Loader (mit GIF Ani Funktion):

[Java]

private ImageData writeImage(File file) {

		boolean isGif 	= false;
		int frames 		= 1;
		int fps 		= 0;

		try {

			BufferedImage importImage 	= null;

			if(file.getName().toLowerCase().contains(".gif")) { // * GIF Image, also nach Tile BufferedImage konvertieren *

				ImageReader reader = ImageIO.getImageReadersByFormatName("gif").next();

				isGif = true;


				try{	
					reader.setInput(new FileImageInputStream(file));					

					// * Daylay/FPS nur auslesen, wenn mehr als 1 Frame vorhanden *
					if(reader.getNumImages(true) > 1) {			

						IIOMetadata meta = reader.getImageMetadata(1);
						IIOMetadataNode imgRootNode = null;						
						imgRootNode = (IIOMetadataNode)
						meta.getAsTree("javax_imageio_gif_image_1.0");
						IIOMetadataNode gce = (IIOMetadataNode) imgRootNode.getElementsByTagName("GraphicControlExtension").item(0);
						int animDelay = Integer.parseInt(gce.getAttribute("delayTime"));

						fps = (int)(1000.0f/(animDelay*10));
					}

				}catch(IllegalArgumentException e) { e.printStackTrace();}



				boolean readingImage = false;
				try {

					reader.setInput(new FileImageInputStream(file));

					int totalImages = reader.getNumImages(true);
					int width 		= reader.getWidth(0);
					int height 		= reader.getHeight(0);

					importImage = new BufferedImage(width*totalImages, height, BufferedImage.TYPE_INT_ARGB);


					readingImage = true;
					int currentImage = 0;

					while(readingImage) {

						BufferedImage image = reader.read(currentImage);								
						Log.i("Importer","GIF loaded frame: " + (currentImage +1) + " / " + totalImages );

						importImage.createGraphics().drawImage(image, null, width*currentImage, 0);

						image.flush();
						currentImage++;

						if(currentImage == reader.getNumImages(true)) {
							readingImage = false;
							reader.reset();
							reader.dispose();
						}
					}
					frames = currentImage;


				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			else { // * Kein animiertes Gif, also 1:1 als PNG speichern *

				importImage = ImageIO.read(file);									
			}

			String pureFilename = getPureFilenameWithoutExtension(file.getName());
			Log.i("Importer","Write PNG: "+new java.io.File(".").getCanonicalPath() +"/imported_images/" + file.getName() );

			File target = new File("./imported_images");
			if(!target.exists()) target.mkdir();

			// * Original ins Archiv *
			copy(file, new File(new java.io.File(".").getCanonicalPath() + "/imported_images/"+file.getName()));


			// * "loaded_anim_image.png" zum verwenden *
			ImageIO.write(importImage, "png", new File("./loaded_anim_image.png"));	


		} catch (Exception e) {	
			e.printStackTrace(); 

			frames = -1; // * Fehler beim Laden/Schreiben des Images *
		}


		ImageData imageData = new ImageData();
		imageData.frames = frames;
		imageData.fps = fps;

		return imageData;
	}

[/Java]


Ich habe eine Fast-Lösung gefunden, in dem ich den Screenshot nochmal in ein TYPE_BYTE_INDEXED BufferedImage kopiere.

Aber ehrlich gesagt... irgendwas stimmt da nicht, das Grün ist voller Punkte (siehe Screenshot 3):


```
// * In Indexed Farben (8-Bit GIF) verwandeln, sonst klappt der Greenscreen nicht, aber gruener Hintergrund ist jetzt zerstoert *
			screenshotImage = new BufferedImage(screenimage.getWidth(), screenimage.getHeight(), BufferedImage.TYPE_BYTE_INDEXED); 		
			Graphics2D g2d = screenshotImage.createGraphics(); 
			g2d.drawImage(screenimage, 0,0,screenimage.getWidth(), screenimage.getHeight(),null);				
			g2d.dispose();
```


----------



## Guest2 (28. Aug 2010)

Hast Du mal versucht, ob der Fehler weiterhin besteht, wenn Du den screenshot so anfertigst wie ich es in Deinem anderen Thread gezeigt habe?  

Gruß,
Fancy


----------



## Friedhelm (28. Aug 2010)

> Hast Du mal versucht, ob der Fehler weiterhin besteht, wenn Du den screenshot so anfertigst wie ich es in Deinem anderen Thread gezeigt habe?
> 
> Gruß,
> Fancy



Ich verfahre nur noch so, ansonsten bekomme ich Fehler 

Aber das mit den zerstörten Farben (siehe Screenshot mit Hund und dem zerstörten grünen Hintergrund) habe ich immer noch. Ich vermute mal das die Mischung GIF (Indexed Colors) und PNG (RGB) sich bei OPenGL + glReadPixels irgendwie beisst und das ich da Ordnung rein bringen muss.

Nur weiss ich nicht wie ;(


----------



## Marco13 (28. Aug 2010)

Ach so, das ganze wird irgendwie als GIF geladen. Das ganze jetzt auf die Schnelle nachzuvollziehen ist ein bißchen schwierig, und bei sowas wie ImageIO oder ImageReader weiß man AFAIK erstmal nicht, welchen Type das BufferedImage hat, das man da zurückbekommt. Das Bild in ein anderes Bild (mit bekanntem Typ) reinzeichnen zu müssen ist nicht sooo ungewöhnlich. Aber INDEXED kann eigentlich nicht funktionieren ... Irgendwie muss man dafür sorgen, dass der Typ zu der GL-Zeichenroutine passt. Unerfreulicherweise gibt es bei GL kein GL_ARGB, und beim BufferedImage kein 3BYTE_RGB (nur 3BYTE_BGR - deswegen mein Tipp mit dem {2,1,0}  ). Beim Kopieren von Bildern mit GL kann man als Quelltyp GL_BGR angeben, und damit hatte es in meinem konkreten Fall dann geklappt. Bei einem KSKB würde ich vielleicht nochmal genauer schauen.


----------



## Guest2 (28. Aug 2010)

Ich meine auch die Art wie ich glReadPixels angewendet habe. Mit BGRA obwohl mein BufferedImage vom Typ TYPE_INT_RGB ist. Ich kann zumindest so ad hoc in deinem Beispiel oben nicht exakt nachvollziehen wo welche Pixeldaten gerade wie interpretiert werden (zumindest nicht so das ich die Hand dafür ins Feuer legen würde). 

Ich weis auch nicht wie iMove die Pixeldaten interpretiert. Nur vermutlich anders als Du sie übergibst. 

Das das was mit dem Einlesen aus GIFs zu tun hat würde ich ausschließen. Mit glReadPixels liest Du aus dem Framebuffer. Und wenn Du nichts Besonderes gemacht hast, dann sind das immer RGBA interpretiert aus 32Bit Integer little endean.

 (Btw. Du schriebst irgendwo, das Du einen Mac nutzt, der hat aber schon eine x86 CPU oder ist das noch ein ganz alter?)

Gruß,
Fancy

Edit: Liest er den nun aus dem FrameBuffer (glReadPixels) oder aus einem BufferedImage?. Und ja, ein KSKB ist immer gut!


----------



## Friedhelm (28. Aug 2010)

Marco13 hat gesagt.:


> Irgendwie muss man dafür sorgen, dass der Typ zu der GL-Zeichenroutine passt. Unerfreulicherweise gibt es bei GL kein GL_ARGB, und beim BufferedImage kein 3BYTE_RGB (nur 3BYTE_BGR - deswegen mein Tipp mit dem {2,1,0}  ). Beim Kopieren von Bildern mit GL kann man als Quelltyp GL_BGR angeben, und damit hatte es in meinem konkreten Fall dann geklappt. Bei einem KSKB würde ich vielleicht nochmal genauer schauen.



Also 2,1,0 habe ich schon probiert... keine Änderung. Tja, ich weiß auch net. Ich werd wohl noch ein paar Stunden mit Farbtests verbringen müssen ;(


----------



## Friedhelm (28. Aug 2010)

Guest2 hat gesagt.:


> (Btw. Du schriebst irgendwo, das Du einen Mac nutzt, der hat aber schon eine x86 CPU oder ist das noch ein ganz alter?)



Das ist ein MacMini late 2009 (9400M Grafikkarte) + Snow Leopard (alle Updates)



> Edit: Liest er den nun aus dem FrameBuffer (glReadPixels) oder aus einem BufferedImage?. Und ja, ein KSKB ist immer gut!



Also ich lade z.B. ein animiertes GIF... mache daraus ein Tiled PNG und lade dann die Tiles als Texturen in die Grafikkarte. Dann Lass ich den Sprite über den Bildschirm sausen (alles OpenGL) und lese dann einen Screenshot mit glReadPixels (siehe Source oben). Dann speichere ich davon JPEG's. Öffne ich das in Quicktime ist alles ganz normal. Lade ich die in iMovie 09 als Greenscreen Overlay, dann bekomme ich das Invertierte Bild. Wandle ich die Bilddaten vorher in INDEXED, dann werden die Original-Farben zerstört (8Bit 256 Colors?), aber man kann in iMovie damit arbeiten. 

Echt seltsam 

Was ist KSKB???


Nachtrag:

Achso, das sind natürlich keine JPEG Einzelbilder, sondern ich speichere die JPEGS in den MJPEG Container. Aber daran kann es meiner Meinung nach nicht liegen... 

[Java]


	public static int swapInt(int v)
	{
		return  (v >>> 24) | (v << 24) | ((v << 8) & 0x00FF0000) | ((v >> 8) & 0x0000FF00);
	}

	public static short swapShort(short v)
	{
		return (short)((v >>> 8) | (v << 8));
	}

	public static byte[] intBytes(int i)
	{
		byte[] b = new byte[4];
		b[0] = (byte)(i >>> 24);
		b[1] = (byte)((i >>> 16) & 0x000000FF);
		b[2] = (byte)((i >>> 8) & 0x000000FF);
		b[3] = (byte)(i & 0x000000FF);

		return b;
	}

	public static byte[] shortBytes(short i)
	{
		byte[] b = new byte[2];
		b[0] = (byte)(i >>> 8);
		b[1] = (byte)(i & 0x000000FF);

		return b;
	}



		public byte[] fcc = new byte[]{'s','t','r','h'};
		public int cb = 64;
		public byte[] fccType = new byte[]{'v','i','d','s'};
		public byte[] fccHandler = new byte[]{'M','J','P','G'};
		public int dwFlags = 0;
		public short wPriority = 0;
		public short wLanguage = 0;
		public int dwInitialFrames = 0;
		public int dwScale = 0; 
		public int dwRate = 1000000;
		public int dwStart = 0;
		public int dwLength = 0;
		public int dwSuggestedBufferSize = 0;
		public int dwQuality = -1;
		public int dwSampleSize = 0;
		public int left = 0;
		public int top = 0;
		public int right = 0;
		public int bottom = 0;



		public byte[] fcc = new byte[]{'s','t','r','f'};
		public int cb = 40;
		public int biSize = 40;
		public int biWidth = 0;
		public int biHeight = 0;
		public short biPlanes = 1;
		public short biBitCount = 24;
		public byte[] biCompression = new byte[]{'M','J','P','G'};
		public int biSizeImage = 0; 
		public int biXPelsPerMeter = 0;
		public int biYPelsPerMeter = 0;
		public int biClrUsed = 0;
		public int biClrImportant = 0;




private byte[] writeImageToBytes(BufferedImage image) throws Exception	{

		IIOImage iioImg = new IIOImage(image, null, null);

		// Image in den ByteArrayOutputStream schreiben
		ByteArrayOutputStream baos = new ByteArrayOutputStream();

		Iterator imageWriters = ImageIO.getImageWritersByFormatName("jpeg");

		if ( imageWriters.hasNext() ) {

			ImageWriter writer 			= (ImageWriter) imageWriters.next();
			JPEGImageWriteParam param 	= new JPEGImageWriteParam(Locale.getDefault());
			ImageOutputStream ios 		= ImageIO.createImageOutputStream(baos);

			param.setCompressionMode(JPEGImageWriteParam.MODE_EXPLICIT);
			param.setCompressionQuality(0.8f);

			writer.setOutput(ios);
			writer.write(iioImg.getMetadata(), iioImg, param);
			ios.flush();
			writer.dispose();
		}

		baos.close();

		return baos.toByteArray();
	}



public void addImageToMJPEGStream(BufferedImage image) throws Exception
	{
		byte[] fcc = new byte[]{'0','0','d','b'};
		byte[] imagedata = writeImageToBytes(image);
		int useLength = imagedata.length;
		long position = aviChannel.position();
		int extra = (useLength+(int)position) % 4;
		if(extra > 0)
			useLength = useLength + extra;

		indexlist.addAVIIndex((int)position,useLength);

		aviOutput.write(fcc);
		aviOutput.write(intBytes(swapInt(useLength)));
		aviOutput.write(imagedata);
		if(extra > 0)
		{
			for(int i = 0; i < extra; i++)
				aviOutput.write(0);
		}
		imagedata = null;
	}



[/Java]


----------



## Friedhelm (29. Aug 2010)

So, ich habe mir mal die ColorModels angesehen:

Screenshot von OpenGL:
ColorModel: ColorModel: #pixelBits = 24 numComponents = 3 color space = java.awt.color.ICC_ColorSpace@49bdc9d8 transparency = 1 has alpha = false isAlphaPre = false

BufferedImage:
ColorModel (after): ColorModel: #pixelBits = 24 numComponents = 3 color space = java.awt.color.ICC_ColorSpace@49bdc9d8 transparency = 1 has alpha = false isAlphaPre = false

JPEG-Image (das wird gespeichert):
ColorModel (JPG): ColorModel: #pixelBits = 24 numComponents = 3 color space = java.awt.color.ICC_ColorSpace@49bdc9d8 transparency = 1 has alpha = false isAlphaPre = false


Kann es vielleicht sein das iMovie kein Alpha mag, oder im Image da was durcheinander gekommen ist? Wie kann ich Transparency ausschalten in einem BufferedImage?


----------



## Friedhelm (29. Aug 2010)

Ich habe eben etwas gefunden unter: Farbfilter (unten)

Dort entsteht genau der Effekt wie ich mit iMovie habe (Negativ Bild).


[Java]

Negativfilter

    Eine andere Form des RGBImageFilters kann zur Erzeugung eines Positivs aus einem Negativ (bzw. umgekehrt) eingesetzt werden: 

    Der Filter invertiert jeden einzelnen Farbanteil. Hierzu wird der Wert des Farbanteils vom Maximalwert 255 abgezogen und als neuer Farbwert verwendet.

      class NegativFilter extends RGBImageFilter {

        public NegativFilter() {
          // keine positionsabhängige Filterung
          canFilterIndexColorModel = true;
        }

        public int filterRGB(int x, int y, int rgb) {
          return  (0xFF000000 | (0xFFFFFFFF - rgb));
        }
      }

    Die Invertierung der einzelnen Farbanteile kann am einfachsten vorgenommen werden, wenn man den RGB-Wert des Pixels von dem Maximalwert bildet:

      return  (0xFF000000 | (0xFFFFFFFF - rgb));

    Diese Differenzbildung beeinträchtigt allerdings auch die Deckung des Farbwertes. War die Deckung zuvor maximal, ist sie nach der Differenzbildung gleich 0. Das Bild wäre also in diesem Fall transparent. Deshalb wird anschließend eine ODER-Verknüpfung mit dem Wert 0xFF000000 vorgenommen. Dadurch wird die Deckung auf 255 gesetzt. 

    Unterschiedliche Alpha-Werte werden also bei dieser Filterung nicht unterschieden. Unabhängig vom Alpha-Wert des Originalpixels wird der Alpha-Wert des Ausgangspixels auf 255 gesetzt. 

    Das vollständige Listing des NegativFilter mit einem Testprogramm ist auf der CD enthalten. 

[/Java]


----------



## Friedhelm (29. Aug 2010)

Eigentlich wollte ich gerade schlafen gehen, doch bei der letzten Aktion wollte ich nur mal so ein paar Pixel kopieren... und habe dann eine 100 Pixel Linie vom Original BufferedImage (glScreenshot) auf ein neues Indexed BufferedImage kopiert. Das funktionierte dann auch in Verwendung mit iMovie (Bild war nicht mehr invertiert).

Doch wie es aussieht ist iMovie sehr pingelig... und es klappt nicht immer, die Randlösung am rechten Rand einen ca. 20 Pixel Balken mit den zerstörten Daten zu platzieren funktioniert in iMove leider nicht immer.


----------



## Marco13 (29. Aug 2010)

" KSKB " steht übrigens für was, was erscheint, wenn man mal kurz mit der Maus über " KSKB " stehen bleibt


----------



## Friedhelm (29. Aug 2010)

Marco13 hat gesagt.:


> " KSKB " steht übrigens für was, was erscheint, wenn man mal kurz mit der Maus über " KSKB " stehen bleibt



:lol:


----------



## Friedhelm (30. Aug 2010)

Ok, also es liegt an iMovie 09. Ich habe das Problem jetzt so gelösst, dass ich im Abstand von 9 x/y Pixeln jeweils einen zerstoerten Grünen Pixel in ein neues Bild setze, und auf den übrigen Pixeln eine RGB Copy mache.

Meine bisherigen Tests waren dannach erfolgreich. Damit hat sich das hier erledigt


----------



## Marco13 (30. Aug 2010)

Klingt nach einem üblen Hack..


----------



## Friedhelm (30. Aug 2010)

Marco13 hat gesagt.:


> Klingt nach einem üblen Hack..



Ist es auch, da iMovie 09 wohl nach dem letzten Update ein üblen Bug hat:

- Apple - Support - Discussions - Transparent Green Screen ...
- [iMovie] Greenscreen Problem-falsche Farbendarstellung
- PNG Problem in iMovie 09! Bitte um Hilfe!

Ich kann froh sein, dass ich durch meinen Hack das umgehen konnte, sonst würde ich noch in 2 Jahren nach dem Fehler in OpenGL und Images suchen


----------

