Bildkomprimering för mikrodator med 2D DCT

Berätta om dina pågående projekt.
vedderb
Inlägg: 70
Blev medlem: 16 oktober 2010, 11:07:09

Bildkomprimering för mikrodator med 2D DCT

Inlägg av vedderb »

När jag testade min 320*240*RGB LCD-display med en ATXMEGA32A4 så ville jag visa testa att visa en bild på den, men en okomprimerad bild får aldrig plats på flashminnet. Om man använder 8 bitar/färg så tar en sådan bild 320*240*3 = 230,4 kB! Det är ganska mycket i relation till processorns flashminne på 32 kB där även resten av programmet ska få plats. Det finns två möjligheter:

1. Hämta bilden från extern källa.
Detta är vad jag har gjort tidigare.

2. Komprimera bilden.
Bilden behöver komprimeras ca 10 ggr!

För att lära mig lite mer om praktisk implementation av bildkomprimering så skrev jag en egen komprimeringsalgoritm som bygger på en typ II/III 2D DCT (2-Dimensional Discrete Cosine Transform). För att den ska gå att köra på en långsam mikrodator så implementerade jag hela inverstransformen i fixed-point. Det som krävs av processorn utöver plats för den komprimerade bilden är ca 1kB RAM.

Komprimeringen går till så här:

1. Dela upp bilden i 8x8-pixel block.

2. Konvertera RGB-delblocken till YUV-delblock. Eftersom ögat är mer känsligt för själva formen än för färgerna så kan man lägga betydligt mer bandbredd på luminans-komponenten (Y) än på Chrominans-komponenterna (U, V).

3. Gör en 2D DCT på varje block på bilden. Sedan kan de höga frekvenserna kastas bort. Det är detta som sparar en massa data. Vi ser inte höra frekvenser i bilder lika bra som låga frekvenser och ofta är de höga frekvenserna endast brus.

För att återställa bilden görs samma sak baklänges.

De transformerade koefficienterna skalas sedan ner för att få plats i 8-bitars variabler. För att det ska gå snabbt så är alla cosinusdelar i transförmen förberäknade och sparade i tabeller.

Här är java-programmet jag har gjort för att testa metoden (jobbar fortfarande på optimeringar, detta är bara vad jag skrev ihop igår kväll):

Kod: Markera allt

	/*
	 * Fixed point cosine coefficients for inverse-transform.
	 */
	public static int cos_t[][] = {
		{ 127,  125,  117,  106,  90,  71,   49,   25  },
		{ 127,  106,  49,  -25,  -90, -125, -117, -71  },
		{ 127,  71,  -49,  -125, -90,  25,   117,  105 },
		{ 127,  25,  -117, -71,   90,  106, -49,  -125 },
		{ 127, -25,  -117,  71,   90, -106, -49,   125 },
		{ 127, -71,  -49,   125, -90, -25,   117, -106 },
		{ 127, -106,  49,   25,  -90,  125, -117,  71  },
		{ 127, -125,  117, -106,  90, -71,   49,  -25  },
	};
	
	/*
	 * Floating point cosine coefficients for transform
	 */
	public static double cos_t_d[][] = {
		{ 1,  0.980785280403230,  0.923879532511287,  0.831469612302545,  0.707106781186548,  0.555570233019602,  0.382683432365090,  0.195090322016128 },
		{ 1,  0.831469612302545,  0.382683432365090, -0.195090322016128, -0.707106781186547, -0.980785280403230, -0.923879532511287, -0.555570233019602 },
		{ 1,  0.555570233019602, -0.382683432365090, -0.980785280403230, -0.707106781186547,  0.195090322016128,  0.923879532511287,  0.831469612302546 },
		{ 1,  0.195090322016128, -0.923879532511287, -0.555570233019602,  0.707106781186548,  0.831469612302546, -0.382683432365090, -0.980785280403231 },
		{ 1, -0.195090322016128, -0.923879532511287,  0.555570233019602,  0.707106781186548, -0.831469612302545, -0.382683432365091,  0.980785280403230 },
		{ 1, -0.555570233019602, -0.382683432365090,  0.980785280403230, -0.707106781186547, -0.195090322016128,  0.923879532511287, -0.831469612302545 },
		{ 1, -0.831469612302545,  0.382683432365090,  0.195090322016128, -0.707106781186547,  0.980785280403231, -0.923879532511286,  0.555570233019602 },
		{ 1, -0.980785280403230,  0.923879532511287, -0.831469612302545,  0.707106781186548, -0.555570233019602,  0.382683432365090, -0.195090322016129 },
	};
	
	/*
	 * Fixed point scaling table for inverse transform
	 */
	public static int sc_t[] = { 64516, 91239, 129032 };
