Saturday, October 12, 2013

JavaScript Gauge

วันนี้ลองหาโค้ด JavaScript Gauge เจอสองเว็บที่น่าสนใจครับ
http://justgage.com/

แสดงผลเป็นเวกเตอร์ทำให้รูปชัดเจนแม้ย่อหรือขยาย ดูแล้วเข้าใจง่าย แต่ยังมีข้อจำกัดหลายอย่างที่กำลังพัฒนาอยู่ เวอร์ชันที่เป็น demo อยู่มีฟังก์ชันที่น่าสนใจมาก http://justgage.com/demos/

ตัวอย่างโค้ดโดยใช้เวอร์ชันที่กำลังพัฒนาอยู่ครับ ดาวน์โหลดได้ที่ http://justgage.com/justgage.js
<!doctype html>
<html>
<head>
 <title>Auto-adjust</title>
 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />   
 <script src="raphael.2.1.0.min.js"></script>
 <script src="justgage.js"></script>
 <script>   
   window.onload = function(){
  var g1 = new JustGage({
    id: "g1", 
    value: 150, 
    min: 0,
    max: 200,
    title: "PPM10",
    label: "ug/m3",
    customSectors: [{
     color : "#00ff00",
     lo : 0,
     hi : 120
   }, {
     color : "#ff0000",
     lo : 121,
     hi : 300
    }]
  });
   };
 </script>
</head>

<body>
 <div id="g1" style="width:200px; height:160px; display:inline-block;"></div>
</body>
</html>

และอีกตัวหนึ่งครับ
https://github.com/Mikhus/canv-gauge
ตัวนี้สามารถกำหนดค่าได้ค่อนข้างมาก และสามารถระบุค่าเป็นแบบ tag ของ html หรือเขียนเป็นโค้ดของ JavaScript ก็ได้
ตัวอย่างโค้ดแบบ html
<!doctype html>
<html>
<head>
 <title>Canv-gauge</title>
 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />   
 <script src="gauge.min.js"></script>
</head>

<body>
 <canvas width="200" height="200"
  data-type="canv-gauge"
  data-title="PPM10"
  data-value="100"
  data-units="ug/m3"
  data-max-value="300"
  data-major-ticks="0 100 200 300"
  data-highlights="0 120 #0f0, 120 300 #f00"
 ></canvas>
</body>
</html>

ลองเลือกใช้กันดูครับ

Saturday, August 10, 2013

Histogram

เราลองมาคำนวณ Histogram ของรูปกันครับ



import cv2
import numpy as np

img = cv2.imread("lena.jpg")
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)

#compute histogram
#create zero array for histogram, 255 bins
hist = np.zeros(256)
#for each pixel
for p in gray.flat:
    hist[p]+=1

#normalize histogram to display in 256x256 image
hist = hist/hist.max()*255
hist = np.uint8(hist)

#create a new image for displaying histogram
#note that the image origin is at top-left corner
out = np.zeros((256,256,3),dtype=np.uint8)
for r in range(256):
    cv2.line(out,(r,255),(r,255-hist[r]),(0,0,255))

cv2.imshow("Original",gray)
cv2.imshow("Histogram",out)
cv2.waitKey()
cv2.destroyAllWindows()

Thursday, August 8, 2013

OpenCV กับ Video

เมื่อเราต้องการเปิดไฟล์วิดีโอด้วย OpenCV เพื่อนำเฟรมมาประมวลผล จะต้องทำอย่างไรบ้าง

วิธีที่ 1
  • ถ้าไม่ต้องการติดตั้ง video codec เพิ่มเติม หรือไม่สามารถติดตั้งได้ อาจจะจำเป็นต้องแปลงไฟล์วิดีโอให้อยู่ในรูปแบบ raw นั่นคือ ไฟล์วิดีโอที่ไม่ได้เข้ารหัส ดังข้อแนะนำ จาก http://opencv.willowgarage.com/wiki/VideoCodecs ซึ่งจะมีหลายวิธี เช่นวิธีนี้
    1. ดาวน์โหลด mencoder จาก http://oss.netfarm.it/mplayer-win32.php
    2. แตกไฟล์เฉพาะ mencoder.exe ออกมา
    3. ใช้คำสั่ง mencoder in.mp4 -ovc raw -nosound -o out.avi เพื่อแปลงไฟล์ in.mp4 ให้เป็น raw format ชื่อ out.avi
