Showing posts with label image processing. Show all posts
Showing posts with label image processing. Show all posts

Saturday, November 17, 2007

Load bitmap images onto Xilinx Spartan-3 FPGA board

As I mentioned before in this blog about my Hardware Synthesis term project, I designed the copy of Pong game using Verilog and tested it on Xilinx Spartan-3 FPGA Starter Kit. The highlight feature of my project is the ability to display bitmap images as game's components. The following instructions are how to do that.

Note: I will assume that the readers know how to design ASIC using Verilog language with Xilinx Webpack and have some background in computer programming.

First, since I was going to use 640x480 8-color mode, so I prepared 8-color bitmap images of in-game components. These are few examples.



To display these images, we need to store their pixel data somewhere in our circuit. There are few options.

  1. Hard-code the static wire or reg variables, which store pixel data, in the Verilog code. This is going to be easy. But you will suffer from long synthesis, post-place and route time. The size of .v files are going to be unacceptable big too.
  2. Utilize the Block RAMs available as external component on Xilinx Spartan-3 FPGA board. Just set the initial values of these Block RAMs to the pixel data of our images.
In this entry, I am going to use the latter method. For more information about how to use Block RAMs in your design, you can consult this documentation from Xilinx.

No matter which way you chose, we need to convert .jpg, .gif , .png, .bmp, etc. images data into easy-to-understand pixel data. This can be done via a little programming. The following Java program prompts user for an image and convert it to Coefficients File (.coe) for use as initial value of Block RAMs. The format of Coefficient File can be found in the Xilinx documentation I mentioned recently.
The program generates two files, name.coe for color images and name.bw.coe for black and white images. BW images consume less space.

You can now add new ROM module to your design using Xilinx Core Generator from with Xilinx Project Navigator. The detailed instructions on how to do this can also be found on the document I mentioned before. But I am kind enough to provide you this less-detailed screen shots :)



Create new source and select IP (Coregen & Architecture Wizard)



For this kind of use, a read-only Single Port Block Memeory would be enough.



You can specify some memory attributes here.



This is the most important part. You have to tick the "Load Init File" checkbox and click "Load File ..." button then choose the generated .coe file or .bw.coe file.



You can click on the "Show Coefficients" button to make sure that your file properly loaded.

Finally, click on the "Generate" button to generate your ROM module. Your ROM is now ready to use :)

I hope this helps.

Wednesday, May 2, 2007

Really simple blob detector

Few days ago while i was reading news at Blognone, I spotted an interesting topic on how to detect
(and calculate the area of ..) circles in an image. This is a well-known problem in the field of Computer Vision known as Blob Detection. I had some experiences in implementing the blob detector in C and C# but had never done it in Java. So I decided to write one. The code was more compact and straight to the point than my C and C# version.

This is the input image.



And here is my code.




package blobdetector;

import java.awt.Point;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.LinkedList;
import java.util.Queue;
import javax.imageio.ImageIO;
import javax.management.Query;

/**
*
* @author m3rlinezatgmaildotcom
*/
public class Main {

public Main() {
}

public static boolean isBlack(BufferedImage image,int posX,int posY){
// หาสีที่สุดที่สนใจ
int color = image.getRGB(posX,posY);

// หาค่าความสว่างจากการเฉลี่ย RGB
int brightness =
(color & 0xFF) +
((color >> 2) & 0xFF) +
((color >> 4) & 0xFF);
brightness /= 3;
return brightness < 128;
}

public static void main(String[] args) {
if(args.length != 1){
System.err.println("ERROR: Pass filename as argument.");
return;
}

String filename = args[0];
// String filename = "C:\\Users\\Natthawut\\Desktop\\Polymorphism\\blob.jpg";
try {
BufferedImage bimg = ImageIO.read(new File(filename));

// map สำหรับเก็บว่าจุดใดบ้างที่ได้รับการสำรวจไปแล้ว
boolean[][] painted =
new boolean[bimg.getHeight()][bimg.getWidth()];

// วนรอบทุกจุดในรูป
for(int i = 0 ; i < bimg.getHeight() ; i++){
for(int j = 0 ; j < bimg.getWidth() ; j++) {
// System.out.println(i + " " + j + " b " + isBlack(bimg,j,i));
// ถ้าจุดนั้นเป็นสีดำ และยังไม่เคยถูกสำรวจ
if(isBlack(bimg,j,i) && !painted[i][j]){

// ทำการ floodfill
Queue<Point> queue = new LinkedList<Point>();
queue.add(new Point(j,i));

int pixelCount = 0;
while(!queue.isEmpty()){
Point p = queue.remove();

// เช็คว่าจุดที่ดึงมาอยู่ในขอบเขต
if((p.x >= 0) && (p.x < bimg.getWidth() && (p.y >= 0) && (p.y < bimg.getHeight()))){
if(!painted[p.y][p.x] && isBlack(bimg,p.x,p.y)){
painted[p.y][p.x] = true;
pixelCount++;

// ใส่จุดรอบๆจุดที่ดึงออกมาลงไปในคิว
queue.add(new Point(p.x + 1,p.y)); queue.add(new Point(p.x - 1,p.y));
queue.add(new Point(p.x,p.y + 1)); queue.add(new Point(p.x,p.y - 1));
}
}
}
System.out.println("Blob detected : " + pixelCount + " pixels");
}

}
}

} catch (IOException ex) {
ex.printStackTrace();
}

}

}