//	public static int sc_t[] = { 65536, 65536, 131072 };
	
	/*
	 * Floating point scaling table for transform
	 */
	public static double sc_t_d[] = { 4, 5.656854249492381, 8 };
	
	/*
	 * Scaling of transform coefficients. 16 makes them fit into 8 bits.
	 */
	public static int div = 16;
	
	/**
	 * Get a 2D DCT (Discrete cosine transform) from an 8x8 block
	 * in the spatial domain. This method uses floating-point
	 * operations.
	 * @param block
	 * The 8x8 block to transform.
	 * @param len
	 * The number of coefficients to store in each dimension.
	 * @return
	 * A vector with the block in the frequency domain. The length
	 * will be len*len.
	 */
	public static void getDCTBlock(int block[], int len, int out[]) {
		double aa[] = new double[len * len];

		for (int i = 0; i < aa.length; i++) {
			aa[i] = 0;
		}

		for (int i = 0; i < len; i++) {
			for (int j = 0; j < len; j++) {
				for (int k = 0; k < 8; k++) {
					for (int l = 0; l < 8; l++) {
						aa[i * len + j] += (double)block[k * 8 + l] * cos_t_d[k][i] * cos_t_d[l][j];
					}
				}
				int sc_ind = 0;
				if (i == 0) {
					sc_ind++;
				}
				if (j == 0) {
					sc_ind++;
				}
				aa[i * len + j] /= (sc_t_d[sc_ind] * (double)div);
			}
		}
		
		for (int i = 0; i < aa.length; i++) {
			out[i] = (int)aa[i];
		}
	}
	
	/**
	 * Get a IDCT (Inverse Discrete Cosine Transform) 8x8 block from
	 * a block in the frequency domain. This method uses only
	 * fixed point operations.
	 * @param block_in
	 * The block in the frequency domain. Length should be a power of 2.
	 * @return
	 * A 8x8 block in the spatial domain.
	 */
	public static void getIDCTBlock(int block_in[], int block_out[]) {
		int len = (int) Math.sqrt(block_in.length);

		for (int i = 0; i < block_out.length; i++) {
			block_out[i] = 0;
		}

		for (int i = 0; i < 8; i++) {
			for (int j = 0; j < 8; j++) {
				for (int k = 0; k < len; k++) {
					for (int l = 0; l < len; l++) {
						int sc_ind = 0;
						if (k == 0) {
							sc_ind++;
						}
						if (l == 0) {
							sc_ind++;
						}
						block_out[i * 8 + j] += (block_in[k * len + l] * cos_t[i][k] * cos_t[j][l] * div) / sc_t[sc_ind];
					}
				}
			}
		}
	}
	
	/**
	 * Compress an image with the DCT (Discrete Cosine Transform).
	 * @param img
	 * The image to compress.
	 * @param y
	 * The number of Y (luma) coefficients to store in each dimension.
	 * @param u
	 * The number of U (Chrominance) coefficients to store in each dimension.
	 * Can usually be a lower number than the luma coefficients.
	 * @param v
	 * The number of V (Chrominance) coefficients to store in each dimension.
	 * Can usually be a lower number than the luma coefficients.
	 * @return
	 * A vector with the compressed image.
	 */
	public static int[] getDCTImage(BufferedImage img, int y, int u, int v) {
		int w = img.getWidth();
		int h = img.getHeight();
		
		int[] aa = new int[y * y * ((w / 8) * (h / 8)) + u * u
				* ((w / 8) * (h / 8)) + v * v * ((w / 8) * (h / 8))];
		int block_y[] = new int[8 * 8];
		int block_u[] = new int[8 * 8];
		int block_v[] = new int[8 * 8];
		int y_c[] = new int[y*y];
		int u_c[] = new int[u*u];
		int v_c[] = new int[v*v];
		int rgb, r, g, b;
		int pos = 0;

		for (int i = 0; i < w; i += 8) {
			for (int j = 0; j < h; j += 8) {
				for (int k = 0; k < 8; k++) {
					for (int l = 0; l < 8; l++) {
						rgb = img.getRGB(i + k, j + l);
						r = (rgb & 0xFF0000) >>> 16;
						g = (rgb & 0x00FF00) >>> 8;
						b = rgb & 0x0000FF;

						block_y[k * 8 + l] = (299 * r + 587 * g + 114 * b) / 1000;
						block_u[k * 8 + l] = (-147 * r - 289 * g + 436 * b) / 1000;
						block_v[k * 8 + l] = (615 * r - 515 * g - 100 * b) / 1000;
						
//						block_y[k * 8 + l] = r;
//						block_u[k * 8 + l] = g;
//						block_v[k * 8 + l] = b;
					}
				}
				getDCTBlock(block_y, y, y_c);
				getDCTBlock(block_u, u, u_c);
				getDCTBlock(block_v, v, v_c);

				for (int k = 0; k < y_c.length; k++) {
					aa[pos + k] = y_c[k];
				}
				pos += y_c.length;
				for (int k = 0; k < u_c.length; k++) {
					aa[pos + k] = u_c[k];
				}
				pos += u_c.length;
				for (int k = 0; k < v_c.length; k++) {
					aa[pos + k] = v_c[k];
				}
				pos += v_c.length;
			}
		}

		return aa;
	}
	
	/**
	 * De-compress an image compressed with the DCT (Discrete Cosine Transform).
	 * @param dct
	 * The compressed image.
	 * @param w
	 * The width of the image.
	 * @param h
	 * The height of the image.
	 * @param y
	 * The number of Y (luma) coefficients stored in each dimension.
	 * @param u
	 * The number of U (Chrominance) coefficients stored in each dimension.
	 * @param v
	 * The number of V (Chrominance) coefficients stored in each dimension.
	 * @return
	 */
	public static BufferedImage getIDCTImage(int dct[], int w, int h, int y,
			int u, int v) {
		BufferedImage img = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
		int y_c[] = new int[y * y];
		int u_c[] = new int[u * u];
		int v_c[] = new int[v * v];
		int block_y[] = new int[8*8];
		int block_u[] = new int[8*8];
		int block_v[] = new int[8*8];
		int r, g, b;
		int pos = 0;

		for (int i = 0; i < w; i += 8) {
			for (int j = 0; j < h; j += 8) {
				for (int k = 0; k < y_c.length; k++) {
					y_c[k] = dct[pos + k];
				}
				pos += y_c.length;
				for (int k = 0; k < u_c.length; k++) {
					u_c[k] = dct[pos + k];
				}
				pos += u_c.length;
				for (int k = 0; k < v_c.length; k++) {
					v_c[k] = dct[pos + k];
				}
				pos += v_c.length;

				getIDCTBlock(y_c, block_y);
				getIDCTBlock(u_c, block_u);
				getIDCTBlock(v_c, block_v);

				for (int k = 0; k < 8; k++) {
					for (int l = 0; l < 8; l++) {
						r = (1000*block_y[8*k+l] + 1140 * block_v[8*k+l]) / 1000;
						g = (1000*block_y[8*k+l] - 395*block_u[8*k+l] - 581*block_v[8*k+l]) / 1000;
						b = (1000*block_y[8*k+l] + 2032 * block_u[8*k+l]) / 1000;

//						r = block_y[8*k+l];
//						g = block_u[8*k+l];
//						b = block_v[8*k+l];

						if (r < 0) {
							r = 0;
						}
						if (r > 255) {
							r = 255;
						}
						if (g < 0) {
							g = 0;
						}
						if (g > 255) {
							g = 255;
						}
						if (b < 0) {
							b = 0;
						}
						if (b > 255) {
							b = 255;
						}

						img.setRGB(i + k, j + l, (r << 16) + (g << 8) + b);
					}
				}
			}
		}

		return img;
	}
