Wednesday, December 3, 2014

[OpenCV กับการแสดงผลใน Tkinter] และ [PIL กับการแสดงผลด้วย OpenCV]

ตั้งใจจะเขียนเรื่องนี้นานแล้ว พึ่งจำได้ครับ

ตอนเราประมวลผลรูปด้วย OpenCV มันก็จะมีหน้าต่างแสดงผลมาให้ ด้วยคำสั่ง เช่น imshow

อย่างไรก็ตาม ถ้าเราใช้ Python ทางเลือกหนึ่งในการแสดงผลก็คือ การใช้ Tkinter ซึ่งเป็น UI ที่มีมากับ Python เลย

สิ่งที่ต้องทำคือ
1. รูปแบบการเก็บข้อมูลภาพของ OpenCV ต่างกับแบบอื่น คือเป็น BGR จำเป็นต้องเปลี่ยนให้เป็น RGB ก่อน
2. OpenCV เก็บข้อมูลภาพเป็น NumPy Array การอ่านไปแสดงผลใน Tkinter จำเป็นต้องใช้คำสั่งการอ่านจาก Array แทน

โค้ดก็จะเป็นประมาณนี้ครับ คืออ่านค่าภาพ (และประมวลผล) ด้วย OpenCV จากนั้นสร้าง UI ด้วย Tkinter และแสดงผล
import cv2
from Tkinter import Tk, Label
from PIL import Image, ImageTk

#load image in grayscale, output is numpy array
img = cv2.imread("lena.jpg", cv2.IMREAD_COLOR)

if img==None:
    print "Image not loaded"
else:
    #convert BGR to RGB
    imgRGB = img[:,:,::-1]
    #convert to PIL format
    imgtk = Image.fromarray(imgRGB)
    #create main window
    root = Tk()
    root.title("Image Display")
    #convert to Tkinter format
    imgTk = ImageTk.PhotoImage(imgtk)
    #create a label to store and display image
    lbl = Label(root, image=imgTk)
    #pack a label to fit image
    lbl.pack()
    #lbl.image = imgTk
    root.mainloop()

ผลลัพธ์ที่ได้ก็คือ


ในทางกลับกันถ้าเราต้องการโหลดรูปด้วย PIL แต่ไปแสดงผลแบบ OpenCV กระบวนการก็จะกลับกันคือ
1. แปลงรูปให้เป็น numpy array ก่อน
2. เปลี่ยนจาก RGB ให้เป็น BGR

ลองดูโค้ดตามนี้ครับ
import cv2
from PIL import Image
import numpy as np

#load image by PIL
img = Image.open("lena.jpg")

#convert PIL image to numpy array
img = np.array(img)

#convert RGB to BGR
imgBGR = img[:,:,::-1]

#display in opencv
cv2.namedWindow("Output")
cv2.imshow("Output",imgBGR)
cv2.waitKey()
cv2.destroyAllWindows()

ผลลัพธ์ที่ได้น่าจะประมาณนี้


อ้างอิง
http://stackoverflow.com/questions/384759/pil-and-numpy

Tuesday, December 2, 2014

ลองติดตั้ง VirtualBox บน LXLE

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

อยากลองติดตั้ง VirtualBox แล้วรัน Android Emulator ดู ถ้าเซ็ต memory ต่ำๆ ซัก 216 MB น่าจะไหวอยู่

ว่าแล้วก็ติดตั้งเลย เลือก Software Center ของ lubuntu ติดตั้ง VirtualBox เวอร์ชันที่มีให้ก็ประมาณ 4.1 ทำนองนี้

ติดตั้งเสร็จ เพิ่มตัว emu (ในที่ใช้ OVA ของ AndroVM) รันไม่ได้ซะงั้น มีข้อความผิดพลาดบอกว่า

Kernel driver not installed (rc=-1908)

The VirtualBox Linux kernel driver (vboxdrv) is either not loaded or there is a permission problem with /dev/vboxdrv. Please reinstall the kernel module by executing

'/etc/init.d/vboxdrv setup'

as root. If it is available in your distribution, you should install the DKMS package first. This package keeps track of Linux kernel changes and recompiles the vboxdrv kernel module if necessary.


แย่ละ ไปต่อไม่ถูกเลย

ไปลองค้นหาดู หลายๆที่บอกว่าเพราะไม่มีพวก kernel drivers + build tools เลยลองทำตามเค้า เช่นที่เว็บ http://www.binarytides.com/fix-vbox-kernel-driver-error/

ก็ยังไม่สำเร็จในขั้นสุดท้าย

