Sunday, December 29, 2013

เมื่อ ADB ไม่สามารถติดต่อ AVD หรือ Mobile Device ได้

เมื่อเราใช้ Eclipse และสั่งรัน emulator (AVD) ด้วย ADT plugin จนเคยชินด้วยความสะดวกสบาย จนมาวันหนึ่งที่เราต้องการสั่งรัน AVD (Android Virtual Device) โดยไม่มี Eclipse เราจะทำอย่างไร

ก่อนอื่น ต้องรู้ก่อนว่า จะทดสอบโปรแกรมบน AVD นั้น ซึ่งใช้ Eclipse คลิกอย่างเดียว มันมีกระบวนการเบื้องหลังอยู่อย่างไรบ้าง

  1. Start ADB (Android Debug Bridge) ตัวนี้ล่ะครับที่ทำให้เราสามารถเชื่อมต่อกับ emulator หรือ device จริงๆได้
  2. Start AVD หรือ เสียบสาย USB ที่เชื่อมต่อกับอุปกรณ์
  3. เชื่อม ADB กับ AVD หรืออุปกรณ์
  4. ตอนนี้ก็สามารถติดตั้ง ทดสอบโปรแกรม หรือจัดการอะไรต่างๆมากมายบน AVD หรืออุปกรณ์ได้แล้ว
 อย่างไรก็ตาม ปัญหาที่มักจะเจอ คือ ขั้นตอนที่ 3 ไม่สามารถสามารถติดต่อ AVD หรือ Mobile Device ได้

วิธีการลองแก้ไข คือ
  1. ตรวจสอบให้ชัดเจนว่า AVD หรืออุปกรณ์ได้ทำงานแล้ว (เปิดอยู่) เสียบสาย USB แน่น ลองดึงออกแล้วเปลี่ยนช่องเสียบดู
  2. ตรวจสอบว่าได้เปิด USB debugging ใน developer mode แล้ว
  3. เปิด command prompt และไปที่โฟลเดอร์ของ Android SDK หาโฟลเดอร์ sdk\platform-tools แล้วพิมพ์คำสั่งว่า "adb devices" ถ้าขึ้นชื่อ AVD หรืออุปกรณ์ และไม่ตามด้วยคำว่า offline แสดงว่าใช้งานได้แล้ว (ถ้าไม่ได้ใช่ eclipse ตัว adb จะไม่ได้ทำงานเองนะครับ ต้องไป start มัน ซึ่งสามารถใช้คำสั่ง adb devices ได้เลย)
  4. แต่ถ้ายังมีคำว่า offline ตามท้าย ก็ยังใช้ไม่ได้ ลอง restart adb ใหม่ด้วยคำสั่ง "adb kill-server" (หรือใช้ task manager เพื่อ stop process ชื่อ adb.exe ก็ได้) ตามด้วย "adb devices" อีกครั้ง แล้วดูผล
  5. บางครั้งต้องติดตั้ง driver ของอุปกรณ์/มือถือ ชิ้นนั้นก่อน ซึ่งมักจะมีมาพร้อมกับอุปกรณ์เอง (อยู่ในโฟลเดอร์หรือต้องเปลี่ยนโหมดอุปกรณ์ให้เป็นการติดตั้งซอฟต์แวร์ก่อน) หรือสามารถหาดาวน์โหลดได้ทั่วไป เช่น ดูจากที่นี่ครับ
  6. หากยังไม่ได้ ลองตรวจสอบว่ามี program หรือ process ใด ที่ใช้ port 5555/5554 ทับกับ adb/AVD หรือไม่ ถ้ามีก็หยุด process นั้น แล้วลองข้อ 3 ใหม่
  7. หากยังไม่ได้ ลองลบ AVD นั้น แล้วสร้างขึ้นใหม่ (บางคนไม่ลบ แค่เลือก wipe data ตอน start ก็ใช้งานได้) แล้วลองข้อ 3 ใหม่
  8. หากยังไม่ได้อีก บางเว็บแนะนำให้อัพเดท Android SDK แล้วลองข้อ 3 ใหม่
  9. หากยังไม่ได้อีก ลองบังคับการเชื่อมต่อผ่านไอพี โดยใช้คำสั่ง "adb connect IP:port" ดู ซึ่งมักจะเป็นกรณีพิเศษ เช่นใช้ emulator ที่ไม่ได้มากับ SDK ตัวอย่างเช่น "adb connect localhost:5555"
  10. ถ้ายังไม่ได้ อาจจะต้องลองหาว่าเป็นเฉพาะ emulator ตัวนั้น หรืออุปกรณ์ตัวนั้นหรือเปล่าครับ ซึ่งอาจจะมีวิธีอื่นๆอีก ถ้าหาเจอเพิ่มเติมจะมาอัพเดทต่อไปนะครับ

Monday, December 23, 2013

Fast Android Emulator

