Sunday, March 13, 2011

ImageJ 2: Invert an image

เรายังอยู่ที่การ invert image ครับ แต่จะลองใช้เทคนิคในการเข้าถึงพิกเซลต่างๆกัน ลองดูตัวอย่างโคัดครับ ในที่นี้มีอย่างน้อย 4 วิธี ดูรายละเอียดตามที่หมายเหตุได้ครับ

//Plugin to invert a grayscale image
import ij.ImagePlus;
import ij.plugin.filter.PlugInFilter;
import ij.process.ImageProcessor;
import ij.process.ByteProcessor;

// _ in the class name = plugin
public class My_Invert implements PlugInFilter 
{
 public int setup(String arg, ImagePlus im) 
 {
  return DOES_8G;  //accept only 8-bit grayscale image
 }

 public void run(ImageProcessor ip) 
 {  
  //1. use getPixel, putPixel to access each pixel
  //they check image boundary+invoke function call, slowest
  int w = ip.getWidth();
  int h = ip.getHeight();
  //for each pixel
  for(int x=0;x<w;x++)
   for(int y=0;y<h;y++)
    ip.putPixel(x,y,255-ip.getPixel(x,y));
    
  /*
  //2. use get,set which do not check boundary
  //still invoke function, faster
  int w = ip.getWidth();
  int h = ip.getHeight();
  //for each pixel
  for(int x=0;x<w;x++)
   for(int y=0;y<h;y++)
    ip.set(x,y,255-ip.get(x,y));
  */
  
  /*  
  //3. use get,set with 1d indices
  //only 1 loop, invoke function, more faster
  int MN = ip.getPixelCount(); //total pixels
  for(int p=0;p<MN;p++)
   ip.set(p, 255-ip.get(p));
  */
  
  /*
  //4. use getPixels to directly process pixel array
  //direct access, no function invocation
  //fastest but more memory need
  //check if ip is ByteProcessor (0-255)
  if(!(ip instanceof ByteProcessor))
   return;
  if(!(ip.getPixels() instanceof byte[]))
   return;
  //dump to array but by reference!!!
  byte[] pixels = (byte[]) ip.getPixels(); 
  int MN = ip.getPixelCount();
  for(int p=0;p<MN;p++)
  {
   int v = 0xFF & pixels[p]; //convert to integer
   v = 255-v;
   pixels[p] = (byte) (0xFF & v); //to byte
  }
  */      
 }
}

ตามทฤษฎีแล้วแต่ละวิธีจะให้ความเร็วต่างกัน ซึ่งจะเห็นชัดขึ้นเมื่อรูปมีขนาดใหญ่ขึ้นครับ

ImageJ 1: Invert an image

ImageJ เป็นซอฟต์แวร์ทางด้านการประมวลผลภาพที่มีประสิทธิภาพ ไม่เสียค่าใช้จ่าย และใช้งานได้ไม่ยากครับ

ในที่นี้เราจะลองศึกษาการประยุกต์ใช้งาน โดยสร้าง plugin ซึ่งเนื้อหาโดยส่วนใหญ่ในหัวข้อนี้และถัดๆไป ผมจะอ้างอิงจากเอกสารในเว็บ http://rsbweb.nih.gov/ij/ และหนังสือ Wilhelm Burger and Mark James Burge, Digital Image Processing: An Algorithmic Introduction using Java, Springer, 2008.

ซึ่งไม่ได้มีเจตนาจะละเมิดลิขสิทธิ์ใดๆทั้งสิ้น

ถ้าพร้อมแล้ว ดาวน์โหลด ImageJ ได้ที่ http://rsbweb.nih.gov/ij/ ครับ

หลังจากคลายไฟล์ที่ดาวน์โหลดมาแ้ล้ว เมื่อเรียกใช้งานก็จะเห็นหน้าตาแบบนี้ครับ

เราจะลองสร้าง plugin เพื่อเพิ่มความสามารถในการประมวลผลภาพตามที่เราต้องการ ในครั้งนี้เราจะลอง invert รูปสีเทาครับ ที่เราเรียกว่าทำ image negative

ขั้นตอนในการสร้าง PlugIn
1. สร้างโฟลเดอร์ หรือ ไฟล์จาวา ในไดเรกทอรี ImageJ/plugins ครับ ในที่นี้เราจะสร้างโฟลเดอร์ชื่อ My และสร้างไฟล์ชื่อ My_Invert.java ไว้
โปรดสังเกตว่า เราใช้เครื่องหมาย _ เพื่อแสดงว่าไฟล์นี้เป็น plugin และจะถูกโหลดเข้าไปใน ImageJ โดยอัตโนมัติ
2. ใช้ IDE ตัวไหนก็ได้แก้ไขไฟล์จาวา ชื่อคลาสต้องตรงกับชื่อไฟล์ plugin
3. ใช้ ImageJ คอมไพล์ไฟล์นั้น
4. แ้ก้ไขไฟล์ถ้ามีข้อผิดพลาด
5. ถ้าไม่มีข้อผิดพลาดก็จะใช้ plugin นั้นได้เลยทันที