วิธีที่ 2
  • ตรวจสอบว่าเครื่องคอมพิวเตอร์ของเราสามารถเล่นไฟล์นั้นได้ โดยใช้โปรแกรมทั่วไปเช่น media player ถ้าเล่นได้ แสดงว่าเครื่องของเรามี codec ที่ใช้ถอดรหัสของวิดีโอนั้นแล้ว ถ้าเล่นไม่ได้ ให้ลองติดตั้ง video codec เช่น k-lite codec pack เสียก่อน จากนั้นจึงทดสอบอีกครั้ง
  • ให้เปิดไฟล์ OpenCV (ดาวน์โหลดได้ที่ opencv.org) ด้วยโปรแกรมเช่น 7zip สมมติว่าชื่อ OpenCV-2.4.6.0.exe
  • แตกไฟล์ชื่อ \opencv\build\x86\vc11\bin\opencv_ffmpeg246.dll ออกมา
  • คัดลอกไฟล์ดังกล่าว ไปไว้ที่เดียวกัน source code
  • ถ้า python ทำงานอยู่ ให้ปิดและเปิดใหม่อีกครััง
ไม่ว่าจะใช้วิธีที่ 1 หรือ 2 ก็ตาม จะใช้รหัสโปรแกรมเพื่อทดสอบดังนี้
import cv2
import sys

#open video file
cap = cv2.VideoCapture('video.mp4')

#check if connected
if cap.isOpened():
    print "Open"
else:
    print "Close"
    sys.exit(-1)

#loop to capture each frame and display
#until press ESC
key = 0
ret = True
fps = int(cap.get(5))   #get video FPS

while key!=27:
    ret,im = cap.read()
    if ret==False:
        break
    #display image
    cv2.imshow('video test',im)
    #wait for input key every 1000/fps ms
    key = cv2.waitKey(1000/fps)

#close capture device
cap.release()
#destroy all ui windows
cv2.destroyAllWindows()

ก็ควรที่จะอ่านไฟล์วิดีโอที่เข้ารหัสนี้ได้

ถ้าจะต่อกับกล้อง webcam ปกติที่มีมาพร้อมคอมพิวเตอร์ หรือเป็นแบบ USB สามารถใช้
cap = cv2.VideoCapture(0)

สำหรับภาพจาก ip webcam ที่ส่งข้อมูลไฟล์มาเป็น mjpeg เราสามารถใช้
cap = cv2.VideoCapture('http://admin:admin@192.168.1.3/img/video.mjpeg')

ทั้งนี้ขึ้นอยู่กับ API ของ ip webcam นั้นด้วยนะครับ แต่ละยี่ห้ออาจจะใช้คำสั่งต่างกัน

Saturday, March 2, 2013

หาขอบที่เป็นเส้นตรงด้วย Probabilistic Hough Transform

นอกจาก Hough Transform แล้ว OpenCV ยังมีอีกคำสั่งหนึ่งในการหาขอบเส้นตรง นั่นคือ HoughLinesP ซึ่งสามารถศึกษาวิธีการได้จาก [1]

คำสั่ง HoughLinesP จะให้ผลลัพธ์เป็นจุดสองจุดที่เป็นต้น (x1,y1) และปลาย (x2,y2) ของเส้นตรงที่คำนวณได้ การพลอตผลลัพธ์ก็จะทำได้ง่ายขึ้น

ลองดูตัวอย่าง เปรียบเทียบกับบทความก่อนหน้าครับ


import cv2
import numpy as np

img = cv2.imread("building.jpg")
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
#Appy Gaussian blur to remove some noises
inoise = cv2.GaussianBlur(gray,(3,3),sigmaX=0)