จากการที่ได้เขียนโปรแกรมบน Android และต้องทดสอบกับ Emulator คำถามนึงที่เกือบทุกคนจะถามเหมือนกันคือ ทำไม Emulator มันถึงช้าได้ขนาดนี้ เราลองมาหาทางเลือกอื่นๆ สำหรับเพิ่มความเร็วให้ Android Emulator กันดีกว่าครับ

วิธีแรก (ง่ายสุด)
ใช้ genymotion [1] ดาวน์โหลด ติดตั้ง ใช้งานได้เลย
ข้อดี

  • ใช้งานง่าย
  • มีแบบฟรีและแบบเสียค่าใช้จ่าย
  • สามารถจำลอง sensor แบบต่างๆได้ เช่น GPS / Screen Rotation / Battery / Gyroscope ฯลฯ
  • มี plugin ให้กับ eclipse และ android studio สามารถเรียก emulator ได้จากใน IDE และรันโปรแกรมได้โดยตรง

ข้อจำกัดเล็กน้อย

  • ต้องสมัครเป็นสมาชิก (ฟรี)
  • ต้องดาวน์โหลดและติดตั้ง emulator ใหม่ ถ้าต้องการใช้งานในเครื่องอื่นๆเพิ่มเติม
วิธีที่สอง
ติดตั้ง ORACLE VirtualBox และ AndroVM ซึ่งจะมีขั้นตอนพอสมควรดังนี้
1. ดาวน์โหลดและติดตั้ง VirtualBox [2] ซึ่งถ้าต้องการใช้แบบ portable สามารถดาวน์โหลดตัวช่วยคือ Portable VirtualBox [3]
หมายเหตุ ตัว genymotion ก็ต้องใช้ VirtualBox เหมือนกัน แต่มันมีเวอร์ชัน bundle คือผนวกมาให้กับตอนดาวน์โหลดเลย

2. ดาวน์โหลด AndroVM [4] (ซึ่งความจริงก็คือต้นตำรับของ genymotion นั่นแหละ) จะได้ไฟล์ .ova มา ซึ่งแน่นอนว่าจะไม่ได้อัพเดทแล้ว
หมายเหตุ ตอนดาวน์โหลด ถ้ามี t หมายถึง tablet ส่วน p หมายถึง phone ครับ หาก
ใครจะใช้ไฟล์ ova ของ genymotion ก็ได้ ไปหากันเอาเองนะครับ


3. รัน VirtualBox จะได้หน้าตาแบบนี้ครับ


4. เลือกเมนู File/Import Appliance แล้วเลือกไฟล์ ova ที่ดาวน์โหลดมาจาก AndroVM คลิก Next และ Import

5. จะเกิด VM ใหม่ในแถบด้านซ้ายมือดังรูป


ตอนนี้ถ้าลองคลิก Start ก็จะสามารถเห็น Emulator แล้วครับ

อย่างไรก็ตาม Emulator ที่มีตอนนี้ จะยังไม่สามารถใช้งานกับ adb ได้ครับ เพราะมันไม่รู้จักกัน เราต้องเชื่อม emulator กับ adb เข้าด้วยกันก่อน เพื่อให้รันหรือติดตั้งโปรแกรมได้

6. กลับไปที่หน้า VirtualBox จากนั้นให้คลิกที่ปุ่ม Settings แล้วเลือกเมนู Network



ในหัวข้อ Attached to: ถ้าเลือกแบบ Bridged Adapter แล้วมีรายการชื่อ network card โผล่มาให้ ก็ให้เลือกแบบนี้

ถ้าไม่มีรายชื่อ network card โผล่มา ก็ต้องใช้รูปแบบอื่น เช่นในที่นี้จะใช้ NAT


จากนั้นเลือกปุ่ม Port Forwarding แล้วคลิกปุ่มสีเขียวขวามือ เพื่อสร้าง rule ใหม่ และกรอกข้อมูลตามนี้


7. จากนั้นให้ Start Emulator
8. ไปโฟลเดอร์ที่มี adb อยู่ ถ้าใครดาวน์โหลด adt bundle [5] มา ก็จะอยู่ในโฟลเดอร์ \adt-bundle\sdk\platform-tools
9. เปิดหน้าต่าง command line แล้วพิมพ์ว่า adb connect localhost


สังเกตว่าถ้าสำเร็จ จะเกิดคำว่า connected to localhost:5555

10. สามารถรันหรือติดตั้งโปรแกรม android ได้แล้วครับ

11. ถ้าจะติดตั้งไฟล์ apk เพื่อทดสอบ จะให้ง่าย ให้ย้ายไฟล์ apk ไปไว้ในโฟลเดอร์เดียวกับ adb แล้วใช้คำสั่ง
adb install xxx.apk

เมื่อติดตั้งสำเร็จ ก็จะเกิด icon ของโปรแกรมในกลุ่มของ application ให้ทดสอบได้ครับ

12. ถ้าจะรันผ่าน eclipse หรือ android studio ก็ทำได้เช่นกัน เพราะมันจะเห็น emulator ที่เราใช้งานโดยอัตโนมัติ