Här är resultaten från en testkörning (original till vänster, återskapad efter komprimering till höger):
Bild

Bilden ovan (Lena, http://www.ecogito.net/articles/lena.html) är komprimerad så att endast 9% informationen av originalbilden används. Alltså över 10 gånger komprimering! Man kan se block-artefakten och zoomar man in ser man att färgera har mycket lägre upplösning än luminansen.

Nästa steg blir att implementera inverstransformen på en mikrodator. Inga konstiga funktioner används i återskapningen och allt är fixed-point, så det borde inte vara något problem.
blueint
Inlägg: 23238
Blev medlem: 4 juli 2006, 19:26:11
Kontakt:

Re: Bildkomprimering för mikrodator med 2D DCT

Inlägg av blueint »

Intressant! :tumupp:

Licens på kodsnutten?
vedderb
Inlägg: 70
Blev medlem: 16 oktober 2010, 11:07:09

Re: Bildkomprimering för mikrodator med 2D DCT

Inlägg av vedderb »

Blir nog gpl3 eller så när den är klar.

Om någon är intresserad så kan jag lägga upp ett litet javaprogram som använder metoderna med vettiga parametrar. Som det är nu så saknas det en del kommentarer för att ha användning för det.

Jag kommer även lägga upp c-koden som kan köras på valfri mikroprocessor när jag har skrivit den. Kommer testa på en ATXMEGA32A4 först. Propeller och pic är andra kandidater.
bearing
Inlägg: 11674
Blev medlem: 2 mars 2006, 01:01:45
Ort: Ängelholm

Re: Bildkomprimering för mikrodator med 2D DCT

Inlägg av bearing »

Intressant! jag försökte en gång med DCT, men förstod aldrig riktigt hur det gick till. Jag fick till en Wavelet dock, som verkade ge ungefär samma komprimeringsgrad som JPEG vid ungefär samma visuella förlust.

Är det någon kompromiss i din 8-bit process som gör att Lena blir så blockig?
Jag testade att ladda hem Lena och komprimerade henne med JPEG 50% kvalitet, vilket gav 8,2kB stor bild. Den bilden är grynig, men inte lika blockig. Har för mig att standard JPEG använder nedsamplade färgkomponenter med faktorn 2:1 i både x och y, vilket gör att färgblocken blir 16x16 när de återskapats. Kan det vara anledningen till att JPEGen inte ser lika blockig ut?
Du har inte behörighet att öppna de filer som bifogats till detta inlägg.
vedderb
Inlägg: 70
Blev medlem: 16 oktober 2010, 11:07:09

Re: Bildkomprimering för mikrodator med 2D DCT

Inlägg av vedderb »

JPEG2000 använder wavelets, vilket är bättre än DCT för bilder, så om du har någon kod som fungerar på en mikrodator så är jag intresserad :)

