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 อาจทำได้ เช่น กำหนดระยะห่างสูงสุดระหว่างเส้นสองเส้นในแนวเดียวกัน ถ้าระยะห่างนี้น้อยกว่าที่กำหนดก็ให้ถือว่าเส้นทั้งสองเป็นเส้นตรงเส้นเดียวกัน

Tuesday, February 26, 2013

การหาขอบ (Edge detection)

การหาขอบของภาพ ถูกนำมาใช้เพื่อหลายวัตถุประสงค์ เช่น เพื่อ segment ภาพนั้นออกเป็นส่วนๆ หรือ เพื่อเป็นกระบวนการเบื้องต้นสำหรับการหาเส้นตรง หรือวงกลมในภาพ ฯลฯ ในที่นี้เราจะลองใช้คำสั่งสำเร็จรูปของ OpenCV เพื่อหาขอบภาพ ด้วยสามวิธี ได้แก่



Canny

import cv2
import numpy as np

img = cv2.imread("lena.jpg",cv2.CV_LOAD_IMAGE_GRAYSCALE)
#Appy Gaussian blur to remove some noises
inoise = cv2.GaussianBlur(img,(3,3),sigmaX=0)

#Canny edge detection
lowThresh = 50
upThresh = 100
out = cv2.Canny(inoise,lowThresh,upThresh)

cv2.imshow("Origin",img)
cv2.imshow("Edge Detection",out)
cv2.waitKey()
cv2.destroyAllWindows()


Sobel
#Gradient X
gradX = cv2.Sobel(inoise,cv2.CV_16S,dx=1,dy=0)
#Gradient Y
gradY = cv2.Sobel(inoise,cv2.CV_16S,dx=0,dy=1)
#Convert Gradients to 8 bits
gradX = cv2.convertScaleAbs(gradX)
gradY = cv2.convertScaleAbs(gradY)
#Total Gradient (approximate)
#grad = abs(gradX) + abs(gradY)
out = cv2.addWeighted(gradX,1,gradY,1,0)


Laplacian
out = cv2.Laplacian(inoise,ddepth=cv2.CV_16S,ksize=3)
#Need to set depth to 16-bit signed integer because Laplacian can give negative intensity.
# Convert it to 8-bit image with absolute value.
out = cv2.convertScaleAbs(out)