ไปหาดูอีก บางคนบอกว่านอกจากไม่มีพวกไฟล์ข้างต้นแล้ว เวอร์ชันของ VirtualBox ใน Software Center มันเก่าไป ไม่สนับสนุนกัน

สุดท้าย วิธีที่มั่วแล้วใช้ได้คือ
1. ติดตั้ง kernel drivers + build tools ก่อน โดยใช้คำสั่ง

$ sudo apt-get install build-essential module-assistant 
$ sudo m-a prepare

2. ดาวน์โหลด VirtualBox มาใหม่จาก https://www.virtualbox.org/wiki/Linux_Downloads เลือกเวอร์ชันให้ตรงกับ OS ของเรา อย่างเช่น ผมใช้ LXLE 12.04 ที่มีพื้นฐานมาจาก Ubuntu 12.04 LTS ก็โหลดเวอร์ชันนี้

3. ติดตั้งโดยดับเบิลคลิกทีไฟล์ที่โหลดมาเลย มันจะเรียก GDebi Package Installer มาติดตั้งไฟล์ .deb ที่โหลดมา รวมถึงคอมไพล์ไฟล์ที่เกี่ยวข้องให้ใหม่เอง

เย้ ใช้การได้แล้วครับ


Sunday, November 30, 2014

ลองเล่น Linux LXLE

มีคอมเก่าอยู่ครับ อายุประมาณหกเจ็ดปี อยากลองเอามาเล่นใหม่ โดยกะว่าจะใช้งานทั่วไป ใช้พิมพ์งาน Word คำนวณ Excel เล่นเน็ต ดูหนัง ฟังเพลง เขียนโปรแกรม JAVA ฯลฯ เริ่มจะเยอะ ยกเว้นเล่นเกมส์

สเปกที่มีอยู่ก็ประมาณ
CPU Centrino Duo
RAM 1 GB
HDD 100 GB
USB 2.0 สามารถกำหนด BIOS ให้บูตผ่าน USB ได้ (พึ่งรู้เหมือนกัน)
CD/DVD drive
แบตเสื่อม แต่เสียบสายใช้งานได้อยู่
OS เดิม Windows XP

ของเดิมก็ใช้งานได้อยู่ ติดแค่ว่ามันหน่วงบ้าง เพราะลง Firewall + Antivirus และเกรงว่า XP กำลังจะเป็นเป้าหมายด้านความปลอดภัย

เลยเลือกหา OS ที่จะลงใหม่โดยอยากได้ของฟรี ทำงานได้เร็วๆ ไม่หน่วงมาก แต่ไม่ถึงขั้นกับ UI ดูไม่ได้ พอเข้าไปดูใน distrowatch.com ก็เห็นว่าตามสถิติ Page Hits ที่ผ่านมา 6 เดือน เจ้า Linux Mint ยังคงเป็นผู้นำ ตามมาด้วย Ubuntu แต่ความนิยมไม่ใช่วัตถุประสงค์ของเรานี่นา เราต้องการใช้เครื่องเก่า งั้นก็ไปค้นหาดีกว่าโดยใช้ keyword คือประเภท Old computers ผลลัพธ์ก็คือ

LXLE (อะไรอ่ะ) ได้รับความนิยมมาอันดับ 1 และในเว็บตัวเองคือ www.lxle.net บรรยายตัวเองว่า Revive that old PC! อืม น่าสนใจว่าโม้หรือเปล่า เอามาลองสักหน่อย

ว่าแล้วก็ไปโหลดทันใด ซึ่งบังคับให้โหลดแบบ torrent เท่านั้น (ไม่เข้าใจ เว็บที่ให้เก็บไฟล์ฟรีมีเยอะแยะ) ซึ่งเค้าก็มีเหตุผลของเค้าอยู่แต่ขี้เกียจอ่าน

โหลดมาเสร็จได้ไฟล์ iso ก็จะติดตั้งผ่าน USB drive ขั้นตอนแรกเลย ก็ต้อง format มันใหม่ให้เป็น FAT32 จากนั้นก็ไปโหลดตัวช่วยคือ http://unetbootin.sourceforge.net/ เพื่อคัดลอกไฟล์จาก iso ที่โหลดมาไปยัง USB drive และทำให้มันบูตได้

เมื่อเตรียมการเสร็จ ก็เสียบ USB drive เข้ากับคอมเก่า แล้วเปิดเครื่อง มีเมนูให้เลือกติดตั้ง แล้วก็กำหนดค่าเล็กน้อย ใช้เวลาไม่เกินครึ่งชั่วโมงก็เสร็จ