Anledningen att det blev blockigt beror dels på avrundningar då inverstransformen görs med heltal och dels på att jag skalar ner koefficienterna ganska mycket (16 gånger). Innan jpeg2000 så användes liknande DCT där också, men med variabelt antal bitar per block beroende på hur mycket höga frekvenser de olika blocken innehåller och sedan huffman på det. Det är lite avancerat att köra på en mikrodator med begränsat minne, men resultatet blir bättre.

En annan sak som bidrar till att det ser blockigt ut i bilden är att färgerna endast har dc-komponenten i blocken sparad i bilden ovan. Det är alltså en svartvit bild med 8x8 enfärgade block ovanpå. Det går givetvist att ändra på med parametrarna så kompilera gärna koden och experimentera lite!

Jag funderar på att lägga huffman-kodning ovanpå DCTn som ungefär halverar storleken ytterligare utan att tappa kvalitet men då kommer det gå extremt långsamt att köra på en mikrodator.
vedderb
Inlägg: 70
Blev medlem: 16 oktober 2010, 11:07:09

Re: Bildkomprimering för mikrodator med 2D DCT

Inlägg av vedderb »

Här är ett komplett javaprogram om någon vill testa med lite olika parametrar etc. Inga speciella bibliotek krävs, så det är bara att kompilera och köra. Dela gärna med er om ni förbättrar något.

Kod: Markera allt

package test;

import java.awt.BorderLayout;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;

import javax.imageio.ImageIO;
import javax.swing.*;
import javax.swing.UIManager.LookAndFeelInfo;

public class MainWindow {
	
