RGB888 till RGB565

Elektronik- och mekanikrelaterad mjukvara/litteratur. (T.ex schema-CAD, simulering, böcker, manualer mm. OS-problem hör inte hit!)
vedderb
Inlägg: 70
Blev medlem: 16 oktober 2010, 11:07:09

RGB888 till RGB565

Inlägg av vedderb »

Hej

Jag har en oled-display med 16-bitars färger och vill konvertera bilder för att visa dem på den. Jag skrev ett java-program som gör om RGB888 till RGB565 genom att helt enkelt trunkera de 8 bitarna för varje pixel till 5 respektive 6 respektive 5 bitar.

Följande del gör det jobbet

Kod: Markera allt

for (int i = 0; i < img.getWidth();i++) {
	for (int j = 0;j < img.getHeight();j++) {

		int a = 0, b = 0, red, green, blue;
		a = img.getRGB(i, j);
						
		red = (a & 0xFF0000) >>> 16;
		green = (a & 0x00FF00) >>> 8;
		blue = a & 0x0000FF;
						
		a = 	((blue >>> 3) << 11) | 
				(((green >>> 2) << 5) & 0x7E0) |
				((red >>> 3) & 0x1F);
						
		b = a >>> 8;

		buffer[j*2] = (byte)b;
		buffer[j*2 + 1] = (byte)a;
	}
	
	out.write(buffer);
}
Problemet är att kvantiseringsfelet i konverteringen gör att vissa färgövergångar ser lite dåliga ut. Efter lite sökande på nätet kom jag fram till att jag behöver göra dithering (http://en.wikipedia.org/wiki/Dither). Jag hitta dock inte något konkret exempel på hur man kan göra det från 24-bitars färger till 16-bitars färger, utan bara ner till 8-bitar, 4-bitar etc. Så min fråga är: hur bör jag göra? Kodexempel eller javabibliotek skulle hjälpa.

Jag ber om ursäkt ifall frågan är på fel forum, men displayen är elektronikrelaterad i alla fall då jag byggde allt för att styra den själv.

Tack
sneaky
Inlägg: 1621
Blev medlem: 22 juni 2009, 18:38:42

Re: RGB888 till RGB565

Inlägg av sneaky »

Det här är inte svaret du vill ha men jag vet att man i Photoshop kan spara BMP-filer direkt i RGB565-format, jag gör det själv till mina små LCD-skärmar. Gratisalternativ som Paint.NET, Gimp osv kanske också göra det. Lite bökigare än ett litet script dock såklart.
sodjan
EF Sponsor
Inlägg: 43249
Blev medlem: 10 maj 2005, 16:29:20
Ort: Söderköping

Re: RGB888 till RGB565

Inlägg av sodjan »

> Jag ber om ursäkt ifall frågan är på fel forum,

Forumet är väl ganska OK, men det kanske bör ligga i "Mjukvara / Litteratur".

Sen är ju lösning på det aktuella problem väldigt beroende på om det är en
engång grej (ett antal bilder som ska konverteras nu) eller om det är
en lösning som ska ligga "i drift" på något sätt. Om det är en engångs
grej så är det nog enklast att fixa det så som sneaky föreslår.
vedderb
Inlägg: 70
Blev medlem: 16 oktober 2010, 11:07:09

Re: RGB888 till RGB565

Inlägg av vedderb »

Tänkte bland annat konvertera hela filmer som jag exporterar med mplayer, så något automatiserat vore att föredra, helst i java då jag gör alla andra beräkningar där.

Annars får jag försöka implementera floyd-steinberg eller liknande från grunden, vilket jag helst undviker.

Missade att den delen av forumet fanns, ska tänka på det nästa gång.
sodjan
EF Sponsor
Inlägg: 43249
Blev medlem: 10 maj 2005, 16:29:20
Ort: Söderköping

Re: RGB888 till RGB565

Inlägg av sodjan »

OK. Generellt är det viktigt att vara så tydlig som möjligt kring vad det
hela handlar om och hur det ska användas för att tips o.s.v ska "träffa rätt".
Nu så har det hela bl.a gått från "bilder" till "filmer". Om det är viktigt
vet inte jag, jag bara noterade det... :-)
vedderb
Inlägg: 70
Blev medlem: 16 oktober 2010, 11:07:09

Re: RGB888 till RGB565

Inlägg av vedderb »

Bland annat filmer... men också bilder. Poängen är att det är många konverteringar det handlar om, vilket jag kunde ha nämnt i första inlägget iofs. Jag exporterar filmer till en massa bilder först ändå, så det är ju fortfarande bilder det handlar om.
vedderb
Inlägg: 70
Blev medlem: 16 oktober 2010, 11:07:09

Re: RGB888 till RGB565

Inlägg av vedderb »

Jag skrev min egna java-funktion (metod) för floyd-steinberg-dithering (uppfann hjulet igen :)).

Här är resultaten:
Bild
En rgb888 bild (16777216 färger) nedkonverterad till rgb233 (256 Färger).

Resultaten blev bra för alla kombinationer jag testade av bitar per färg. Här är java-koden ifall någon får samma problem: (det går att "dithra" rgb888 till ett godtyckligt rgbxxx med den)

Kod: Markera allt

	EDIT: tog bort koden här då den gör fel. Se min post längre med en uppdaterad version om du behöver ha den.
Har inte testad med större bilder än, men på den lilla bilden gick det på bråkdelen av en sekund.

Hoppas det hjälper någon.
Senast redigerad av vedderb 8 januari 2011, 16:45:46, redigerad totalt 2 gånger.
vedderb
Inlägg: 70
Blev medlem: 16 oktober 2010, 11:07:09

Re: RGB888 till RGB565

Inlägg av vedderb »