จากนั้นก็เป็นการลองใช้งาน มีข้อสังเกตเบื้องต้นดังนี้
ข้อดี
-ติดตั้งง่าย หาอุปกรณ์ที่มีอยู่ในเครื่องเช่น Network card และ sound ได้เอง ไม่ต้องลง drivers เพิ่ม
-บูตใช้เวลาไม่ถึง 1 นาทีก็พร้อมใช้งาน
-สวย (เกี่ยวไหมนี่) UI ดี มีหลายโหมดการใช้งานให้เลือกว่าจะเป็นแบบ Windows หรือ Mac ทำนองนี้ มี Wallpaper สวยๆมาให้เป็นร้อยแบบ (มีรูปเมืองไทยด้วย)
-มีตัวคอยบอกการใช้งานทรัพยากรให้เห็น ซึ่งถ้าข้อมูลถูกต้อง ตั้งแต่ใช้งานมาวันกว่าๆ นี้ ยังไม่เคยเห็น CPU หรือ RAM ของระบบถูกใช้ถึง 50% เลย (เพราะเล่นแต่เน็ต กับพิมพ์งาน 555) แต่แทบไม่เห็นการกระตุกหรือช้าจากการใช้งานทั่วไป (ไม่นับเน็ตช้านะครับ)
-มีซอฟต์แวร์ที่จำเป็นมาให้พร้อม เล่นเน็ตมี Firefox ใช้งานเอกสารมี Libre Office ฯลฯ แทบจะไม่ต้องหาลงเอง ถ้าจะลงเองก็มี Software Center ของ lubuntu ให้

ข้อสังเกต (อาจรวมข้อด้อยด้วยมั้ง)
-มีซอฟต์แวร์เกินความจำเป็นแถมมาให้มากไป เช่น เกมส์
-หาไอคอนระบุภาษาไม่เจอ ตอนนี้ก็ยังหาวิธีให้แสดงผลภาษาปัจจุบันไม่ได้ 
-switch ภาษาอังกฤษไทยลำบาก ตอนแรกเซ็ตให้ใช้ Grave ได้ ตอนนี้ทำไม่ได้เฉยเลย สงสัยเรายังไม่เข้าใจพอ เลยต้องใช้ Alt+Left Shift ไปก่อน
-หาได้แล้วครับ แต่ยุ่งยากสักหน่อย อันดับแรกต้องไปที่ System Tools/Synaptic Package Manager แล้วค้นหา iBus จากนั้นติดตั้ง ibus-m17n เพิ่มเติม แล้วไปที่ Preferences/Input Method Switcher แล้วเลือก iBus จากนั้นรีสตาร์ทเครื่อง แล้วไปที่ Preferences/Keyboard Input Methods มันจะขึ้น iBus Preferences ให้เราเลือก Input Method แล้วเลือกภาษาตามรูป


จากนั้น ในหน้า General ให้เลือกดังนี้ สังเกตว่าในส่วนของ Next input method เราจะสามารถกำหนดได้ว่าจะใช้แป้นอะไรในการเลือกภาษาครับ