	/*
	 * Fixed point cosine coefficients for inverse-transform.
	 */
	public static int cos_t[][] = {
		{ 127,  125,  117,  106,  90,  71,   49,   25  },
		{ 127,  106,  49,  -25,  -90, -125, -117, -71  },
		{ 127,  71,  -49,  -125, -90,  25,   117,  105 },
		{ 127,  25,  -117, -71,   90,  106, -49,  -125 },
		{ 127, -25,  -117,  71,   90, -106, -49,   125 },
		{ 127, -71,  -49,   125, -90, -25,   117, -106 },
		{ 127, -106,  49,   25,  -90,  125, -117,  71  },
		{ 127, -125,  117, -106,  90, -71,   49,  -25  },
	};
	
	/*
	 * Floating point cosine coefficients for transform
	 */
	public static double cos_t_d[][] = {
		{ 1,  0.980785280403230,  0.923879532511287,  0.831469612302545,  0.707106781186548,  0.555570233019602,  0.382683432365090,  0.195090322016128 },
		{ 1,  0.831469612302545,  0.382683432365090, -0.195090322016128, -0.707106781186547, -0.980785280403230, -0.923879532511287, -0.555570233019602 },
		{ 1,  0.555570233019602, -0.382683432365090, -0.980785280403230, -0.707106781186547,  0.195090322016128,  0.923879532511287,  0.831469612302546 },
		{ 1,  0.195090322016128, -0.923879532511287, -0.555570233019602,  0.707106781186548,  0.831469612302546, -0.382683432365090, -0.980785280403231 },
		{ 1, -0.195090322016128, -0.923879532511287,  0.555570233019602,  0.707106781186548, -0.831469612302545, -0.382683432365091,  0.980785280403230 },
		{ 1, -0.555570233019602, -0.382683432365090,  0.980785280403230, -0.707106781186547, -0.195090322016128,  0.923879532511287, -0.831469612302545 },
		{ 1, -0.831469612302545,  0.382683432365090,  0.195090322016128, -0.707106781186547,  0.980785280403231, -0.923879532511286,  0.555570233019602 },
		{ 1, -0.980785280403230,  0.923879532511287, -0.831469612302545,  0.707106781186548, -0.555570233019602,  0.382683432365090, -0.195090322016129 },
	};
	
	/*
	 * Fixed point scaling table for inverse transform
	 */
	public static int sc_t[] = { 64516, 91239, 129032 };
//	public static int sc_t[] = { 65536, 65536, 131072 };
	
	/*
	 * Floating point scaling table for transform
	 */
	public static double sc_t_d[] = { 4, 5.656854249492381, 8 };
	
	/*
	 * Scaling of transform coefficients. 16 makes them fit into 8 bits.
	 */
	public static int div = 16;
	
	/**
	 * Get a 2D DCT (Discrete cosine transform) from an 8x8 block
	 * in the spatial domain. This method uses floating-point
	 * operations.
	 * @param block
	 * The 8x8 block to transform.
	 * @param len
	 * The number of coefficients to store in each dimension.
	 * @return
	 * A vector with the block in the frequency domain. The length
	 * will be len*len.
	 */
	public static void getDCTBlock(int block[], int len, int out[]) {
		double aa[] = new double[len * len];

		for (int i = 0; i < aa.length; i++) {
			aa[i] = 0;
		}

		for (int i = 0; i < len; i++) {
			for (int j = 0; j < len; j++) {
				for (int k = 0; k < 8; k++) {
					for (int l = 0; l < 8; l++) {
						aa[i * len + j] += (double)block[k * 8 + l] * cos_t_d[k][i] * cos_t_d[l][j];
					}
				}
				int sc_ind = 0;
				if (i == 0) {
					sc_ind++;
				}
				if (j == 0) {
					sc_ind++;
				}
				aa[i * len + j] /= (sc_t_d[sc_ind] * (double)div);
			}
		}
		
		for (int i = 0; i < aa.length; i++) {
			out[i] = (int)aa[i];
		}
	}
	