And here is the output.

Blob detected : 1 pixels
Blob detected : 1339 pixels
Blob detected : 1 pixels
Blob detected : 5582 pixels
Blob detected : 1 pixels
Blob detected : 1 pixels
Blob detected : 1 pixels
Blob detected : 1 pixels
Blob detected : 1 pixels
Blob detected : 1 pixels
Blob detected : 1 pixels
Blob detected : 1 pixels
Blob detected : 1 pixels
Blob detected : 1 pixels
Blob detected : 1 pixels
Blob detected : 4018 pixels
Blob detected : 1 pixels
Blob detected : 1 pixels
Blob detected : 1 pixels
Blob detected : 1 pixels
Blob detected : 1 pixels
Blob detected : 1 pixels
Blob detected : 1 pixels
Blob detected : 1 pixels
Blob detected : 1 pixels
Blob detected : 1 pixels
Blob detected : 1 pixels
Blob detected : 1 pixels
Blob detected : 1 pixels
Blob detected : 1 pixels
Blob detected : 1 pixels


Edit: P'Deans4J suggested me to write the same program using recursion too. Here is my code in recursive version. I added another static method "floodfill" which returns number of pixels in current blob.




package blobdetector;

import java.awt.Point;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.LinkedList;
import java.util.Queue;
import javax.imageio.ImageIO;
import javax.management.Query;

/**
*
* @author m3rlinezatgmaildotcom
*/
public class Main {

public Main() {
}

public static boolean isBlack(BufferedImage image,int posX,int posY){
// หาสีที่สุดที่สนใจ
int color = image.getRGB(posX,posY);

// หาค่าความสว่างจากการเฉลี่ย RGB
int brightness =
(color & 0xFF) +
((color >> 2) & 0xFF) +
((color >> 4) & 0xFF);
brightness /= 3;
return brightness < 128;
}

public static int floodfill(
BufferedImage image,
boolean[][] painted,
int posX, int posY){

// ตรวจสอบขอบเขต
if((posX < 0) || (posX >= image.getWidth()) || (posY < 0) || (posY >= image.getHeight()))
return 0;

if(!painted[posY][posX] && isBlack(image,posX,posY)){
painted[posY][posX] = true;
return 1 + floodfill(image,painted,posX+1,posY) +
floodfill(image,painted,posX-1,posY) +
floodfill(image,painted,posX,posY+1) +
floodfill(image,painted,posX,posY-1);
}

return 0;
}

public static void main(String[] args) {
if(args.length != 1){
System.err.println("ERROR: Pass filename as argument.");
return;
}

String filename = args[0];

try {
BufferedImage bimg = ImageIO.read(new File(filename));

// map สำหรับเก็บว่าจุดใดบ้างที่ได้รับการสำรวจไปแล้ว
boolean[][] painted =
new boolean[bimg.getHeight()][bimg.getWidth()];


// วนรอบทุกจุดในรูป
for(int i = 0 ; i < bimg.getHeight() ; i++){
for(int j = 0 ; j < bimg.getWidth() ; j++) {

// ถ้าจุดนั้นเป็นสีดำ และยังไม่เคยถูกสำรวจ
if(isBlack(bimg,j,i) && !painted[i][j]){

int pixelCount = floodfill(bimg,painted,j,i);

System.out.println("Blob detected : " + pixelCount + " pixels");
}

}
}

} catch (IOException ex) {
ex.printStackTrace();
}

}

}


While the recursive version uses less LOC, easier to understand and easier to code than the first solution, its performance is not as good as the first one and it actually gives me java.lang.StackOverflowError when used with the sample image. But if the problem's size is small, I prefer implementing the recursive version too.