ก็น่าจะเกิดไอคอนภาษา ด้านขวาล่างของจอครับ
แต่อย่างไรก็ตาม มันก็ยังดูขัดๆอยู่เวลาพิมพ์ เพราะมันจะมีเส้นใต้ขีดเป็นแนวในการพิมพ์ให้ รู้สึกแปลกๆครับ และหากใช้ตัว Grave เป็นตัวเปลี่ยนภาษา มันก็จะติดตัว ` หรือ _ มาด้วย ต้องเลือกตัวอื่นที่กดแล้วไม่เป็นตัวอักษรแทน เช่น Window Key

-ปรับความสว่างหน้าจอไม่ได้ มีซอฟต์แวร์ให้ปรับ แต่ทำแล้วก็ไม่เห็นการเปลี่ยนแปลง ปรับได้นะครับ อยู่ที่ Preferences/Brightness
-ตัว Software Center เหมือนขาดบางอย่างไป เช่น OpendJDK-jdk ก็ไม่มี แล้วจะเขียน JAVA ยังไง ต้องไปติดตั้งผ่าน terminal เอง

ถ้าเจออะไรจะมาเขียนเพิ่มนะครับ

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

ปิดท้ายด้วยรูปหน้าจอครับ


Thursday, November 20, 2014

jQuery Mobile กับปัญหาการคลิกเอง (Firing twice / Firing multiple times)

เขียน jQuery Mobile กับ Cordova อยู่ เจอปัญหาปุ่มถูกคลิกเอง คลิกหลายๆครั้งด้วย ทำให้กระโดดไปหน้าอื่นทั้งๆที่ยังไม่ได้คลิก

ไปค้นดูครับ คนเจอปัญหาเยอะแยะ เช่น
http://stackoverflow.com/questions/14411532/all-jquery-mobile-events-firing-twice
http://stackoverflow.com/questions/10794181/jquery-mobile-tap-event-triggered-for-twice

และมีหลายวิธีแก้เหลือเกิน เช่น
1. เปลี่ยน event สำหรับการแตะ จาก tap เป็น click คือให้ใช้
$("#btt").off().on("click",function(){
 //do something
})

2. ใช้ off ปิด event ก่อนหน้า แล้วตามด้วย on เช่น
$("#btt").off().on("tap",function(){
      //off then on
});

หรือ
$("#btt").off("tap").on("tap",function(){
      //off then on
});

3. ย้าย script ไปไว้ระหว่างแท็ก head ไม่ใช่ส่วน body

4. เพิ่ม event.preventDefault(); เช่น
$("#btt").on("tap",function(event){
    event.preventDefault();
    //other codes
});

5. วางลำดับการอ้างถึง JavaScript และแทรก script ต่อไปนี้ลงไป
<link rel="stylesheet" href="css/jquery.mobile-1.4.4.min.css" />
<script src="js/jquery-2.1.1.min.js"></script>
<script>
 $(document).on("mobileinit", function() {
  $.mobile.ajaxEnabled = false;
  $.mobile.pushStateEnabled = false;
  $.mobile.linkBindingEnabled = false;
  $.mobile.hashListeningEnabled = false;
  $.mobile.defaultTransition = 'none';
 });   
</script>
<script src="js/jquery.mobile-1.4.4.min.js"></script>  
<script src="js/index.js"></script>
<script src="cordova.js"></script>
<script src="cordova_plugins.js"></script>

และอีกมากมาย

ส่วนตัวผมเองเจอปัญหากับ tap แล้วแค่พอเปลี่ยนเป็น click ก็หาย

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

$(document).on("pagecreate","#pageID",function(){
    //begin code here
});

ส่วนตัวแปรที่ต้องการอัพเดททุกครั้ง เช่น เพื่อการแสดงผล ให้เอาไว้ใน

$(document).on("pagebeforeshow","#pageID",function(){
    //begin code here
});

ไม่ควรเอา event สำหรับปุ่มไว้ใน page event: pagebeforeshow เพราะทุกครั้งที่กลับมาที่หน้านี้ ผมเข้าใจว่าปุ่มมันเหมือนจะถูกกำหนด event สองครั้งซ้อน เมื่อเรากดปุ่มก็เลยเหมือนกดสองครั้ง

ถ้าใครเจอปัญหานี้ ลองวิธีแก้ต่างๆข้างต้นดูครับ

jQuery Mobile กับ Apache Cordova จะวางลำดับกันอย่างไร

เคยสงสัยเหมือนผมไหมครับว่าถ้าเราใช้ jQuery Mobile กับ Apache Cordova จะวางลำดับการเริ่มโปรแกรมอย่างไร เพราะ

jQuery Mobile ให้เริ่มด้วย
$(document).on("pagecreate","#pageID",function(){
    //begin code here
});

แต่ Cordova บอกให้เริ่มด้วย
document.addEventListener("deviceready", onDeviceReady, false);

function onDeviceReady(){
    //begin code here
}

เชื่อใครดีล่ะ

ในเน็ตก็มีคนสอบถามกันเยอะครับ เหมือนว่าคำตอบที่เป็นที่ยอมรับอยู่ในนี้ http://stackoverflow.com/questions/10945643/correct-way-of-using-jquery-mobile-phonegap-together

ถ้าเอาแบบลูกทุ่ง ส่วนตัวผมคิดว่ามีหลักของตัวเองคือ
1. หน้าไหนไม่มีการใช้คุณสมบัติของ Cordova เช่น ไม่ได้ใช้ Cordova events, plugin ก็ไม่ต้อง include ไฟล์ cordova.js, cordova_plugin.js ใช้แบบ jQuery Mobile เลย คือใช้การเริ่มแบบ jQuery

2. หน้าที่ใช้คุณสมบัติของ Cordova ให้  include ไฟล์ cordova.js, cordova_plugin.js และเริ่มแบบนี้
$(document).on("pagecreate","#pageID",function(){
    document.addEventListener("deviceready", onDeviceReady, false);
});

function onDeviceReady(){
    //begin code here
}

เป็นการลองแบบคาดการณ์ดูนะครับ ไม่รู้ว่าจะมีปัญหาอะไรที่ยังไม่ทราบไหม