	/**
	 * Get a IDCT (Inverse Discrete Cosine Transform) 8x8 block from
	 * a block in the frequency domain. This method uses only
	 * fixed point operations.
	 * @param block_in
	 * The block in the frequency domain. Length should be a power of 2.
	 * @return
	 * A 8x8 block in the spatial domain.
	 */
	public static void getIDCTBlock(int block_in[], int block_out[]) {
		int len = (int) Math.sqrt(block_in.length);

		for (int i = 0; i < block_out.length; i++) {
			block_out[i] = 0;
		}

		for (int i = 0; i < 8; i++) {
			for (int j = 0; j < 8; j++) {
				for (int k = 0; k < len; k++) {
					for (int l = 0; l < len; l++) {
						int sc_ind = 0;
						if (k == 0) {
							sc_ind++;
						}
						if (l == 0) {
							sc_ind++;
						}
						block_out[i * 8 + j] += (block_in[k * len + l] * cos_t[i][k] * cos_t[j][l] * div) / sc_t[sc_ind];
					}
				}
			}
		}
	}
	
	/**
	 * Compress an image with the DCT (Discrete Cosine Transform).
	 * @param img
	 * The image to compress.
	 * @param y
	 * The number of Y (luma) coefficients to store in each dimension.
	 * @param u
	 * The number of U (Chrominance) coefficients to store in each dimension.
	 * Can usually be a lower number than the luma coefficients.
	 * @param v
	 * The number of V (Chrominance) coefficients to store in each dimension.
	 * Can usually be a lower number than the luma coefficients.
	 * @return
	 * A vector with the compressed image.
	 */
	public static int[] getDCTImage(BufferedImage img, int y, int u, int v) {
		int w = img.getWidth();
		int h = img.getHeight();
		
		int[] aa = new int[y * y * ((w / 8) * (h / 8)) + u * u
				* ((w / 8) * (h / 8)) + v * v * ((w / 8) * (h / 8))];
		int block_y[] = new int[8 * 8];
		int block_u[] = new int[8 * 8];
		int block_v[] = new int[8 * 8];
		int y_c[] = new int[y*y];
		int u_c[] = new int[u*u];
		int v_c[] = new int[v*v];
		int rgb, r, g, b;
		int pos = 0;

		for (int i = 0; i < w; i += 8) {
			for (int j = 0; j < h; j += 8) {
				for (int k = 0; k < 8; k++) {
					for (int l = 0; l < 8; l++) {
						rgb = img.getRGB(i + k, j + l);
						r = (rgb & 0xFF0000) >>> 16;
						g = (rgb & 0x00FF00) >>> 8;
						b = rgb & 0x0000FF;

						block_y[k * 8 + l] = (299 * r + 587 * g + 114 * b) / 1000;
						block_u[k * 8 + l] = (-147 * r - 289 * g + 436 * b) / 1000;
						block_v[k * 8 + l] = (615 * r - 515 * g - 100 * b) / 1000;
						
//						block_y[k * 8 + l] = r;
//						block_u[k * 8 + l] = g;
//						block_v[k * 8 + l] = b;
					}
				}
				getDCTBlock(block_y, y, y_c);
				getDCTBlock(block_u, u, u_c);
				getDCTBlock(block_v, v, v_c);

				for (int k = 0; k < y_c.length; k++) {
					aa[pos + k] = y_c[k];
				}
				pos += y_c.length;
				for (int k = 0; k < u_c.length; k++) {
					aa[pos + k] = u_c[k];
				}
				pos += u_c.length;
				for (int k = 0; k < v_c.length; k++) {
					aa[pos + k] = v_c[k];
				}
				pos += v_c.length;
			}
		}

		return aa;
	}
	