หมายเหตุ
  • ขั้นตอนการเชื่อมต่อระหว่าง adb กับ emulator ต้องทำเองทุกครั้งเมื่อเริ่ม emulator ใหม่

ข้อมูลอ้างอิง
[1] http://www.genymotion.com/
[2] https://www.virtualbox.org/
[3] http://www.vbox.me/
[4] http://androvm.org/blog/download/
[5] http://developer.android.com/sdk/index.html
[6] http://therockncoder.blogspot.com/2013/06/using-virtualbox-android-emulator.html
[7] http://stackoverflow.com/questions/6751911/android-emulation-on-virtual-box-in-eclipse

Wednesday, October 23, 2013

Phonegap + jQuery Mobile + Android

ช่วงนี้ Mobile Application กำลังมาแรง เค้าว่ากันว่าจะสร้าง app พวกนี้ น่าจะทำได้ประมาณ 3 แบบคือ
1. Native app เขียนโค้ดโดยตรงสำหรับ Android iOS WP8 ไปเลย
2. Web app เขียนเป็นเว็บโดยใช้ HTML+CSS+JavaScript ไง เร็วดี
3. Hybrid app เขียนเแบบ 2 แต่ wrap ให้เป็นแบบ 1 โดยใช้เครื่องมือช่วยเช่น Phonegap

ข้อดีข้อเสียก็ว่ากันไป แต่บทความนี้เราจะลองพูดถึงแบบที่ 3 ดูครับ โดยเน้นไปที่ Android อย่างไรก็ตาม วิธีนี้ถ้าใช้ phonegap อย่าลืมไปเช็ค Platform Support ก่อนที่ http://docs.phonegap.com/en/edge/guide_support_index.md.html#Platform%20Support ว่ามันทำได้อย่างที่เราต้องการหรือเปล่า