ลองมาดูโค้ดของ plugin invert กันครับ
//Plugin to invert a grayscale image
import ij.ImagePlus;
import ij.plugin.filter.PlugInFilter;
import ij.process.ImageProcessor;
import ij.process.ByteProcessor;

// _ in the class name = plugin
public class My_Invert implements PlugInFilter 
{
 public int setup(String arg, ImagePlus im) 
 {
  return DOES_8G;  //accept only 8-bit grayscale image
 }

 public void run(ImageProcessor ip) 
 {  
  //1. use getPixel, putPixel to access each pixel
  //they check image boundary+invoke function call, slowest
  //0.015 second
  int w = ip.getWidth();
  int h = ip.getHeight();
  //for each pixel
  for(int x=0;x<w;x++)
   for(int y=0;y<h;y++)
    ip.putPixel(x,y,255-ip.getPixel(x,y));
        
 }
}

ทดลองโหลดรูปใดๆ ขึ้นมาก่อน เช่น

จากนั้น คอมไพล์โดยใช้เมนู Plugin / Compile and Run...

ผลลัพธ์ที่ได้

จะเห็นได้ว่าเรามีการเข้าถึงและประมวลผลพิกเซลได้โดยสะดวกครับ

Saturday, July 3, 2010

Basic Image Loading and Display

เรามาลองทำโปรเจคเกี่ยวกับ Image processing เบื้องต้นด้วยจาวากันครับ

ก่อนอื่น เราจะออกแบบให้มีหน้าแสดงผลง่ายๆ โดยใช้เฟรม (JFrame) ครับ ซึ่งก็จะมีหน้าตาดังนี้

ซึ่งก็ใช้รหัสโปรแกรมง่ายๆ ดังนี้ครับ
package dip;

import javax.swing.JFrame;

public class Main
{
    public static void main(String[] args)
    {
        new ImageFrame();
    }
}

class ImageFrame extends JFrame
{
    public ImageFrame()
    {
        //set Frame's properties
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setTitle("Image Processing");   //frame title
        setSize(250,250);   //initial size
        setLocationRelativeTo(null); //set window to center of the screen
        setVisible(true);
    }    
}

ในขั้นตอนต่อไปก็จะทำการทดลองอ่านรูปขึ้นมา แล้วแสดงผลในหน้าต่างดังกล่าว ซึ่งจะมีขั้นตอนคือ

  1. สร้าง component เพื่อรองรับรูป อาจจะเป็น JComponent หรือ JPanel ก็ำได้
  2. อ่านรูปที่ต้องการ
  3. วาดรูปนั้นลงบน component
  4. เพิ่ม component ที่มีรูปอยู่นี้บนเฟรม
ขั้นตอนที่ 1-3 สร้าง component เพื่อรองรับรูป, อ่านรูปที่ต้องการ และวาดรูปลงบน component โดยกำหนดให้รูปอยู่ที่ d:/lena.jpg
เราจะสร้างคลาสขึ้นมาใหม่ ดังนี้

package dip;

import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JComponent;

class Canvas extends JComponent
{
    private BufferedImage img;

    public Canvas()
    {
        try
        {
            //read an image lena.jpg
            img = ImageIO.read(new File("d:/lena.jpg"));
        }
        catch(IOException e)
        {
            e.printStackTrace();
        }
    }

    public void paintComponent(Graphics g)
    {
        if(img==null)
            return;
        g.drawImage(img,0,0,null);
    }
}

ขั้นตอนที่ 4 เพิ่ม component นี้ให้เฟรม ในที่นี้เราจะแก้ไข constructor ของเฟรมหลัก ดังนี้

    public ImageFrame()
    {
        //set Frame's properties
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setTitle("Image Processing");   //frame title
        setSize(250,250);   //initial size
        setLocationRelativeTo(null); //set window to center of the screen
       //create and add image component to frame
        Canvas myCanvas = new Canvas();
        add(myCanvas);
        setVisible(true);
    } 

เมื่อรันโปรแกรม ผลลัพธ์ที่ได้ก็จะเป็นดังนี้ครับ

Tuesday, May 11, 2010

Table's backshashbox

\usepackage{slashbox}

\begin{tabular}{|l|l|l|} 
\hline
\backslashbox[2cm]{Lesson}{Date} & Monday & Tuesday\\
\hline
Stratigraphy &roomA & roomA\\
Chemistry & roomB & Lab$\alpha$\\
Physics & roomC & Lab$\beta$\\
\hline
\end{tabular}

ผลลัพธ์

ทำกรอบล้อมข้อความ กำหนดสีพื้นหลัง

วิธีง่ายๆคือ ใช้แพคเกจ
\usepackage{framed}

แล้วใช้งาน เช่น
\begin{frame}
text
\end{frame}

หรือถ้าอยากใช้แถบในแนวดิ่งแทน ก็ใช้
\begin{leftbar}
 text
\end{leftbar}

หรือถ้าต้องการกำหนดสีพื้นหลังของข้อความ
\usepackage{framed}
\usepackage{color}

\definecolor{shadecolor}{rgb}{0.9,0.8,1}
\begin{shaded}
text
\end{shaded}