	/**
	 * De-compress an image compressed with the DCT (Discrete Cosine Transform).
	 * @param dct
	 * The compressed image.
	 * @param w
	 * The width of the image.
	 * @param h
	 * The height of the image.
	 * @param y
	 * The number of Y (luma) coefficients stored in each dimension.
	 * @param u
	 * The number of U (Chrominance) coefficients stored in each dimension.
	 * @param v
	 * The number of V (Chrominance) coefficients stored in each dimension.
	 * @return
	 */
	public static BufferedImage getIDCTImage(int dct[], int w, int h, int y,
			int u, int v) {
		BufferedImage img = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
		int y_c[] = new int[y * y];
		int u_c[] = new int[u * u];
		int v_c[] = new int[v * v];
		int block_y[] = new int[8*8];
		int block_u[] = new int[8*8];
		int block_v[] = new int[8*8];
		int r, g, b;
		int pos = 0;

		for (int i = 0; i < w; i += 8) {
			for (int j = 0; j < h; j += 8) {
				for (int k = 0; k < y_c.length; k++) {
					y_c[k] = dct[pos + k];
				}
				pos += y_c.length;
				for (int k = 0; k < u_c.length; k++) {
					u_c[k] = dct[pos + k];
				}
				pos += u_c.length;
				for (int k = 0; k < v_c.length; k++) {
					v_c[k] = dct[pos + k];
				}
				pos += v_c.length;

				getIDCTBlock(y_c, block_y);
				getIDCTBlock(u_c, block_u);
				getIDCTBlock(v_c, block_v);

				for (int k = 0; k < 8; k++) {
					for (int l = 0; l < 8; l++) {
						r = (1000*block_y[8*k+l] + 1140 * block_v[8*k+l]) / 1000;
						g = (1000*block_y[8*k+l] - 395*block_u[8*k+l] - 581*block_v[8*k+l]) / 1000;
						b = (1000*block_y[8*k+l] + 2032 * block_u[8*k+l]) / 1000;

//						r = block_y[8*k+l];
//						g = block_u[8*k+l];
//						b = block_v[8*k+l];

						if (r < 0) {
							r = 0;
						}
						if (r > 255) {
							r = 255;
						}
						if (g < 0) {
							g = 0;
						}
						if (g > 255) {
							g = 255;
						}
						if (b < 0) {
							b = 0;
						}
						if (b > 255) {
							b = 255;
						}

						img.setRGB(i + k, j + l, (r << 16) + (g << 8) + b);
					}
				}
			}
		}

		return img;
	}
	
	public static void main(String[] args) {

		try {
			for (LookAndFeelInfo info : UIManager.getInstalledLookAndFeels()) {
				if ("GTK+".equals(info.getName())) {
					UIManager.setLookAndFeel(info.getClassName());
					break;
				}
			}
		} catch (UnsupportedLookAndFeelException e) {
			// handle exception
		} catch (ClassNotFoundException e) {
			// handle exception
		} catch (InstantiationException e) {
			// handle exception
		} catch (IllegalAccessException e) {
			// handle exception
		}
		
		int dct[] = null;
		BufferedImage img_orig = null;
		String path = "/home/benjamin/Dropbox/DCT Test/lena.png";
		
		try {
			img_orig = ImageIO.read(new File(path));
		} catch (IOException e) {
		}
		
		int w = img_orig.getWidth(),
		h = img_orig.getHeight(),
		y = 4,
		u = 1,
		v = 1;

		System.out.println("DCT...");
		long t1 = System.currentTimeMillis();
		dct = getDCTImage(img_orig, y, u, v);
		long t2 = System.currentTimeMillis();
		System.out.println("" + (t2 - t1));
		
		System.out.println("IDCT...");
		t1 = System.currentTimeMillis();
		BufferedImage img = getIDCTImage(dct, w, h, y, u, v);
		t2 = System.currentTimeMillis();
		System.out.println("" + (t2 - t1));
		
		System.out.println("Done.");
		
		System.out.println("Orig size: " + w*h*3);
		System.out.println("After DCT: " + dct.length);
		System.out.println("Compression: " + (double)dct.length / (double)(w*h*3));
		
		JFrame d = new JFrame();
		JLabel l = new JLabel(new ImageIcon(img_orig));
		JLabel l2 = new JLabel(new ImageIcon(img));
		l.setBorder(BorderFactory.createTitledBorder(
				BorderFactory.createEtchedBorder(), "Before compression"));
		l2.setBorder(BorderFactory.createTitledBorder(
				BorderFactory.createEtchedBorder(), "Reconstruction after compression"));
		d.setTitle("DCT Compression");
		d.add(l, BorderLayout.WEST);
		d.add(l2, BorderLayout.EAST);
		d.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		d.pack();
		d.setLocationRelativeTo(null);
		d.setVisible(true);

	}

}
arte
Inlägg: 317
Blev medlem: 13 januari 2006, 01:18:50

Re: Bildkomprimering för mikrodator med 2D DCT

Inlägg av arte »

Roligt projekt!

Hur mycket kvantiserar du?
Har du 16 koefficienter luma och 1 koefficient U och V för ett 8x8 block?
Så 4ggr komprimering i luma och 16ggr i chroma?

Tips:
Prediktera DC och LP coefficienter från närliggande block, då borde du få många noll coeff. som du sedan kan använda RLE för att komprimera bort.