Här är ett till exempel på en lite större bild nedkonverterad till rgb222 (64 färger). Även denna tog bara en bråkdel av en sekund att filtrera med algoritmen ovan. Helt otroligt van man kan åstadkomma med lite enklare bildbehandling.

Översta bilden är original. Nere till vänster bara trunkerad. Nere till höger trunkerad med hjälp av algoritmen ovan.
Bild

Anledningen att molnen är grå är att bilden visas som rgb24-format med de minst signifikanta bitarna till varje färg satta till 0. Skulle man visa den på en skärm med 64 färger så skulle det svara mot den ljusaste färgen.
vedderb
Inlägg: 70
Blev medlem: 16 oktober 2010, 11:07:09

Re: RGB888 till RGB565

Inlägg av vedderb »

Här är en ny version av koden som är lite mer lättläst, kortare och snabbare och dessutom gör rätt (mer rätt i alla fall, den ovan har en hel del fel):

Kod: Markera allt

	/**
	 * Use the Floyd-Steinberg algorithm to dither an image for color reduction.
	 * 
	 * @param img_old
	 * The original image
	 * @param bits_r
	 * The number of bits for red in the new image
	 * @param bits_g
	 * The number of bits for green in the new image
	 * @param bits_b
	 * The number of bits for blue in the new image
	 * @return
	 */
	public static BufferedImage ditherImage(BufferedImage img_old,
			int bits_r, int bits_g, int bits_b) {

		int[][][] img_buffer = new int[img_old.getWidth()][img_old.getHeight()][3];

		int 	a, red,
				green, 
				blue, 
				red_n, 
				green_n, 
				blue_n,
				quant_error_r,
				quant_error_g,
				quant_error_b;
		
		int mask_r = (0xFF << (8 - bits_r)) & 0xFF;
		int mask_g = (0xFF << (8 - bits_g)) & 0xFF;
		int mask_b = (0xFF << (8 - bits_b)) & 0xFF;
		
		/*
		 * Copy source image
		 */
		for (int y = 0;y < img_old.getHeight();y++) {
			for (int x = 0;x < img_old.getWidth();x++) {
				a = img_old.getRGB(x, y);
				
				red = (a & 0xFF0000) >>> 16;
				green = (a & 0x00FF00) >>> 8;
				blue = a & 0x0000FF;
				
				img_buffer[x][y][0] = red;
				img_buffer[x][y][1] = green;
				img_buffer[x][y][2] = blue;
			}
		}
		
		for (int y = 0;y < img_old.getHeight();y++) {
			for (int x = 0;x < img_old.getWidth();x++) {				
				red = img_buffer[x][y][0];
				green = img_buffer[x][y][1];
				blue = img_buffer[x][y][2];
				
				if (red > 255) {
					red = 255;
				}
				if (green > 255) {
					green = 255;
				}
				if (blue > 255) {
					blue = 255;
				}
				
				if (red < 0) {
					red = 0;
				}
				if (green < 0) {
					green = 0;
				}
				if (blue < 0) {
					blue = 0;
				}
				
				red_n = red & mask_r;
				green_n = green & mask_g;
				blue_n = blue & mask_b;
				
				quant_error_r = red - red_n;
				quant_error_g = green - green_n;
				quant_error_b = blue - blue_n;
				
				img_buffer[x][y][0] = red_n;
				img_buffer[x][y][1] = green_n;
				img_buffer[x][y][2] = blue_n;
				
				if (x < (img_old.getWidth() - 1)) {
					img_buffer[x + 1][y][0] += ((quant_error_r << 12) * 7) >> 16;
					img_buffer[x + 1][y][1] += ((quant_error_g << 12) * 7) >> 16;
					img_buffer[x + 1][y][2] += ((quant_error_b << 12) * 7) >> 16;
				}


				if (y < (img_old.getHeight() - 1)) {
					img_buffer[x][y + 1][0] += ((quant_error_r << 12) * 5) >> 16;
					img_buffer[x][y + 1][1] += ((quant_error_g << 12) * 5) >> 16;
					img_buffer[x][y + 1][2] += ((quant_error_b << 12) * 5) >> 16;
				}
				
				if (x > 0 && y < (img_old.getHeight() - 1)) {
					img_buffer[x - 1][y + 1][0] += ((quant_error_r << 12) * 3) >> 16;
					img_buffer[x - 1][y + 1][1] += ((quant_error_g << 12) * 3) >> 16;
					img_buffer[x - 1][y + 1][2] += ((quant_error_b << 12) * 3) >> 16;
				}
				
				if (x < (img_old.getWidth() - 1) && y < (img_old.getHeight() - 1)) {
					img_buffer[x + 1][y + 1][0] += ((quant_error_r << 12) * 1) >> 16;
					img_buffer[x + 1][y + 1][1] += ((quant_error_g << 12) * 1) >> 16;
					img_buffer[x + 1][y + 1][2] += ((quant_error_b << 12) * 1) >> 16;
				}
			}
		}
		
		BufferedImage img = new BufferedImage(img_old.getWidth(), img_old.getHeight(),
				BufferedImage.TYPE_INT_RGB);
		
		/*
		 * Now truncate image again
		 */
		for (int y = 0;y < img_old.getHeight();y++) {
			for (int x = 0;x < img_old.getWidth();x++) {				
				red = img_buffer[x][y][0];
				green = img_buffer[x][y][1];
				blue = img_buffer[x][y][2];
				
				
				red_n = red & mask_r;
				green_n = green & mask_g;
				blue_n = blue & mask_b;
				
				a = (red_n << 16) + (green_n << 8) + blue_n;
				img.setRGB(x, y, a);
				
			}
		}
		
		return img;
		
	}
Skriv svar