#Canny
lowThresh = 50
upThresh = 200
edge = cv2.Canny(inoise,lowThresh,upThresh)

#Probabilistic Hough Line Transform
lines = cv2.HoughLinesP(edge,rho=1,theta=np.pi/180,threshold=120,minLineLength=30,maxLineGap=10)

img2 = img.copy()
#Plot detected lines
for x1,y1,x2,y2 in lines[0]:
    cv2.line(img2,(x1,y1),(x2,y2),(0,255,0),2)

cv2.imshow("Probabilistic Hough Lines",img2)
cv2.waitKey()
cv2.destroyAllWindows()

อ้างอิง
[1] Matas, J. and Galambos, C. and Kittler, J.V., Robust Detection of Lines Using the Progressive Probabilistic
Hough Transform. CVIU 78 1, pp 119-137 (2000)

Friday, March 1, 2013

การหาขอบที่เป็นเส้นตรงด้วย Hough Line Transform

เมื่อเราหาขอบ (edge) ของรูปภาพได้แล้ว เราจะทราบได้อย่างไรว่ามีขอบที่เป็นเส้นตรงหรือไม่




เราสามารถใช้ Hough Transform เพื่อตอบคำถามนี้ได้ ดังนี้

ถ้ามีเส้นตรงใดๆ (สีดำ) ตามรูป ที่มีระยะจากจุด (0,0) ไปตั้งฉากกับเส้นตรงนั้นเท่ากับระยะ r โดยเส้นตั้งฉากนี้ทำมุมกับแกน x เป็นมุม T
เส้นตรงปกติจะมีสมการเป็น y = mx + c

แต่เราสามารถเขียนอยู่ในรูปของ polar coordinate คือ
y = (-1/tan(T)) x + (r/sin(T))
y = -cos(T)/sin(T) x  + r/sin(T)
y sin(T) = -x cos(T) + r
หรือ
y sin(T) + x cos(T) = r

โดยทั่วไปแล้ว จะกำหนดค่า T ให้อยู่ในช่วง [0, PI] และค่า r ใน [-D, D] เมื่อ D คือความยาวของเส้นทะแยงมุมของภาพ (ระยะไกลสุดของภาพที่วัดจากจุดกำเนิด)

สมมติว่าเรามีจุดๆหนึ่ง เส้นตรงที่ลากผ่านจุดนี้จะมีได้ไม่จำกัด นั่นคือ จะมีค่า r และ T มากมาย

แต่ถ้ามีจุดสองจุดขึ้นไป เส้นตรงที่ลากผ่านจุดเหล่านี้จะมีได้เส้นเดียว นั่นคือ เส้นที่มีค่า r และ T เท่ากัน

ดังนั้น เมื่อเราคิดใน polar coordinate หลักการหาขอบที่เป็นเส้นตรงด้วย Hough Transform ก็คือ

1. หาขอบของภาพด้วยวิธีิใดๆ เช่น Canny ให้ได้ผลลัพธ์เป็นขาวดำ (ขาว = ขอบ)
2. เริ่มต้นด้วย r=0, T=0 (เส้นตรงที่ทับกับแกน y)  ไล่ตรวจสอบว่ามีพิกเซลสีขาว (ขอบ) ที่อยู่บนแนวเส้นตรงนี้เท่าใด ถ้ามีเกินกว่าค่า threshold ที่กำหนด (เช่น 100 จุด) ก็ให้เก็บค่า (r,T) นี้ไว้
3. เปลี่ยนแปลงค่า r ไปเรื่อยๆ ตรวจสอบเช่นเดิม
4. เปลี่ยนแปลงค่า T ไปเรื่อยๆ ตรวจสอบเช่นเดิม
5. สุดท้ายจะได้ชุดของ (r,T) ซึ่งแต่ละคู่จะเป็นค่าของขอบเส้นตรงที่อยู่ในรูป
6. พลอตเส้นตรงเหล่านั้น

ใน OpenCV เราสามารถใช้คำสั่ง cv2.HoughLines() ดังตัวอย่างต่อไปนี้

import cv2
import numpy as np