Ifall lookup tabellen tar för mycket plats så finns det approximeringar av DCT som bara kräver addition och shift. PCT (JPEG XR) heter en sådan
vedderb
Inlägg: 70
Blev medlem: 16 oktober 2010, 11:07:09

Re: Bildkomprimering för mikrodator med 2D DCT

Inlägg av vedderb »

Hur mycket kvantiserar du?
Har du 16 koefficienter luma och 1 koefficient U och V för ett 8x8 block?
Så 4ggr komprimering i luma och 16ggr i chroma?
Precis!
Just nu kvantiserar jag alla koefficienter till 8 bitar då det ska gå snabbt att köra på en mikrodator. Finns garanterat bättre lösningar :)

Prediktion låter som en bra ide. Jag tror jag kommer behålla 8 bitar/koefficient men ha en enkel linjär modell för dc-komponenten baserad på de ca 10 senaste blocken och spara/skicka skillnaden. En annan sak jag funderar på är att ha sista biten i varje tal som en ggr16-bit eller liknande då dc-komponenten brukar dominera så stort (ex: 92, 0, 0, 1, 2, 0, 3, 4 ...). Då används bitarna på ett mer meningsfullt vis när koefficienterna är små.

Störta problemet med det här på en mindre mikrodator är ram-minnet. Det finns inte på närheten tillräckligt med ram för att spara en hel bild, så varje block kommer skrivas direkt till displayen efter IDCTn.

Cos-tabellen är inget problem faktiskt, den tar bara 64 byte minne just nu. Det är ingen komplett cos-tabell utan bara de värden som används i transformen är förberäknade.

Jag hoppas att jag har tid att testa det här på riktig hårdvara imorgon. Skickar eventuellt lite fler bilder.
blueint
Inlägg: 23238
Blev medlem: 4 juli 2006, 19:26:11
Kontakt:

Re: Bildkomprimering för mikrodator med 2D DCT

Inlägg av blueint »

Går 3-klausul BSD licens bra möjligtvis? :humm:
vedderb
Inlägg: 70
Blev medlem: 16 oktober 2010, 11:07:09

Re: Bildkomprimering för mikrodator med 2D DCT

Inlägg av vedderb »

Det går väl bra.
Behöver du använda koden? Roligt att höra att någon får användning för den :)
blueint
Inlägg: 23238
Blev medlem: 4 juli 2006, 19:26:11
Kontakt:

Re: Bildkomprimering för mikrodator med 2D DCT

Inlägg av blueint »

Inget konkret projekt. Men jag har haft funderingar på att koppla en CMOS sensor till en MCU och skicka via 1-wire/DCC/LIN/DMX liknande överföring. Upplösning på 320x200 6-bpp eller liknande med 0,5 fps. Så kan man övervaka enklare saker.
Ska man skicka okomprimerat tar det enormt utrymme av överföringskapaciteten.
Nerre
Inlägg: 27195
Blev medlem: 19 maj 2008, 07:51:04
Ort: Upplands väsby

Re: Bildkomprimering för mikrodator med 2D DCT

Inlägg av Nerre »

Är det bara jag som tycker det är lite konstigt med termen "mikrodator"?

Även en PC är väl en "mikrodator"?

Jag antar att det som avses är en mikrocontroller?
blueint
Inlägg: 23238
Blev medlem: 4 juli 2006, 19:26:11
Kontakt:

Re: Bildkomprimering för mikrodator med 2D DCT

Inlägg av blueint »

Provade att översätta programkoden till C. Den producerar gibberish av rätt storlek. Så om man får till koden, går den att kläma in på en ATmega?
vedderb
Inlägg: 70
Blev medlem: 16 oktober 2010, 11:07:09

Re: Bildkomprimering för mikrodator med 2D DCT

Inlägg av vedderb »

Det är det som är tanken, i alla fall med inverstransformen. Om du även vill komprimera bilder på en atmega så går det om man modifierar det lite.

Jag tänkte köra koden på en xmega snart så jag kan lägga upp det också når jag är klar.
blueint
Inlägg: 23238
Blev medlem: 4 juli 2006, 19:26:11
Kontakt:

Re: Bildkomprimering för mikrodator med 2D DCT

Inlägg av blueint »

Fixar ATmega kompilatorerna C++ ..?
Skriv svar