สิ่งที่เราต้องการในบทความนี้
1. phonegap (http://phonegap.com/install/) ผมลองเวอร์ชัน 3 แล้วงงพอควร แนะนำให้ใช้ 2.9.0 ซึ่งดาวน์โหลดได้โดยตรงที่ https://github.com/phonegap/phonegap/archive/2.9.0.zip
2. jquery Mobile เอามาเขียนเว็บให้ดูหน้าตาเข้าที ให้ touch ได้ swipe ได้นะ (http://jquerymobile.com/)
3. Genymotion ตัวนี้จะใช้หรือไม่ใช่ก็ได้ เอาไว้เป็น android emulator เพราะตัวที่มากับ ADT Bundle นั้นช้าที่สุดในจักรวาลลลลล ตัวนี้เร็วกว่าแบบเทียบกันไม่ได้ เหมาะกับโปรแกรมเมอร์ที่จะทดสอบโค้ดบน android emulator (http://genymotion.com/)
4. ADT Bundle สำหรับเขียนโค้ด build และรัน app (http://developer.android.com/sdk/installing/bundle.html)
5. ถ้าอยากลอง ตัวนี้จะใช้หรือไม่ก็ได้ Netbeans 7.4 Beta (http://download.netbeans.org/netbeans/7.4/beta/) ย้ำว่าควรเป็นตัวนี้เท่านั้น ตัวเต็มเวอร์ชัน 7.4 ดันเป็นตัวที่ใช้กับ phonegap ยากซะงั้น เลือกโหลดที่เป็น php เวอร์ชันก็พอครับ

วิธีการใช้งาน (เลือกเอาครับว่าจะใช้แบบไหน)
แบบที่ 1 ใช้ Netbeans
กลายเป็นว่า Netbeans เป็น IDE ที่สนับสนุนการใช้ phonegap เต็มที่ สำเร็จรูปมากกกก แต่ดันทำไม่สุด เวอร์ชัน Beta สามารถกำหนดว่า phonegap อยู่ที่ไดเรกทอรีไหน แต่เวอร์ชันจริงมันกลับไปเช็ค path การติดตั้ง phonegap ซึ่งผมไม่ต้องการ ผมอยากได้ IDE ที่มันเป็น portable ก๊อบใส่ USB drive ได้ง่ายๆ เลยถอยมาใช้ตัว Beta ดู
ปล. แนะนำให้โหลด Netbeans php ตัว zip มาใช้แบบไม่ต้องติดตั้ง
ปล.2 เว็บดาวน์โหลด Netbeans มันประหลาดมาก โหลดแล้วหลุดตลอดจนเซ็ง

หลังจากดาวน์โหลด แตกไฟล์ต่างๆเรียบร้อยแล้วก็เปิด Netbeans เลยครับ
-เลือกเมนู Tools/Options แล้วกำหนดไดเรกทอรีต่างๆ เช่น


-สร้าง New Project มันจะมีให้เลือก Phonegap Application หรือ Cordova Application ให้เลย พร้อมทั้งสำเนาไฟล์ต่างๆมาหมด สุดยอดดดด
-ลองรันรูปแบบต่างๆได้โดย


-สามารถ Build เป็น Native app ผ่าน phonegap แบบง่ายๆ โดยคลิกขวาที่ Project แล้วเลือก Build ก็จะได้ไฟล์ apk ที่เป็น debug เวอร์ชันในไดเรกทอรีชื่อ platforms/android/bin

เห็นไหมครับ ง่ายกว่านี้ไม่มีแล้ว แต่ๆๆๆๆ มันไม่สามารถเลือก emulator ตัวอื่น เช่นจาก genymotion ได้ครับ ต้อง build ออกมาก่อนเป็นไฟล์ apk แล้วใช้ adb ติดตั้งไปบน emulator ด้วยคำสั่งเช่น adb install -r xxx.apk ครับ รวมถึงมีข้อจำกัดอีกอย่างคือไฟล์ apk จะเป็น debug เวอร์ชันเท่านั้น ถ้าจะทำเป็น release หรือ sign เวอร์ชันก็จะมีขั้นตอนอีก

แบบที่ 2 ผ่าน Eclipse
อ้างอิงจากที่นี่เลยครับ http://www.adobe.com/devnet/html5/articles/getting-started-with-phonegap-in-eclipse-for-android.html
ทำตามเค้าเป๊ะ ขั้นตอนเยอะพอสมควร จนถึงขั้นตอนการกำหนด user permission หลังจากนั้นก็ไม่จำเป็นแล้วมั้ง
อย่าลืมก๊อบไดเรกทอรีของ jquery mobile ไปให้ครบด้วย
ตอนกำหนดสิทธิ์ในไฟล์ AndroidManifest.xml ถ้าไม่ได้ใช้อะไร ก็กำหนดแค่
uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"
ตัวเดียวก่อน ส่วนจะต้องกำหนด permission อะไรบ้าง ขึ้นอยู่กับ module ที่ใช้ ซึ่งสามารถอ้างอิงได้จากคู่มือ http://docs.phonegap.com/en/2.9.0/index.html ในส่วนของ API reference ครับ

มีหลายอย่างที่ยังงงๆอยู่ครับ จะลองไปเล่นดูเพิ่มเติม หากมีวิธีที่ง่ายขึ้นจะมาอัพเดทอีกทีครับ

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

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)

Monday, February 25, 2013

Filtering / Convolution

การทำ Filtering / Convolution ก็คือการเอา kernel / template / filter (หน้าต่างขนาดเล็กว่ารูป มักมีขนาดเป็นเลขคี่ เช่น 3x3, 5x5, 7x7 ฯลฯ) มาคูณกับพื้นที่ของรูปที่ kernel นั้นซ้อนทับอยู่ แล้วหาผลรวม จากนั้นก็นำไปแทนที่พิกเซลที่อยู่ตรงกับตำแหน่งกลางของ kernel นั้น

เราลองมาดูการทำ filtering ด้วย average filter เพื่อเบลอรูปกันครับ


import cv2
import numpy as np

img = cv2.imread("lena.jpg")

ksize = 5   #kernel size
#create average kernel
kernel = np.ones((ksize,ksize))/(ksize*ksize)
ddepth = -1     #depth of output image, -1 is same as input image
out = cv2.filter2D(img,-1,kernel)
#same as: out = cv2.blur(img,(ksize,ksize))

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

ถ้าต้องการเขียนโค้ดเอง สมมติว่าเป็นรูปสีเทา และคำนวณเฉพาะพิกเซลที่ตำแหน่งของ kernel ยังอยู่ในรูป
import cv2
import numpy as np

img = cv2.imread("lena.jpg", cv2.CV_LOAD_IMAGE_GRAYSCALE)

ksize = 5   #kernel size, odd value
#create average kernel
kernel = np.ones((ksize,ksize))/(ksize*ksize)

hksize = ksize%2    #half of kernel size
row,column = img.shape[:2]
out = img.copy()
for r in range(row-ksize+1):
    for c in range(column-ksize+1):
        temp = kernel*img[r:r+ksize,c:c+ksize]
        pix = temp.sum()
        out[r+hksize,c+hksize] = np.uint8(pix)

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

ต้องระวังบรรทัด out[r+hksize,c+hksize] = np.uint8(pix) ให้ดีครับ เพราะถ้าไม่ใช่ average filter ในบรรทัดนี้อาจจะต้องทำการ clip/normalize ค่าพิกเซลให้อยู่ในช่วง 0-255 ด้วย

ถ้าเป็นรูปสี BGR ก็ต้องเพิ่มอีก 1 loop สำหรับ channel

hksize = ksize%2    #half of kernel size
row,column,channel = img.shape
out = img.copy()
for k in range(channel):
    for r in range(row-ksize+1):
        for c in range(column-ksize+1):
            temp = kernel*img[r:r+ksize,c:c+ksize,k]
            pix = temp.sum()
            out[r+hksize,c+hksize,k] = np.uint8(pix)

วิธีนี้จะใช้เวลาค่อนข้างมากครับ แต่ทำให้เราสามารถประยุกต์ใช้ kernel ตามที่เราต้องการได้

Thresholding การเปลี่ยนรูปเทาให้เป็นขาวดำ

ทำได้หลายวิธีครับ ในที่นี้เราจะใช้วิธีง่ายๆโดยการกำหนดค่า threshold ซึ่งถ้าค่าพิกเซลมากกว่าหรือเท่ากับค่านี้ก็จะเปลี่ยนให้เป็นสีขาว (255) มิฉะนั้นก็เปลี่ยนเป็นสีดำ (0)

import cv2

img = cv2.imread("lena.jpg",cv2.CV_LOAD_IMAGE_GRAYSCALE)
thres = 128
ret,bw = cv2.threshold(img,thres,255,cv2.THRESH_BINARY)

cv2.imshow("Original",img)
cv2.imshow("Binary",bw)

cv2.waitKey()
cv2.destroyAllWindows()

หรือจะใช้เทคนิคของ NumPy โดยเปลี่ยนโค้ดข้างต้นเป็น
import cv2

img = cv2.imread("lena.jpg",cv2.CV_LOAD_IMAGE_GRAYSCALE)
thres = 128
bw = 255*np.ones_like(img)
bw[img < thres] = 0

cv2.imshow("Original",img)
cv2.imshow("Binary",bw)

cv2.waitKey()
cv2.destroyAllWindows()

จะได้ผลลัพธ์เหมือนกันครับ

การแยกแต่ละ channel ของรูป

ภาพที่อ่านจาก OpenCV ด้วยคำสั่่ง cv2.imread() จะมีลักษณะตามปกติเป็น
  • NumPy Array
  • มี 1,2,3,4 channels
  • มีค่าพิกเซลเป็น uint8
  • รูปสีจะเป็น BGR
ในการเข้าถึงแต่ละพิกเซล ถ้าเป็นรูปขาวดำ หรือสีเทา (channel=1) เราทำได้โดย
img[row,column]

ถ้าเป็นรูปสี BGR (channel = 3) เราจะใช้
img[row,column,channel]

ซึ่งแตกต่างกับ array 3 มิติเล็กน้อยที่จะใช้ img[channel,row,column]

ถ้าเราต้องการเฉพาะ channel ใดๆ ก็สามารถแยกออกมาได้ เช่น
blue = img[...,0]            #same as img[:,:,0]
ตัวแปร blue ก็จะอ้างถึง channel สีน้ำเงินในรูป
หากเราเปลี่ยนค่าตัวแปร blue รูปต้นฉบับจะเปลี่ยนตาม

หากไม่ต้องการให้รูปต้นฉบับเปลี่ยน ต้องทำสำเนาไป เช่น
blue = img[...,0].copy()

ลองดูตัวอย่างครับ

import cv2
import numpy as np

img = cv2.imread('lena.jpg')

blue = img[...,0].copy()
green = img[...,1].copy()
red = img[...,2].copy()

cv2.imshow("Blue",blue)
cv2.imshow("Red",red)
cv2.imshow("Green",green)
cv2.waitKey()
cv2.destroyAllWindows()

หากต้องการให้เห็นแต่ละสีในสีนั้นๆ

ก็ปรับโค้ดเล็กน้อยครับ
import cv2
import numpy as np

img = cv2.imread('lena.jpg')

blue = np.zeros_like(img)
blue[...,0] = img[...,0].copy()
green = np.zeros_like(img)
green[...,1] = img[...,1].copy()
red = np.zeros_like(img)
red[...,2] = img[...,2].copy()

cv2.imshow("Blue",blue)
cv2.imshow("Red",red)
cv2.imshow("Green",green)
cv2.waitKey()
cv2.destroyAllWindows()

หรือ
import cv2
import numpy as np

img = cv2.imread('lena.jpg')

blue = img.copy()
blue[...,1:3] = 0
green = img.copy()
green[...,0:3:2] = 0
red = img.copy()
red[...,0:2] = 0

cv2.imshow("Blue",blue)
cv2.imshow("Red",red)
cv2.imshow("Green",green)
cv2.waitKey()
cv2.destroyAllWindows()

OpenCV ก็มีคำสั่งสำเร็จรูปสำหรับการแยก channel คือ คำสั่ง split() ลองดูตัวอย่างการใช้งานครับ
import cv2
import numpy as np

img = cv2.imread("lena.jpg")
out = cv2.split(img)

cv2.imshow("Blue",out[0])
cv2.imshow("Green",out[1])
cv2.imshow("Red",out[2])
cv2.waitKey()
cv2.destroyAllWindows()

ก็จะได้ผลลัพธ์เหมือนตัวอย่างแรกครับ

Brightness และ Contrast

เราลองมาปรับค่า brightness โดยการบวกเพิ่มค่าพิกเซล และค่า contrast โดยการคูณเพิ่มค่าพิกเซล ของรูปสีเทา ให้ได้ผลดังนี้ครับ


เราจะใช้คำสั่ง cv2.add() และ cv2.multiply() ซึ่งคำสั่งทั้งสองจะ clamp หรือ saturate พิกเซล (ถ้าค่าต่ำกว่า 0 ให้เป็น 0 และ ถ้าค่าสูงกว่า 255 ให้เป็น 255) โดยอัตโนมัติครับ

โค้ดประมาณนี้ครับ
import cv2

#read image as gray
img = cv2.imread("lena.jpg",cv2.CV_LOAD_IMAGE_GRAYSCALE)

#increase brightness
bri = cv2.add(img,100)
#increase contrast
con = cv2.multiply(img,1.5)

cv2.imshow("Original",img)
cv2.imshow("Brightness",bri)
cv2.imshow("Contrast",con)

cv2.waitKey()
cv2.destroyAllWindows()

ในกรณีที่เป็นรูปสี ที่มีหลาย channel ผมลองโค้ดเดิมแล้วผลลัพธ์ไม่ถูกต้องครับ มันบวกหรือคูณแค่ channel เดียว เลยพยามเปลี่ยนโค้ดให้เป็นเมตริกซ์บวกหรือคูณกัน (ซึ่งอาจจะมีวิธีที่ดีกว่า) ดังนี้ครับ
import cv2
import numpy as np

img = cv2.imread("lena.jpg")

#brightness
a = 100*np.ones_like(img)
bri = cv2.add(img,a)

#contrast

b = np.zeros_like(img)
con = cv2.scaleAdd(img,1.5,b)   #1.5*img + 0


cv2.imshow("Original",img)
cv2.imshow("Brightness",bri)
cv2.imshow("Contrast",con)

cv2.waitKey()
cv2.destroyAllWindows()

ผลลัพธ์

อย่างไรก็ตาม เราสามารถใช้เทคนิคของ NumPy Array มาทำงานนี้ได้ ดังตัวอย่างต่อไปนี้ครับ ซึ่งใช้ได้กับทั้งรูปสีเทาและสีปกติ
import cv2
import numpy as np

img = cv2.imread("lena.jpg")

#increase brightness
bri = img+100.0
#clip in range 0-255
bri = np.clip(bri,0,255)
#convert to uint8
bri = np.uint8(bri)

#increase contrast
con = img*1.5
con = np.clip(con,0,255)
con = np.uint8(con)

cv2.imshow("Original",img)
cv2.imshow("Brightness",bri)
cv2.imshow("Contrast",con)

cv2.waitKey()
cv2.destroyAllWindows()

สังเกตว่า bri = img+100.0 ตัวเลขที่เอาไปบวกมีทศนิยมด้วย เพื่อเปลี่ยนให้ค่า Array จาก uint8 เป็น float เนื่องจาก ถ้าใช้แค่ bri = img+100 ค่าพิกเซลที่เกิน 255 จะถูกทอนค่าลง เช่น 256 ก็จะกลายเป็น 1 (256%255) ทำให้ได้ค่าที่ไม่ถูกต้องครับ

NumPy Array เปรียบเทียบคำสั่งกับ Matlab

อ้างอิงจาก https://docs.scipy.org/doc/numpy-dev/user/numpy-for-matlab-users.html

Matlab --> Python: NumPy
help func --> help ("func")

ndims(a) --> ndim(a) or a.ndim

size(a) --> shape(a) or a.shape

size(a,n) --> a.shape[n-1]

[ 1 2 3; 4 5 6 ] --> array([[1.,2.,3.], [4.,5.,6.]])

[ 1 2 3; 4 5 6 ] --> vstack(([1,2,3],[4,5,6]))

a(end) --> a[-1]

a.' --> a.T or a.transpose()

a.*b --> a*b

a*b --> dot(a,b)

find(a>0.5) --> nonzero(a>0.5)

y=x --> y=x.copy()

y=x(2,:)--> y=x(2,:)

zeros(3,4)--> zeros((3,4))

ones(3,4)--> ones((3,4))

rand(3,4)--> random.rand(3,4)

repmat(a, m, n) --> tile(a, (m, n))

[a b] --> concatenate((a,b),1) or hstack((a,b)) or column_stack((a,b)) or c_[a,b]

[a; b] --> concatenate((a,b)) or vstack((a,b)) or r_[a,b]

Sunday, February 24, 2013

NumPy Array ตอนที่ 5 การเพิ่มสมาชิกและซ้อน Array

สามารถใช้คำสั่ง append() , concatenate(), vstack(), hstack(), dstack() เช่น

>>> a = np.array([1,2])
>>> b = np.append(a,3)
>>> b
array([1, 2, 3])
>>> b = np.append(a,[3,4])
>>> b
array([1, 2, 3, 4])

เรายังสามารถเลือกที่จะเพิ่มแถว หรือ คอลัมน์ได้ แต่ข้อมูลที่จะเพิ่มต้องเท่ากับจำนวนแถวหรือคอลัมน์นั้น

>>> a = np.array([[1,2],[3,4]])
>>> a
array([[1, 2],
       [3, 4]])

>>> b = np.append(a,[[5,6]],axis=0)
>>> b
array([[1, 2],
       [3, 4],
       [5, 6]])

>>> c = np.append(b,[[0],[0],[0]],axis=1)
>>> c
array([[1, 2, 0],
       [3, 4, 0],
       [5, 6, 0]])

คำสั่ง b = np.append(a,[[5,6]],axis=0) สามารถแทนด้วย
b = np.concatenate((a,[[5,6]])) หรือ
b = np.vstack((a,[[5,6]]))

สำหรับรูป มี แถว คอลัมน์ และ channel ถ้าจะให้ง่ายก็คือ
vstack เพิ่มแถว
hstack เพิ่มคอลัมน์
dstack เพิ่มความลึก

NumPy Array ตอนที่ 4 การเปลี่ยนขนาดของ Array

การเปลี่ยนขนาดของ Array
  • ทำให้เหลือ 1 มิติ
สามารถใช้ฟังก์ชัน ravel() เช่น

>>> a = np.array([[1,2],[3,4]])
>>> a
array([[1, 2],
       [3, 4]])

>>> b = a.ravel()
>>> b
array([1, 2, 3, 4])
  • เปลี่ยนเป็น 2 มิติ ขนาดใดๆ
ใช้คำสั่ง array.shape(row,column) เช่น
>>> b.shape = (2,2)
>>> b
array([[1, 2],
       [3, 4]])
หมายเหตุ ถ้าอยากละมิติใดๆไว้ให้ถูกคำนวณเอง ให้ใส่ค่า -1 เช่น
b.shape = (2,-1)
หรือ ใช้คำสั่ง reshape(row,column) เช่น
c = b.reshape(2,2)
หรือ ใช้คำสั่ง resize(row,column) แต่จะเป็นการเปลี่ยน array นั้นแทน เช่น
b.resize(2,2)

ถ้าต้องการเข้าถึงแต่ละสมาชิกของ array ขนาดใดๆ ในลักษณะของ 1 มิติ สามารถใช้คุณสมบัติ array.flat ได้ เช่น
>>> b
array([[1, 2],
       [3, 4]])
>>> for i in b.flat:
...     print i
...     
1
2
3
4

Friday, February 22, 2013

NumPy Array ตอนที่ 3 Index และการเรียกใช้สมาชิก

Indexing
การเข้าถึงสมาชิกของ Array จะทำผ่าน index โดยจะมีค่าเริ่มต้นเป็น 0 1 2 ไปตามลำดับ และสามารถอ้างอิงจากตัวสุดท้ายมาตัวแรก โดยใช้เลข -1 -2 -3 ไป

import numpy as np 
 
a = np.array([7, 9, 4])

a[0]
a[-1]

ผลลัพธ์
7
4

นอกจากนี้ ยังสามารถใช้เครื่องหมาย : เพื่อระบุช่วงของ index ได้ในรูปแบบ
array[start:end:step] โดยผลลัพธ์จะไม่นับค่า end นะครับ เช่น

a[0:2:1]

ผลลัพธ์ 
[7, 9]

ถ้าใส่แค่ค่า end ค่า start จะเป็น 0 และค่า step จะเป็น 1 เช่น

a[:2]

ผลลัพธ์  
[7, 9]

ถ้าไม่ใส่ค่า end ก็จะทำถึงตัวสุดท้าย เช่น

a[0::2]ผลลัพธ์ 
[7, 4]

นั่นคือ
a[:]
ผลลัพธ์ 
[7, 9, 4]

เราสามารถเปลี่ยนค่าสมาชิกหลายๆตัวพร้อมกันได้ โดยใช้หลักการนี้ เช่น

a[1:3] = 0
จะได้
[7, 0, 0]

และสามารถแสดงผลสมาชิกของ array ด้วยคำสั่ง for และ in

for i in a:
      print i**2

จะได้  
49
0
0

สำหรับ array ตั้งแต่สองมิติเป็นต้นไป index ในแต่ละมิติก็จะแยกกันด้วยเครื่องหมาย , เช่น

b = np.array([ ["00","01"], ["10","11"], ["20","21"] ])

[['00', '01'],
['10', '11'],
['20', '21']]

b.shape
(3, 2)

ถ้าต้องการแถวใดๆ

b[1,:]                          # same as b[1]
['10', '11']

ถ้าต้องการหลักใดๆ

b[:,1]
['01', '11', '21']
กรณีที่เป็น array สามมิติ (ซึ่งก็คือ array ของ array สองมิติ)
c = np.array([ [["000","001"],["010","011"],["020","021"]] , [["100","101"],["110","111"],["120","121"]]])

[ [['000', '001'],
    ['010', '011'],
    ['020', '021']],

   [['100', '101'],
    ['110', '111'],
    ['120', '121']] ]

จะกลายเป็นว่า มิติที่ 3 กลายเป็นหลักแรกสุด ตามมาด้วยแถวและหลัก นั่นคือ
c[0,0,1] จะได้ค่า 001
และ

c[0, :, :]
[['000', '001'],
 ['010', '011'],
 ['020', '021']]

นอกจากนี้แล้ว ยังสามารถใช้เครื่องหมาย ... แทนทุกมิติที่ไม่ได้ระบุไว้ เช่น
c[0,...] ก็จะได้ผลลัพธ์เหมือน c[0,:,:]

Thursday, February 21, 2013

NumPy Array ตอนที่ 2 การสร้างและประมวลผล Array

มาว่ากันต่อกับการสร้างและใช้งาน NumPy Array ที่จะช่วยให้การประมวลผลสะดวกขึ้นอีกมากครับ

เราสามารถสร้าง Array ที่มีสมาชิกต่อเนื่องกันได้ โดยการใช้คำสั่ง arange(start,stop,step,dtype) ครับ เช่น

import numpy as np

a = np.arange(5)
b = np.arange(0,5,1, dtype=np.float64)
c = np.arange(0.5,0.1,-0.1)

print(a)
print(b)
print(c)

ผลลัพธ์
[0, 1, 2, 3, 4]
[ 0.,  1.,  2.,  3.,  4.]
[ 0.5,  0.4,  0.3,  0.2]
สังเกตว่าผลลัพธ์ไม่รวมค่า stop ด้วยนะครับ

หรือจะสร้าง Array โดยกำหนดจำนวนสมาชิกก็ได้ จากคำสั่ง linspace(start,stop,number) เช่น

import numpy as np

a = np.linspace(0, 3, 4)

จะได้ผลลัพธ์เป็น
[ 0.,  1.,  2.,  3.]
สังเกตว่ารวม stop ด้วยครับ

ถ้าเรามี Array อยู่แล้ว และต้องการสร้าง zero หรือ one matrix ที่มีขนาดเท่ากับ Array นั้น สามารถใช้คำสั่ง
zeros_like(array)
ones_like(array)
ได้ เช่น

import numpy as np

a = np.array([[11,12], [21,22]])
b = np.zeros_like(a)
c = np.ones_like(a)

ผลลัพธ์
[[11, 12],
  [21, 22]]

[[0, 0],
 [0, 0]]

[[1, 1],
 [1, 1]]

การประมวลผล Array
เราสามารถ +, -, *, /, **(ยกกำลัง) Array กับตัวเลข หรือ กับ Array ด้วยกันได้ ซึ่งจะเป็นการประมวลผลแบบทีละตำแหน่ง (element-wise) เช่น

import numpy as np

a = np.identity(2)
b = np.ones((2,2))
print(a)
print(b)

a = a*2
b = a+b
print(a)
print(b)

ก็จะได้ผลลัพธ์เป็น
[[ 1.,  0.],
[ 0.,  1.]]

[[ 1.,  1.],
[ 1.,  1.]]

[[ 2.,  0.],
[ 0.,  2.]]

[[ 3.,  1.],
[ 1.,  3.]]


แต่ถ้าต้องการหาผลคูณแบบเมตริกซ์ของ Array จะใช้คำสั่ง dot(matrix1,matrix2) แทน เช่น

import numpy as np

a = np.identity(2)
b = np.ones((2,2))
print(a)
print(b)

c = a*b
d = np.dot(a,b)
print(c)
print(d)

[[ 1.,  0.],
[ 0.,  1.]]

[[ 1.,  1.],
[ 1.,  1.]]
 
[[ 1.,  0.],
[ 0.,  1.]]

[[ 1.,  1.],
[ 1.,  1.]]

การคำนวณผลสรุปจาก Array
สามารถทำได้ด้วยคำสั่ง เช่น
  • array.max()
  • array.min()
  • array.sum()
ตัวอย่าง

import numpy as np

a = np.array([[2,1],[4,3]])

a.max()
a.min()
a.sum()

ก็จะได้ผลลัพธ์เป็น
4
1
10

เราสามารถกำหนดแถวหรือคอลัมน์ที่ต้องการคำนวณโดยใช้ฟังก์ชันข้างต้นได้ ในรูปแบบ
array.max(axis)
เช่น
array.max(axis=0) หาค่าสูงสุดในแต่ละคอลัมน์
array.max(axis=1) หาค่าสูงสุดในแต่ละแถว

ตัวอย่าง

import numpy as np

a = np.array([[2,1],[4,3]])

a
a.max(axis=0)
a.max(axis=1)

ผลลัพธ์

[[2 1]
 [4 3]]

[4 3]       #[max คอลัมน์1 max คอลัมน์2]

[2 4]       #[max แถว1 max แถว2]