img = cv2.imread("building.jpg")
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
#Appy Gaussian blur to remove some noises
inoise = cv2.GaussianBlur(gray,(3,3),sigmaX=0)

#Canny
lowThresh = 50
upThresh = 200
edge = cv2.Canny(inoise,lowThresh,upThresh)

#Hough Line Transform
lines = cv2.HoughLines(edge,rho=1,theta=np.pi/180,threshold=120)
#rho = resolution of r, here 1 pixel
#theta = resolution of theta, here 1 degree
#threshold =  The minimum number of intersections to detect a line

img2 = img.copy()
#Plot detected lines
for r,theta in lines[0]:
    #(x0,y0) at the intersection point
    x0 = r*np.cos(theta)
    y0 = r*np.sin(theta)
    #(x1,y1) along the line to the left of (x0,y0) for 1000 units
    x1 =  int(x0-1000*np.sin(theta))
    y1 =  int(y0+1000*np.cos(theta))
    #(x2,y2) along the line to the right of (x0,y0) for 1000 units
    x2 =  int(x0+1000*np.sin(theta))
    y2 =  int(y0-1000*np.cos(theta))
    cv2.line(img2,(x1,y1),(x2,y2),(0,255,0),1)

cv2.imshow("Original",img)
cv2.imshow("Edge",edge)
cv2.imshow("Hough Lines",img2)
cv2.waitKey()
cv2.destroyAllWindows()

ให้สังเกตลูปในการพลอตเส้นตรง

ปกติเราสามารถกำหนดจุดสองจุดในการพลอตเส้นตรงใดๆ ได้
เช่นจากรูปข้างต้น เราอาจกำหนดจุดในการพลอตคือ
x1 = 0, y1 = r/sin(T)
x2 = r/cos(T), y2 =0
เพื่อเป็นการพลอตเส้นตรงจากขอบรูปไปยังอีกขอบหนึ่ง
แต่ปัญหาที่อาจจะเกิดขึ้นก็คือ ถ้าเป็นเส้นตรงในแนวนอน (T=0) ค่า y1 จะคำนวณไม่ได้

ดังนั้นในโค้ดของโปรแกรม จึงใช้อีกรูปแบบหนึ่ง ดังรูป
นั่นคือ
x1 = x0 - 1000 sin(T)
y1 = y0 + 1000 cos(T)

x2 = x0 + 1000 sin(T)
y2 = y0 - 1000 cos(T)

โดยที่ค่า 1000 เป็นระยะโดยประมาณครับ สามารถปรับเปลี่ยนได้

ข้อสังเกต

  1. ความแม่นยำขึ้นอยู่กับวิธีการหาขอบ (edge detection)
  2. ขอบเส้นตรงที่หาเจอ อาจจะไม่ใช่ขอบที่ถูกต้อง เพราะวิธีการนี้ตรวจสอบแค่ว่ามีพิกเซลที่วางตัวอยู่ในแนวเส้นตรงเดียวกันมากน้อยแค่ไหน ดังนั้น ในแนวใดๆ แม้ว่าจะไม่ใช่ขอบเส้นตรงจริงๆ หากมีจำนวนพิกเซลที่เป็นขอบอยู่จำนวนมาก ก็จะถูกกำหนดให้เป็นขอบเส้นตรง ตัวอย่างเช่น ถ้าเป็นต้นไม้พุ่มที่มีใบไม้จำนวนมาก ขอบของใบไม้หลายๆใบที่อยู่ในแนวเดียวกัน อาจจะทำให้ถูกคิดเป็นขอบเส้นตรงได้ ทั้งๆที่ไม่ใช่ เช่น เส้นสีเขียวในรูปด้านล่าง
  3. การแก้ปัญหาในข้อ 2 อาจทำได้ เช่น กำหนดระยะห่างสูงสุดระหว่างเส้นสองเส้นในแนวเดียวกัน ถ้าระยะห่างนี้น้อยกว่าที่กำหนดก็ให้ถือว่าเส้นทั้งสองเป็นเส้นตรงเส้นเดียวกัน