อธิบายการพัฒนาซอฟแวร์แบบส่งมอบของก่อน ด้วยตัวอย่าง (Delivery-first by Example)

สวัสดีครับ

บล็อคนี้ผมเขียนต่อจากบล็อคที่แล้ว การพัฒนาซอฟแวร์แบบส่งมอบของก่อน (Delivery-first)

การพัฒนาซอฟแวร์แบบส่งมอบของก่อน แบ่งช่วงขั้นตอน (Phase) การทำงานออกเป็น 3 ช่วง คือ Delivery, Detect และ Defect หรือ 3D

ช่วง Delivery เป้าหมายเพื่อส่งมอบของ (Deploy) โดยการส่งมอบนี้ไม่จำกัด อาจจะเป็นเพื่อส่งให้ลูกค้าใช้เลย (Production) หรือ ส่งเพื่อให้ลูกค้าทดสอบ (Acceptance / Functional test) หรือ ส่งมอบเพื่อทดสอบร่วมกันกับทีมพัฒนา (Integration test) หรือ ทดสอบเฉพาะส่วนเดี่ยวๆก็ได้ (Unit-test) ผมรวมเรียกว่าการส่งมอบ คือ Delivery หมายถึง ส่งมอบของที่ใช้ประโยชน์ได้ ทำงานได้จริง มีผลลัพธ์ออกมา ไม่ใช่แค่ code หรือ ไอเดีย แนวคิด การออกแบบ หรือ เอกสาร ต่างๆ

ช่วง Detect เป้าหมายเพื่อ ตรวจจับ ให้ได้ค่าที่ประเมิณผล (Measure) ได้จริง หรือก็คือการใช้ Fitness function เพื่อตรวจสอบว่าผลลัพธ์ทั้งหมดเราที่ประเมิณผลได้จากซอฟแวร์ที่ส่งมอบไปนั้นบรรลุเป้าหมายของเราหรือไม่ ผลสรุปจาก detect นี้ทำให้ทีมพัฒนา หรือ ผู้ใช้ และผู้ที่เกี่ยวข้อง เห็นชัดเจนว่าผลลัพธ์นี้ถูกต้อง ถูกใจเป็นไปตามความต้องการหรือไม่ ซึ่งก็คือรายการ defect เพื่อใช้ดำเนินการในขั้นตอน Defect ต่อไป

ช่วง Defect เป้าหมายเพื่อพิจารณาตัดสินใจ defect ต่างๆจากที่เรา detect ได้ว่า จะแก้ไข ปรับปรุงของ หรือ ซอฟแวร์ที่เราส่งมอบไปแล้วนั้นต่อไป หรือไม่ แล้ววนกลับไปสู่ช่วง Delivery ต่อไป ขั้นนี้ก็คือ  Continuous improvement ซึ่งเป็นกระบวนการบังคับในรูปแบบการพัฒนาซอฟแวร์เชิงวิวัฒนาการนั่นเอง

ตัวอย่าง ผมจะเป็นงานบัญชีสร้างรายงานสมุดรายวันทั่วไป ผลลัพย์แสดงเป็น html ง่ายๆครับ ขั้นตอนพัฒนาตามนี้เลยครับ

ต้องการ Visual studio 2017 community edit ครับ Download และติดตั้งฟรีได้ที่ VS2017

Delivery 

  1. ผลลัพธ์เป็นรายงาน HTML ครับ ชื่อ file report.html หน้าตาตามที่ออกมาเป็นแบบนี้
  2. สร้าง projects ภาษา C# ชื่อว่า Accounting.Core เป็น library สำหรับงานบันทึกธุรกรรมทางบัญชี 
  3. ใน projects ผมได้สร้าง class 3 class และ 2 enumeration type เพื่อใช้แทนงานทางบัญชี ซึ่งตอนแรก ก็ไม่ใช่ 3 class นี้ แต่ไม่เป็นไร ให้ทำไปก่อนครับ ตามที่เราเข้าใจความต้องการในตอนนั้น ไปได้เลย 3 class มีดังนี้
    1. class Account : สมุดบัญชีบันทึกรายวันทั่วไป
    2. class AccountEntry : รายการบัญชีแบบสองด้าน
    3. class AccountTransaction : ธุรกรรมธุรกิจ เพื่อใช้ระบุได้ว่า แต่ละรายการที่เปลี่ยนแปลงไปนั้นเป็นชุดธุรกรรมเดียวกัน
    4. enum AccountEntryType : ชนิดของการลงบันทึกบัญชีสองด้าน มีสองค่าคือ Debit และ Credit
    5. enum AccountType : ประเภทของรายการบัญชี แต่ละประเภทแบ่งเป็น 3 กลุ่มได้แก่ สินทรัพย์ (Assets), หนีสิน (Liabilities) และ ส่วนของเจ้าของ (Shareholders’ Equity)
  4. code ส่วนของของที่พร้อมส่งมอบแล้วอยู่ที่ Accounting.Core
  5. หลังจาก build เสร็จจะได้ของเป็น library file Accounting.Core.dll พร้อมส่งมอบให้ผู้ใช้ใช้งานได้ โดยผมจะสร้าง projects เพื่อแทนการใช้งาน library นี้อีกหนึ่ง project ชื่อว่า Accounting.Core.Client โดยเป็นแบบ MSTest ครับ คือมันเป็น xUnit สำหรับ run ใช้ของ Accounting.Core.dll ของผม แทนผู้ใช้เป็นตัวอย่างไปก่อน
  6. ใน project Accounting.Core.Client เพิ่ม Test class Delivery เพื่อใช้แสดงการใช้ของ คือ Accounting.Core.dll ครับ โดยการ add reference เข้ามา แล้วเพิ่ม Test method เพื่อดำเนินการเพิ่มธุรกรรมทางธุรกิจเพื่อให้มีรายการบันทึกบัญชี ไว้ออก report ได้ มีรายการธุรกกรรมทั้งหมด 4 รายการ คือ
    1. ธุรกรรมการค้า รหัส 001 – ขายสินค้าให้นาย ก. 1 ชิ้น รับเงินสด 1000 บาท
    2. ธุรกรรมการค้า รหัส 002 – จ่ายสินค้าคงเหลือเพื่อขาย นาย ก. 1 ชิ้น 800 บาท
    3. ธุรกรรมการค้า รหัส 003 – ขายสินค้าให้นาย ข. 2 ชิ้น รับเงินโอน PromptPay 2000 บาท
    4. ธุรกรรมการค้า รหัส 004 – จ่ายสินค้าขายนาย ข. 2 ชิ้น 1600 บาท
    5. หลังจากบันทึกรายการธุรกรรมการค้าเสร็จ ผมสั่ง build ได้ report.html ออกมาไปวางที่ path results ครับ
  7. Code client ทั้งหมด ดูได้ที่ Accounting.Core.Client.Delivery

หลังจากที่มีการใช้งานของแล้ว ขั้นตอนต่อมาก็คือการ Detect ครับ

Detect

  1. เพื่อไม่ให้เสียเวลา ผมใช้ Project client เพื่อเขียน script ในการ detect ผลลัพธ์จากการใช้งานแล้วครับ เพิ่ม Test class Detect เขาไป
  2. เนื่องจาก file ที่เราต้อง Detect นั้นเป็น html ผมขอติดตั้ง lib ตัวช่วยเข้าไปหน่อยคือ HtmlAgilityPack ครับลงผ่าน nuget ได้เลยครับ
    1. PM> install-package HtmlAgilityPack
  3. เพิ่ม Test method watch_sell_goods_and_build_generalJournal_report  เข้าไปเพื่อช่วย run script Detect ผลลัพย์จาก Client ครับ ชือว่า ดู code ได้ที่ Accounting.Core.Client.Detect
  4. หลังจากนั้นให้ run Test method โดยการ click mouse ขวา แล้วเลือก Run test ครับจะเห็นผลลัพธ์ที่ detect ได้เลย เพราะผมใช้ MSTest มันจะแสดง report ออกมาว่าผ่าน ไม่ผ่านตามที่เราเขียนไว้ 
  5. ดูผลลัพย์ detect จากหน้าต่าง Test Explorer ด้านซ้ายได้เลยครับ 

จะเห็นว่าทดสอบผ่านหมดครับ เพราะขึ้นอยู่กับเราจะเขียนให้ code detect ผลลัพย์ให้เป็นอย่างที่คาดหวังขนาดไหน ตัวอย่างนี้ก็เขียนให้ง่ายไว้ก่อน แค่เช็คว่ามี file report.html หรือไม่ และ ตรวจสอบยอดบันทึกด้าน Debit และ Credit ว่าเท่ากันรึเปล่า แค่นั้น

สำหรับขั้นตอน Defect ก็รวมอยู่ใน Detect แล้วครับ มันก็คือเราดูผลที่ Detect ได้จากนั้นเราก็ตัดสินใจว่าจะ แก้ไขซอฟแวร์ของเราหรือไม่ ขั้นตอนนี้เรียกว่า Defect

หวังว่านักพัฒนาจะเข้าใจรูปแบบการแก้ปัญหาแบบ การพัฒนาซอฟแวร์แบบส่งมอบของก่อน หรือ Delivery-first ได้พอเข้าใจบ้าง จริงๆแล้วมันมาจากการเรียนรู้ที่เราไม่เข้าใจว่าจะทดสอบอย่างไร แต่เรารู้แล้วว่าจะแก้ปัญหาอย่างไรก่อน แล้วจึงมาตรวจสอบให้ได้ว่า ผลลัพย์จากการแก้ปัญหาของเรานั้นมันถูกต้อง เป็นที่น่าพอใจรึยัง แล้วก็กลับไปแก้ไขวิธีการแก้ปัญหาของเราให้ได้ผลลัพย์ออกมาจนกระทั่งเราพอแล้ว แค่นั้นครับ

ปัญหาที่ผมเจอคือ บางครั้งเราก็หาคำตอบได้มาทดสอบ ใช้เขียนทดสอบได้ตั้งแต่ต้น เรียกว่า Test-Driven Development (TDD) แต่บางงานเรายังไม่รู้เลยว่าคำตอบควรจะเป็นอย่างไร แต่เรารู้แล้วว่าผลลัพธ์มันจะออกมาแบบนี้ แล้วผมก็จึงสืบค้นหาคำตอบว่าผลลัพย์มันต้องออกมาเป็นแบบไหน จึงได้เกิด Delivery-first แล้วผมก็เรียกมันใหม่ว่า Delivery-driven Development (DdD) ต่อจากนี้ไปครับ

ขอบคุณครับ

#:P

Advertisements

การพัฒนาซอฟแวร์แบบส่งมอบของก่อน (Delivery-first)

สวัสดีครับ

บล็อคนี้ผมจะมาแชร์ เทคนิคการพัฒนาซอฟแวร์ ที่ผมทำแล้วง่ายกว่า เร็วกว่า และได้คุณภาพเหมือนกับ Test-Driven-Development (TDD) ที่เราๆนิยมใช้กัน

เทคนิคนี้ผมได้ใช้มาตลอดนานแล้ว หลังจากที่ผมเรียนรู้เทคนิค TDD ว่ามันแก้ปัญหาการพัฒนาซอฟแวร์ได้ดี ทุกปัญหาผมแก้ได้โดยง่ายด้วยเทคนิค TDD และผมก็เขียนไว้ในบล็อคผมเองก็หลายที่

จนมาวันหนึ่งผมไม่ได้เขียน Test ก่อนเลย พร้อมทั้งได้งานที่ Refactor code ของระบบเก่าหลายโปรเจ็ค ผมก็ได้ใช้เทคนิคนี้มาตลอด แต่ผมก็ไม่ได้เรียกมันสักที แต่มันไม่ใช่ TDD แน่นอน แต่ก็แนะนำให้นักพัฒนาใช้ TDD ก่อน ก่อนที่จะเข้าใจว่าไม่ต้องเขียน test ก่อนพัฒนาเป็นอย่างไร จนได้เกิดเทคนิคนี้ขึ้น

เทคนิคนี้คือว่าไม่ได้เขียน test ก่อน แต่งานที่ผมทำก็ออกมามีคุณภาพ และรวดเร็ว เหมือนกับ TDD ครับ ทั้งงานสร้างใหม่ และงาน refactor ของเดิม

ผมจึงตั้งชื่อเทคนิคนี้เองว่า Delivery-first หรือ ส่งมอบของก่อน ของในที่นี้ เป็นซอฟแวร์

เทคนิค ส่งมอบของก่อน ขั้นตอนก็แสนง่ายครับ มี แค่ 3 ขั้นตอน คือ Delivery-Detect-Defect หรือเรียกสั้นๆว่า 3D ครับ ไม่ว่าคุณจะมีของอยู่แล้ว หรือ สร้างของขึ้นมาใหม่ ใช้เทคนิคนี้ได้หมดครับ ต่างจาก TDD คือ ไม่ต้องเขียน test ก่อน

ขั้นตอนแรก Delivery คือ ลงมือสร้างของนั้นขึ้นมาเลยครับ ถ้าพร้อมส่งมอบก็ส่งได้เลย ถ้าไม่พร้อม ก็ deploy ขึ้นทดสอบก่อน ไม่ต้องเขียน test เขียน code ขึ้นมาก่อน build แล้ว delivery เลย หรือถ้ามีของอยู่แล้วก็ข้ามไปขั้นที่สองครับ

ขั้นตอนที่สอง Detect คือ ให้เขียน code  เพื่อตรวจจับผลลัพธ์ของสำเร็จที่เราส่งมอบไปจากขั้นแรก แล้วเอาแสดงใหเได้ หรือถ้ามีข้อมูลมาเปรียบเทียบผลลัพธ์ที่เราคาดว่ามันต้องเป็น ก็เอามาสรุปรวมกันไปครับ

และ

ขั้นตอนที่สาม Defect คือ ดูจากผลสรุปที่ได้จาก ขั้นตอน Detect แล้วผลลัพธ์ได้ตามคาดหวัง หรือไม่ได้ หรือว่าเราพอใจ ไม่พอใจแล้ว ก็ให้เรากลับไปแก้ไขการสร้างของ ของเราให้ถูกต้องตรงใจได้เลยครับคือ ย้อนกลับไปที่ขั้นตอน Delivery

ตัวอย่าง-1: ซอฟแวร์ feature ง่ายๆอย่างการ login

  1. Delivery – ผมมีหน้า login อยู่แล้ว ผมก็เข้าไปเล่นได้เลยโดย login username/password ปกติ ถ้าไม่มีก็สร้างมันขึ้นมา แล้ว deploy ให้มีหน้าจอสำหรับ login ใส่ username/password ขึ้นมาเลย ผลลัพธ์คือ ผม login ผ่านเข้าไปเจอหน้าแรกได้
  2. Detect – ผมเขียน automate script สำหรับอ่านข้อมูลการ login ของผมขึ้นมา แล้วดูว่า login ได้หรือไม่ แล้วแสดงเป็นรายงานออกมาให้เห็นได้
  3. Defect- ผมก็ตรวจดูผลลัพธ์จากรายงาน Detect ถ้าได้ผลว่าผม login อยู่ ก็แสดงว่าการส่งมอบของนั้นผ่านครับ

ตัวอย่าง-2: สร้างโครงการใหม่เลยครับคือ บันทึกข้อมูลสินค้าสักอย่างหนึ่ง

  1. Delivery – ผมสร้างหน้าจอบันทึกสินค้าที่สามารถบันทึกสินค้าได้จริงๆ แล้ว deploy สำหรับทดสอบก่อนเลยครับ อย่าเขียน test นะครับ
  2. Detect – ผมเขียน automate script เพื่ออ่านข้อมูลที่บันทึกลงไป แล้วสรุปแสดงผลออกมา ให้เห็นอ่านเข้าใจว่ามีข้อมูลที่ บันทึกลงระบบได้จริงๆ
  3. Defect – ก็ตรวจดูผลลัพธ์จาก Detect ครับว่าถูกต้องมั้ย ถ้าเราอยู่ในขั้นตอน test เราก็มีผลที่คาดหวังสรุปออกมาที่ Detect ได้เลยครับ แล้วก็ดำเนินการแก้ไขกลับไปขั้นตอน Delivery ต่อไป

ขอนำเสนอแค่นี้ก่อนครับ ผมจะเขียน code ให้ดูต่อไป ผมใช้เทคนิคนี้แล้วมันง่ายมาก ไม่ต้องมากังวลว่าของจะไม่เสร็จ และไม่มีคุณภาพครับ เพราะมันเสร็จตั้งแต่ต้น รวมถึงมี Detect เพื่อตรวจดูแล้วแจ้ง Defect กลับมาให้เรากลับไปแก้ไขของที่ Delivery ไปแล้วอยู่ตลอดเวลาเลยครับ

* เพิ่มเติมอ่านคำอธิบาย Delivery-first ด้วยตัวอย่าง ได้ที่ อธิบายการพัฒนาซอฟแวร์แบบส่งมอบของก่อน ด้วยตัวอย่าง (Delivery-first by Example)

ขอบคุณครับ

#:P

 

#agile, #tdd

งาน, กำลัง, ความซับซ้อน และ Story points เพื่อการประมาณ และ วางแผนงาน แบบ Agile

สวัสดีครับ

วันนี้ผมจะมาอธิบายการประมาณค่า เพื่อ วางแผนงานแบบ Agile ที่เรานิยมใช้กัน ก็คือ Story points (SP)

SP ก็คือ ขนาดของงาน หรือ ความต้องการ มันก็คือขนาดของซอฟแวร์ที่เรากำลังพัฒนา

เราใช้ SP เพื่อไปวางแผนปล่อยซอฟแวร์, คำนวนต้นทุนค่าแรงพัฒนาซอฟแวร์ และ ติดตามงานเพื่อให้อยู่ในแผนงานที่เรากำลังทำอยู่

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

ส่วนความสามารถของคน หรือ ทีมที่จะทำงานนี้ให้เสร็จนั้น เรานิยมวัดด้วยขนาดของเวลาที่ใช้ทำงานนั้นจนเสร็จ เรียกว่า ความพยายาม (effort)

ในการอธิบายเรื่อง SP นี้ที่เราพบเจอกันบ่อย เขามักจะอธิบายว่า มันคือค่า ความพยายาม กับ ความซับซ้อนของงาน ซึ่งกำหนดแบบนี้ก็ไม่ผิด เดี๋ยวผมจะอธิบายต่อไปว่า SP, ความพยายาม และ ความซับซ้อนของงาน มันแตกต่างกันอย่างไร แล้วความแตกต่างกันตรงนี้ทำให้ SP นั้นเกิดความผิดพลาด (error) ในการวางแผนงานกันในทีมได้อย่างไร

ทำไม SP ถึงถูกอธิบายเป็นแบบนั้น ผมขออธิบายด้วย สมการงาน ครับ สมการงาน กำหนดได้แบบนี้

Work(W) = Force(F) x Distance(d)

Work ก็คือ งาน ซึ่งเทียบได้กับ ความต้องการ หรือ คุณสมบัติของซอฟแวร์ที่ต้องการ (feature) หรือ issue

Force ก็คือ กำลัง ซึ่งเทียบได้กับ ความพยายาม ที่คนในทีมใช้ทำงานนั้นจนสำเร็จ และ

Distance ก็คือ ระยะทาง ซึ่งเทียบได้กับขนาดของงาน หรือ SP

ตัวอย่าง เมื่อเราประมาณงานหนึ่งชิ้น (Work item) สมมิว่า คือ w1

เรามีทีมพัฒนาที่มีสมาชิกอยู่ 2 คน คือ p1 และ p2 ให้ทั้งสองคนประมาณงาน w1 นี้

p1 ประมาณ ใช้เวลา 1 วัน และ p2 ประมาณ ใช้เวลา 2 วัน

ถ้าเราใช้ กำลังเป็นหน่วยวัดของงานแล้ว เราจะพบว่า งานแบบเดียวกันขนาดมันไม่มีทางเท่ากันได้เลย เป็นเพราะอะไร

แทนค่าประมาณงานของทั้งสองคนลงไป ในสมการงาน เราจะได้

w1 = F * 1 — สำหรับ p1

w1 = F * 2 — สำหรับ p2

เราจะพบว่า การทำให้ w1 เหมือนกันให้ได้นั้น p1 ต้องออกแรง (F) มากกว่า p2 เป็น 2 เท่า แต่การที่เราเอาเวลา มาเป็นตัววัดขนาดงาน ให้แต่ละคนในทีมเห็นเหมือนกันนั้นเป็นไปไม่ได้เลย ดังนั้น จึงไม่แปลกว่าทำไมการจัดการวางแผนมีความผิดพลาด (error) อยู่เสมอ เช่น ปัญหาในการวางแผนงานโดยประมาณแบบผู้เชี่ยวชาญ ว่างานชิ้นนี้ คนนี้ใช้ 1 วันทำเสร็จ ดังนั้น งานชิ้นนี้ให้ใครทำก็จะต้อง 1 วันเสร็จเหมือนกัน ซึ่งเราจะเห็นว่า มันเป็นไปไม่ได้ในทุกกรณี หรือ ความเข้าใจที่ว่า งานชิ้นนี้คนเดียวทำ 1 วัน ถ้าเราใช้ 2 คนก็ต้องเสร็จได้ภายในครึ่งวันแน่นอน ความเข้าใจทั้งหมดนี้เกิดมากจาก การใช้ Effort กับ Complexity ประมาณขนาดของงาน ซึ่งทั้งสองค่านี้มีความสัมพันธ์กับทีมพัฒนาแต่ละคน ผมก็จะอธิบายในลำดับต่อไป

ดังนั้การจัดการแบบ Agile จึงเลือกใช้ SP มาเป็นตัวประมาณค่าขนาดของงาน เพื่อนำมาวางแผนงานทำงานต่อไป นั่นเอง

เทคนิคที่นิยมใช้ประมาณขนาดของงาน เช่น Planning poker, Swimlane Sizing

พอเราได้ขนาดมาแล้ว เราก็ให้ทีม มาประมาณกำลัง หรือเวลาทำงานชิ้นนั้นอีกที

ทีนี้มาถึงความซับซ้อนของงาน ทำไมมีหลายๆคน อธิบายว่า SP คือ ความพยายาม กับ ความซับซ้อน ได้

ผมจะยกตัวอย่าง งานเหมือนเดิม ในทีมพัฒนา มีสมาชิก 2 คนคือ p1 และ p2 ให้ประมาณงาน w1 เหมือนกัน ได้ดังนี้

p1 ประมาณงาน w1 ได้ 1 วัน และ p2 ก็ประมาณงาน w1 ได้ 2 วัน

w1 = F * 1 — สำหรับ p1

w1 = F * 2 — สำหรับ p2

จากสมการงาน เรารู้แล้วว่า F ของ p1 ไม่เท่ากับ p2 แต่ทำไมถึงไม่เท่ากัน และทำอย่างไรถึงจะให้เท่ากันได้ เพื่อให้เห็นขนาดของงาน w1 นี้ตรงกัน

ทีนี้ผมจะพยายามทำให้ ทั้งสองคนนี้ใช้กำลังเท่ากัน เพื่อใช้อธิบายเรื่องความซับซ้อนของงาน โดยเลือกเอาค่าน้อยที่สุด เพื่อให้งานเสร็จไวครับ ก็คือ 1 วัน

ถ้า ผมให้ F เท่ากับ Effort (e) คือ ความพยายาม คูณกับ Knowledge (k) ความรู้ในงาน จะได้สมการงานใหม่ เป็น

F = k x e

นำไปแทนค่าใน สมการงาน ได้เป็น

W = k x e x d

โดยที่ k มีค่าระหว่าง 0 ถึง 1 คือ

ถ้าค่า k เท่ากับ 0 ไม่รู้เรื่องเกี่ยวกับงานนั้นเลย, ถ้าค่า k เท่ากับ 1 คือมีความรู้ในงานนั้นอย่างสมบูรณ์ ถ้าค่า k อยู่ระหว่าง 0 กับ 1 ก็คือ มีความรู้ในงานตามส่วนไป เช่น 0.5 หรือ รู้ 50% เป็นต้น (ซึ่ง 1-k มีความสัมพันธ์ทางบวกกับ technical debt )

ถ้าผมให้ Complexity (c) หรือ ความซับซ้อน คือความไม่รู้เกี่ยวกับงาน ดังนั้น c จะเท่ากับ 1 – k นั่นเอง

ดังนั้น ถ้าผมให้ p1 มี k เท่ากับ 1 คือ มีความรู้สมบูรณ์ และ p2 มี k เท่ากับ 0.5 หรือรู้ในงานนี้ 50% แทนค่าในสมการงาน ได้เป็น

w1 = e * 1 * 1 — สำหรับ p1

w1 = e * 0.5 * 2 — สำหรับ p2

เราจะพบว่าสมการงาน มีค่าเท่ากันได้ โดย p1 และ p2 ในความพยายาม (e) เท่ากัน แต่ความรู้ของทั้ง p1 และ p2 ไม่เท่ากัน

และนี่ก็คือที่มาว่า ทำไมถึงมีคนอธิบายว่า Story points ที่ใช้เป็นหน่วยวัดงาน นั้น มันก็คือ ความพยายาม (e) กับ ความซับซ้อน (c) เพราะว่า พวกมันมีความสัมพันธ์กัน แต่ไม่เกี่ยวข้องกัน แต่ส่งผลทำให้เกิดงาน นั้นเอง

ก่อนไป ผมมีอีกสามาการหนึ่งที่สามารถนำมาใช้อธิบายความสัมพันธ์ระหว่าง งาน, กำลัง, ความซับซ้อน และ Story points ก็คือ สมการมูลค่า ที่ว่า

Value = Price x Quantity

ผมเทียบกับงานซอฟแวร์ แล้วอธิบายได้ว่า

Price เท่ากับ Effort x Knowledge และ Quantity ก็คือ Story points ก็ได้เช่นเดียวกับ สมการงาน ครับ

ผมก็ขออธิบายไว้เพียงเท่านี้ หวังว่าจะช่วยให้เข้าใจ Story points เพื่อใช้ในประมาณค่า เพื่อวางแผนงาน ที่เข้าใจได้ชัดเจนขึ้นต่อไปครับ

ขอบคุณครับ

#:P

หนังสือแนะนำอ่านเพิ่มเติม

 

 

สร้าง chart ด้วย d3js กับข้อมูลธนาคารแห่งประเทศไทย ผ่าน Web API อย่างง่าย

สวัสดีครับ

บล็อคนี้ ผมมานำเสนอ การสร้าง chart เพื่อแสดงข้อมูลทางเศรษฐกิจ ที่ได้จาก Web API ของ ธนาคารแห่งประเทศไทย (BOT) ที่เปิดให้ใช้ครับ

ตอนนี้บริการ Web API ที่เปิดให้ มีข้อมูลเกี่ยวกับ อัตราแลกเปลี่ยน, อัตราดอกเบี้ย และ ผลการประมูลตราสารหนี้ อ่านรายละเีอยด ได้ที่นี่ครับ https://iapi.bot.or.th/Developer?lang=th

ตัวอย่าง ที่ผมจะแสดง คือ เป็นแอพ Web แสดง line chart หรือ กราฟเส้น ของข้อมูล อัตราแลกเปลี่ยนเฉลี่ย (รายวัน) แบบนี้

แกนนอน แสดงเวลา แกนตั้งแสดง อัตราแลกเปลี่ยน บาท THB ต่อ ดอลล่าสหรัส USD และ ที่ text box ให้ผู้ใช้ใส่ ปี และ เดือน ลงไป พอกด select ก็จะส่ง ปี และ เดือน ไปเอาข้อมูลจาก BOT Web API มาแล้วแสดงผล เป็น line chart ตามรูปครับ

เริ่มสร้างกันเลย

1. สร้าง html file ขึ้นมาครับ ตั้งชื่อว่า DAILY_AVG_EXG_RATE.html เติม code html เริ่มต้นเข้าไปก่อน แบบนี้ครับ

อธิบาย ดูกล่อง สีเขียว ครับ คือผมเพิ่ม library jQuery สำหรับช่วย query element DOM html ให้ สะดวกดี และ d3js  ใช้สำหรับสร้าง chart แสดงผลข้อมูล

2. เพิ่ม style sheet สำหรับ แสดงเส้น grid บน chart เพื่อให้อ่าน chart ได้ง่ายขึ้น เติม code CSS นี้ต่อจาก script ครับ

3. เพิ่ม javascript file ครับตั้งชื่อ BotChartPlayer.js เพิ่ม code สำหรับ สร้าง chart และ เรียกข้อมูลผ่าน Web API ของแบงค์ชาติครับ แบบนี้

// BotChartPlayer.js
function BotChartPlayer(opts) {

opts = opts || {};
opts.elementID = opts.elementID || ‘chart’;
opts.width = opts.width || 1000;
opts.height = opts.height || 450;
opts.dateFormat = opts.dateFormat || ‘%Y-%m-%d’;
opts.x_column = opts.x_column || ‘date’;
opts.y_column = opts.y_column || ‘value’;
opts.uri = opts.uri || ‘https://iapi.bot.or.th/Stat/Stat-ExchangeRate/DAILY_AVG_EXG_RATE_V1’;

var dataSet = {};

var svg = d3.select(‘#’ + opts.elementID)
.append(“svg”)
.attr(“width”, opts.width)
.attr(“height”, opts.height),
margin = { top: 20, right: 20, bottom: 30, left: 50 },
width = +svg.attr(“width”) – margin.left – margin.right,
height = +svg.attr(“height”) – margin.top – margin.bottom,
g = svg.append(“g”).attr(“transform”, “translate(” + margin.left + “,” + margin.top + “)”);

var parseTime = d3.timeParse(opts.dateFormat),

x = d3.scaleTime().rangeRound([0, width]),

y = d3.scaleLinear().rangeRound([height, 0]);

var line = d3.line()
.x(function (d) { return x(parseTime(d[opts.x_column])); })
.y(function (d) { return y(+d[opts.y_column]); });

g.append(“g”)
.attr(“class”, “axis-bottom grid”);

g.append(“g”)
.attr(“class”, “axis-left grid”)
.append(“text”)
.attr(“fill”, “#000”)
.attr(“transform”, “rotate(-90)”)
.attr(“y”, 6)
.attr(“dy”, “0.71em”)
.attr(“text-anchor”, “end”)
.text(“THB / USD”);

g.append(“path”)
.attr(“class”, “line”)
.attr(“fill”, “none”)
.attr(“stroke”, “steelblue”)
.attr(“stroke-linejoin”, “round”)
.attr(“stroke-linecap”, “round”)
.attr(“stroke-width”, 1.5);

var display = function (data) {

x.domain(d3.extent(data, function (d) {

return parseTime(d[opts.x_column]);

}

));
y.domain(d3.extent(data, function (d) {

return +d[opts.y_column];

}));

g.select(“.axis-bottom”)
.attr(“transform”, “translate(0,” + height + “)”)
.transition()
.call(d3.axisBottom(x).ticks(10).tickSize(-height))
.select(“.domain”)
.remove();

g.select(“.axis-left”)
.transition()
.call(d3.axisLeft(y).ticks(10).tickSize(-width));

g.select(“.line”)
.datum(data)
.transition()
.attr(“d”, line);

}

var pad = function (data, size) {

var s = String(data);
while (s.length < (size || 2)) { s = “0” + s; }
return s;

}

return {

play: function (year, month) {
var d = new Date(year, month, 0),

period = year + ‘-‘ + pad(parseInt(month), 2),

criteria = “start_period=” + period + “-01&end_period=” + period + “-” + d.getDate() + “&currency=USD”;

if (dataSet[period] == undefined) {
d3.json(opts.uri + “/?” + criteria)
.header(‘api-key’, ‘U9G1L457H6DCugT7VmBaEacbHV9RX0PySO05cYaGsm’)
.get(function (error, root) {
if (!error) {
var result = root.result;
if (result.success) {

var data = result.data.data_detail;
dataSet[period] = data;
display(dataSet[period]);

}
}
})
} else {

display(dataSet[period]);

}

}
};

}

// end of file BotChartPlayer.js

4. สุดท้ายครับ เติม code ให้สมบูรณ์ไปที่ file DAILY_AVG_EXG_RATE.html เพื่อแสดงผลของเรา ตามนี้ครับ

 

ขออธิบายหน่อยนึง

กล่องสีเขียว คือ form สำหรับผู้ใช้ ใส่ input ปี, เดือน และ คำสั่ง Select เพื่อแสดง chart

กล่องสีส้ม คือ ส่วนพื้นที่เพื่อใช้แสดง chart โดยเมื่อผู้ใช้กดคำสั่ง Select แล้วแอพจะ ไปเรียก Web API เพื่อเอาข้อมูลมา สร้าง chart โดย class file BotChartPlayer.js ที่ load เข้ามาในบรรทัดที่สอง

กล่องสีน้ำเงิน คือ ขั้นตอนสร้าง BotChartPlayer ใช้สร้าง chart และ เรียก Web API กับขั้นตอน นำค่า ปี และ เดือน เพื่อส่งไปให้ Web API สำหรับช่วงเวลาข้อมูลอัตราแลกเปลี่ยน

เสร็จแล้วครับผม ทีนี้ก็ลองเล่นดูครับ โดยเปิดด้วย browser ปกติครับผม ถ้ามีเวลาผมก็จะค่อยๆเพิ่มการแสดงผล chart สำหรับ BOT Web API อื่นๆที่เหมาะสมต่อไปครับ ตาม source code ได้ที่ ChartBOT

ตัวอย่าง Bar chart ข้อมูลจาก BOT Web API อัตราดอกเบี้ยเงินให้สินเชื่อของธนาคารพาณิชย์ (อัตราร้อยละต่อปี)

ขอบคุณครับ

#:P

เก็บข้อมูล time-series ด้วย InfluxDB

สวัสดีครับ

วันนี้จะมานำเสนอการเก็บข้อมูลแบบ Time-series ด้วยฐานข้อมูลสำหรับแก้ปัญหางานนี้โดยเฉพาะที่ชื่อว่า InfluxDB

ข้อมูล Time-series เป็นข้อมูลแบบ Stream ชนิดหนึ่ง คือมันเป็นข้อมูลสถานะจากวัตถุในเวลาหนึ่ง แต่ถูกเก็บไว้อย่างต่อเนื่อง ดังนั้น เราจึงเห็นการเปลี่ยนแปลงสถานะของวัตถุนั้นในเวลาต่างๆอย่างต่อเนื่องให้เห็นรูปแบบพฤติกรรมของวัตถุนั้นได้ เพื่อตัดสินใจวางแผนแก้ปัญหาต่างๆต่อไป ตามจุดประสงค์ของปัญหาในวัตถุนั้นๆ (Domain problems)

วัตถุที่ว่านี้ เช่น ทรัพยากร, ผู้ใช้ซอฟแวร์, log ข้อมูลต่างๆของ server, Orders, Inventory, Company, Government และสิ่งต่างที่ สามารถวัดค่าบางอย่างได้ รวมเรียกว่าวัตถุ

วัตถุในเป้าหมาย จะต้องถูกวัดค่า(Measure) ได้ โดยมันจะมีคุณลักษณะ หรือ feature ที่แตกต่างกันได้ด้วย รวมเรียกว่า สถานะ จะถูกเก็บไว้นะเวลาหนึ่ง อย่างต่อเนื่อง ที่เรียกว่า Time-series นั่นเอง

ลงมือทำกันเลยครับ

  1. ติดตั้ง wget สำหรับ download Influx download ได้ที่ https://eternallybored.org/misc/wget/ ผมเลือก wget64.exe ได้ file มาแล้ว ให้ set PATH Environment เพื่อใช้งานไดสะดวก
  2. จัดเก็บข้อมูลด้วย InfluxDB

เข้าไป download ได้ที่ https://portal.influxdata.com/downloads หรือใช้ wget เข้า Command prompt แล้วพิมพ์

> wget64 https://dl.influxdata.com/influxdb/releases/influxdb-1.3.5_windows_amd64.zip

แตก zip ออกมาครับ แล้วเปิด command prompt ให้ Run as administrator แล้ว cd ไปที่ folder ที่แตก zip นี้ พิมพ์คำสั่ง เพื่อ start InfluxDB แบบนี้

> influxd -config influxdb.conf

พอ start influxdb ลองทดสอบ สร้าง, query กันดูครับ เข้า command prompt พิมพ์ แล้ว cd ไปที่ influxdb ที่เพิ่งแตก zip ออกมา พิมพ์คำสั่ง เพื่อ connect เข้าใช้งาน

> influx

ถ้าได้ จะขึ้นเหมือนเราแบบนี้

ทีนี้สร้าง db ชื่อว่า testdb ดูครับ

> CREATE DATABASE testdb

แสดง DATABASE ทั้งหมดที่มีออกมา ก็จะเห็น testdb ที่เพิ้งสร้าง

> SHOW DATABASES

เข้าใช้ database testdb แล้วเพิ่มข้อมูลลงไป โดย structure การสร้าง มีโครงสร้างแบบนี้ครับ

<measurement>[,<tag-key>=<tag-value>…] <field-key>=<field-value>[,<field2-key>=<field2-value>…] [unix-nano-timestamp]

<measurement> คือ เป้าหมายสิ่งที่เราต้องการวัดค่า หรือ ทรัพยากรที่เรามีอยู่ เช่น Orders, Claims, CPU, Client, Host เป็นต้น ใน Data warehouse เทียบได้กับ Fact Table

<tag-key>=<tag-value> คือ ชื่อตัวแปลแบบ label กับ ค่าของมัน คือ ตัวแปลที่บอกคุณลักษณะของ measurement มีชนิดข้อมูลเป็น label, nominal หรือ category เช่น Gender ={ Male, Female }, HostName = { host1, host2, … }, OrderStatus = { Pending, Rejects, Cancel, Paid } ใน Data warehouse เทียบได้กับ Dimension

*ค่า tags ต้องไม่มีช่องว่าง

<field-key>=<field-value> คือ ชื่อตัวแปลแบบวัดค่าได้ กับ ค่าของมัน คือ ตัวแปลที่บอกปริมาณของ measurement มีชนิดข้อมูลเป็นตัวเลข เช่น CPU Usage, Revenue, Expense, Income, Total Sale Amount ใน Data warehouse เทียบได้กับ Fact Value

[unix-nano-timestamp] เป็นเวลาที่บันทึก measurement นี้ได้ หน่วยเป็นตัวเลข ถ้าไม่ระบุ จะกำหมดว่าเวลานั้นเป็นสถานะของวัตถุ

ลองสร้างข้อมูลที่ testdb ก่อนอื่นไปที่ testdb ก่อน

> USE testdb

คำสั่ง สร้างข้อมูล

> INSERT cpu,host=”Server-A” total_usage=0.64

> INSERT home,owner=”#:12″,address=”123_456_Bangkok_10110″ total_cost=9291,door_lock=0,peple=0,grass_healthy=0.8,dog_food=0.5,fish_food=0.2

คำสั่ง query ข้อมูล

> SELECT host, total_usage FROM cpu

> SELECT * FROM home WHERE door_lock = 1 AND peple > 0

จะเห็นว่า คำสั่งใช้งานไม่ยาก เหมือน SQL ที่เราคุ้นเคยกัน

ลองพิพม์คำสั่งตามตัวอย่างแล้วดูผลลัพธ์นะครับ ถ้ายังไม่ได้ ก็อย่าเพิ่งไปต่อ ถ้าได้แล้วก็เปิดมันทิ้งไว้

ขั้นต่อไป เราจะเก็บข้อมูลด้วย Telegraf กัน

ตัว Telegraf จะคอยวนเก็บข้อมูล Host ให้เรา ข้อมูลที่เก็บอเช่น CPU, Memory, Hard disk และอื่นๆที่เกี่ยวกับ Host ของเรา เริ่มติดตั้ง และทดลองใช้งานกันเลยครับ

  1. เก็บข้อมูลด้วย Telegraf

เข้าไป download Telegraf ด้วย wget ที่ติดตั้งไว้แล้ว ที่ข้อ 1 จากนั้นใช้มันเพื่อ download Telegraf ครับ แบบนี้

> wget64 https://dl.influxdata.com/telegraf/releases/telegraf-1.4.0_windows_amd64.zip

พอได้มาแล้วให้แตก zip ออกมา แล้ว start มันได้เลย เข้า command prompt แล้ว cd ไปที่ folder ที่แตก zip ออกมา พิมพ์คำสั่ง เพื่อ start มันครับ

> telegraf -config telegraf.conf

ลองเปิดเข้าไปดู config file telegraf.conf ของ telegraf จะเห็นโครงสร้างข้อมูลที่มันเก็บให้เราครับ

ใน section ของ INPUTS

และ ในส่วนของ agent จะเป็น config เกี่ยวกับความถี่ในการเก็บข้อมูล ค่าเริ่มต้นจะเป็นทุก 10 วินาที

ครับทีนี้ เราของเข้าไปที่ Influx แล้ว use database telegraf  แล้ว query ดูครับ

> USE telegraf

> SELECT * FROM win_cpu LIMIT 1

ผลที่แสดงออกมาก็ดูไม่ค่อยรู้เรื่องเท่าไร ก็ไปใช้ซอฟแวร์อีกตัวเพื่อดูผลลัพธ์ แบบ time-series ที่เรียกว่า Chronograf กันต่อเลยครับ

  1. สำรวจข้อมูลด้วย Chronograf

ตัวนี้เรา สามารถ download ตรงๆได้เลยที่

https://dl.influxdata.com/chronograf/releases/chronograf-1.3.8.1_windows_amd64.zip

ได้ file มา ก็ทำเหมือนเดิมครับ แตก zip แต่สำหรับตัวนี้ start ง่ายหน่อย click ขวา ที่ chronograf.exe แล้วเลือก Run as administrator ด้วยนะครับ start Chronograf ก็เสร็จแล้ว ลองเล่นดูเข้าไปที่ http://localhost:8888/

ครั้งแรกให้ ใส่ Connection string: localhost:8086 มันคือ server InfluxDB ที่เรา start ไว้แล้วที่ข้อ 2 และ Name Influx-1 ใส่ Username / Password เสร็จแล้วกด Add source ครับ

เราจะสร้าง Dashboards แรกกัน

1. ไปที่ menu ด้านซ้าย เลือก Dashboards กด Create Dashboards

2. ที่ Cell แรก กด Add Graph ครับ

3. Add Query สำหรับ measure CPU ที่ tab ล่างสุด เลือก database telgraf > win_cpu > Percent_User_Time แล้วกด Save

4. กด Add Cell จะเพิ่ม Cell ว่างเข้ามาใน Dashboards ของเราครับ แล้วกด Add Graph

5. กด Add Query สำหรับ measure Memory ไปที่ tab ล่างสุด เลือก database telgraf > win_mem > Available_Bytes แล้วกด Save ครับ

เสร็จแล้วครับ Dashboards แรก ปรับแต่ง เปลี่ยนชื่ออีกเล็กน้อย ก็จะได้ผลลัพธ์แบบนี้

ก็ลองนำไปใช้ประโยชย์กันนะครับ สำหรับข้อมูลแบบ time-series ด้วย InfluxDB

ผมขอจบบล็อคไว้เพียงเท่านี้ หวังว่าจะเป็นประโยชน์นะครับ

ขอบคุณครับ

#:P

 

 

สร้าง Live Video ด้วย OpenCV และ Kafka

สวัสดีครับ

วันนี้เราก็มานำเสนอต่อด้วยตัวอย่างประยุกต์ใช้ OpenCV อีกหน่อย ต่อเนื่องจากบล็อคก่อนนี้ บล็อคนี้เราจะมาพัฒนาแอพถ่ายทอดสดกันครับ

หลักการก็มีอยู่ว่า พอเราใช้ OpenCV จับ frame ภาพมาแล้วเราต้องการที่จะ ส่งไปยัง แอพผู้รับที่อยู่ไกลออกไป โดยสามารถเห็นภาพที่เราถ่ายอยู่ในขณะเดียวกันนี้ พร้อมกันได้

ดังนั้น เราจะต้องมีสื่อกลางอย่างหนึ่งสำหรับสื่อสาร รับ/ส่งข้อมูล โดยต้องมีตัวส่งข้อมูลภาพ ที่เป็น frame ต่อเนื่องรัวๆนี้ หรือที่เรียกว่า stream ข้อมูล และ ต้องมีตัวคอยรับข้อมูล stream frame ภาพนั้นนำมาแสดงได้อย่างต่อเนื่องรัวๆ เพื่อจะได้เห็นเป็นภาพเคลื่อนไหวได้ ไปพร้อมๆกัน ไม่ว่าจะอยู่ที่ไหนก็ตาม ในแบบที่เรียกว่า real time

สื่อกลางตัวสื่อสารรับส่งข้อมูล stream ที่เราจะนำมาใช้ก็คือ Kafka ครับ Kafka คือ Streaming platform ที่ทำหน้าที่เป็นสื่อกลางแบบเดียวกับ Messaging system แต่มันถูกพัฒนามาเพื่อรับส่งข้อมูลแบบ Streaming โดยเฉพาะ และมันยังสามารถเก็บ stream ข้อมูลไว้ในแบบ time-series ใช้อ่านซ้ำเพื่อประโยชน์ในงานอื่นได้อีกด้วย

ก็ไปอ่านเพิ่มเติมได้ที่ https://kafka.apache.org/intro.html

ภาพนี้เป็นโครงสร้างของแอพถ่ายทอดสดนี้ครับ

จากภาพ มี live_producer เป็นแอพสำหรับ capture ภาพจากโลกจริงเป็น frame ต่อเนื่องแสดงถี่ๆ ก็จะเป็นภาพเคลื่อนไหว Kafka system เป็นระบบสื่อกลาง รับ/ส่ง frame ภาพต่อเนื่องที่จับได้จาก live_producer Kafka system จะทำงานคล้ายๆ อากาศ ที่ส่งข้อมูลคลื่นเสียงได้ และ OpenCV.Live คือ แอพสำหรับ รับภาพถ่ายทอดสดเป็น frame ต่อเนื่องจาก Kafka system OpenCV.Live แอพ สามารถเปิดรับข้อมูลภาพถ่ายทอดสดได้หลายตัว ในภาพเราแสมดไว้ 4 ตัว

เอาละ ดูภาพโครงสร้างความเชื่อมโยงของแอพต่างๆแล้ว เรามาเริ่มลงมือพัฒนามันกันเลยดีกว่าครับ

Download และ ติดตั้ง Kafka

1. เข้าไป download Kafka ได้ที่ https://kafka.apache.org/downloads เลือก Binary kafka_2.12-0.11.0.0.tgz

2. จะได้ file kafka_2.12-0.11.0.0.tgz มาแตก zip ออก (ตัวอย่างเขียนบล็อคเราวางไว้ที่ drive E: )

ก่อนจะ run Kafka เราต้อง start Zookeeper ก่อน มันทำหน้าที่เป็น tasks load balancing  ให้กับ Kafka

3. เปิด Command Prompt แล้วใช้คำสั่ง cd ไปที่ folder kafka_2.12-0.10.2.0 ที่แตก zip ออกมาแล้ว

4. พิมพ์คำสั่งข้างล่างนี้เพื่อ start Zookeeper

bin\windows\zookeeper-server-start config\zookeeper.properties

ต่อไปก็ start Kafka ทำหน้าที่เหมือน Messing system ที่สามารถรับส่งข้อมูลระหว่างแอพแบบ Streamได้

5. เปิด Command Prompt โดยเปิดแบบ Run as administrator ใช้คำสั่ง cd ไปที่ folder kafka_2.12-0.10.2.0

6. พิมพ์คำสั่งข้างล่างนี้เพื่อ start Kafka

bin\windows\kafka-server-start config\server.properties

เสร็จแล้วครับขั้นตอน download และ ติดตั้ง Kafka server

ขั้นต่อไปเราจะพัฒนาแอพ ที่ทำหน้าที่ถ่ายทอดสด โดยมันจะจับภาพจากกล้องแล้ว ส่งข้อมูลไปยัง Kafka อย่างต่อเนื่องเป็น stream data

พัฒนาแอพถ่ายทอดสด

Code ส่วนนี้เขียนด้วย Python ใช้ library OpenCV วิธีติดตั้ง และใช้งานดู้ได้จากบล็อคของเราที่ได้นำเสนอไปแล้วเรื่อง สร้างดวงตาให้ AI ด้วย OpenCV เขียนด้วย Python บน Windows

ส่วนที่ทำหน้าที่ส่งข้อมูลภาพจากกล้อง ไปยัง Kafka จะใช้ library อีกตัวที่ชือว่า Kafka Python client ติดตั้งกันก่อนครับ

1. ติดตั้ง Kafka Python client ด้วย pip เปิด Command Prompt แล้วพิมพ์คำสั่งนี้ลงไป

pip install kafka-python

อ่านข้อมูลเพิ่มเติมได้ที่ https://github.com/dpkp/kafka-python

2. สร้าง file live_producer.py เพื่อเขียน code Python แล้วเขียน code ตามข้างล่างนี้ลงไปครับ

import cv2

from kafka import KafkaProducer

producer = KafkaProducer(bootstrap_servers=’localhost:9092′)

cap = cv2.VideoCapture(0)

while(True):

    ret, frame = cap.read()

    cv2.imshow(‘live_camera’, frame)

    retval, img  = cv2.imencode(‘.png’, frame)

    producer.send(‘live-no99’, img.tobytes())

    if cv2.waitKey(1) & 0xFF == ord(‘q’): break

cap.release()

cv2.destroyAllWindows()

ผมขออธิบาย code เฉพาะส่วนที่ติดต่อกับ Kafka

producer = KafkaProducer(bootstrap_servers=’localhost:9092′) คือ สร้าง class KafkaProducer กำหนด bootstrap_servers ไปยัง URL ของ Kafka server ก็คือ localhost:9092 ที่เราได้เปิดไว้แล้ว เราสามารถเพิ่ม kafka หลายๆเครื่องเป็นกลุ่ม เพื่อประโยชน์ในการกระจายงานได้ โดยขั้นด้วย , ต่อๆกันไป เช่น  kafka-1-ip:9092, kafk-2-ip:9092,…. kafka-n-ip:9092

ดู code ส่วนของ while loop หลังจากจับ frame ภาพได้แล้วแสดงด้วย code frame = cap.read() ก็จะทำการ encode เป็น file ภาพ .png ก่อน ด้วย code retval, img = cv2.imencode(‘.png’, frame) แล้วเรียกใช้ function send ของ producer เพื่อส่งภาพ ไปยัง channel live-99 ต่อไปแสดงด้วย code producer.send(‘live-no99’, img.tobytes())

เสร็จแล้วครับ แอพในส่วนของถ่ายทอดสด ลอง run ขึ้นมาลยครับ เปิด Command Prompt แล้ว run code live_producer.py ด้วยคำสั่ง

python live_producer

พอ run code แล้ว แอพก็จะมีหน้าตาคล้ายๆภาพข้างล่างแบบนี้ ให้เปิดทิ้งไว้เลยครับ ตัวอย่าง มันก็จะมืดๆหน่อยนะครับ

Code ที่เสร็จสมบูรณ์ อยู่ที่ https://github.com/chavp/openCV_with_Python/blob/master/live_producer.py

เรามีแอพที่ทำหน้าที่ส่งข้อมูลภาพ เป็นแบบ stream สดๆ หรือ real time แล้ว ทีนี้ก็ต้องมีแอพ ที่เป็นตัวรับข้อมูล stream ภาพมาแสดงด้วยอีกตัวหนึ่ง

สำหรับแอพตัวนี้ผมจะ ใช้ C# .NET พัฒนา เป็นแบบวินแอพ WPF ทำหน้าที่แสดงผลภาพถ่ายทอดสด ด้วย class System.Windows.Controllers.Image ของ WPF แล้วจะใช้ library ที่เรียกว่า Confluent.Kafka เพื่อคอยรับ

streaming data ภาพจาก channel live-99  บน Kafka มาส่งให้ class System.Windows.Controllers.Image เพื่อแสดงผลภาพต่อเนื่องอีกที

1. เปิด Visual Studio 2017 Community ครับ (ถ้ายังไม่มี ก็ไป download และติดตั้งได้ที่ https://www.visualstudio.com/vs/whatsnew/)

2. สร้าง projects WPF ตั้งชื่อ OpenCV.Live ดูภาพ

ต่อไปเราจะติดตั้ง package Confluent.Kafka

3. ไปที่ menu Tools > NuGet Package Manager > Package Manager Console ขึ้นมาจะอยู่ที่ tab ด้านล่าง พิมพ์คำสั่ง

Install-Package Confluent.Kafka

4. สร้างหน้าจอ windows สำหรับแสดงผล แก้ไข file MainWindow.xaml แบบข้างล่างนี้

<Window x:Class=”OpenCV.Live.MainWindow”

        xmlns=”http://schemas.microsoft.com/winfx/2006/xaml/presentation”

        xmlns:x=”http://schemas.microsoft.com/winfx/2006/xaml”

        xmlns:d=”http://schemas.microsoft.com/expression/blend/2008″

        xmlns:mc=”http://schemas.openxmlformats.org/markup-compatibility/2006″

        xmlns:local=”clr-namespace:OpenCV.Live”

        mc:Ignorable=”d”

        Title=”Channal-99″ Height=”350″ Width=”525″>

    <Grid>

        <Image Name=”videoLiveView” Margin=”10″/>

    </Grid>

</Window>

5. เพิ่ม class ImageStreamDeserializer.cs เพื่อแปลงข้อมูล bytes ที่รับจาก channel live-99  ไปเป็นข้อมูลชนิดอื่นตามที่เราต้องการ ในที่นี้รับมา ไม่ต้องทำอะไร ส่งกลับออกไปเลย

namespace OpenCV.Live

{

    using Confluent.Kafka.Serialization;

    public class ImageStreamDeserializer : IDeserializer<byte[]>

    {

        public byte[] Deserialize(byte[] data)

        {

            return data;

        }

    }

}

7. ไปที่ file MainWindow.xaml.cs แล้วเพิ่ม code ที่คอยข้อมูลจาก channel live-99  ที่ live_producer.py คอยจับภาพจากกล้องแล้วส่งมาอย่างต่อเนื่องแบบเป็น bytes ข้อมูล เข้ามา ซึ่งตอนนี้ก็น่าจะเปิดทิ้งไว้อยู่

using System;

using System.IO;

using System.Collections.Generic;

using System.Threading.Tasks;

using System.Windows;

using System.Windows.Media.Imaging;

namespace OpenCV.Live

{

    using Confluent.Kafka;

    /// <summary>

    /// Interaction logic for MainWindow.xaml

    /// </summary>

    public partial class MainWindow : Window

    {

        public MainWindow()

        {

            InitializeComponent();

            var config = new Dictionary<string, object>

            {

                { “group.id”, “live-no99-group” },

                { “bootstrap.servers”, “localhost:9092” }

            };

            Task.Factory.StartNew(() =>

            {

                using (var consumer = new Consumer<Null, byte[]>(config, null, new ImageStreamDeserializer()))

                {

                    consumer.Assign(new List<TopicPartitionOffset> { new TopicPartitionOffset(“live-no99”, 0, Offset.End) });

                    while (true)

                    {

                        Message<Null, byte[]> msg;

                        if (consumer.Consume(out msg, TimeSpan.FromSeconds(1)))

                        {

                            using (var stream = new MemoryStream(msg.Value))

                            {

                                this.Dispatcher.Invoke(() =>

                                {

                                    videoLiveView.Source = BitmapFrame.Create(stream,

                                                                      BitmapCreateOptions.None,

                                                                      BitmapCacheOption.OnLoad);

                                });

                            }

                        }

                    }

                }

            });

        }

    }

}

8. เสร็จแล้วครับ ซอฟแวร์สำหรับแสดงข้อมูลภาพแบบถ่ายทอดสด เราได้ทดลอง run ออกมาหลายๆ หน้าจอ ผลก็แสดงได้ดังข้างล่างนี้

code ที่สมบูรณ์ https://github.com/chavp/openCV_with_Python/tree/master/OpenCV.Live

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

ขอบคุณครับ

#:P

 

#c, #kafka, #live-video, #opencv, #python

สร้างดวงตาให้ AI ด้วย OpenCV เขียนด้วย Python บน Windows

สวัสดีครับ

วันนี้ก็มานำเสนองานที่เกี่ยวข้องกับ AI ส่วนหนึ่งก็คือ การมองเห็น สำหรับ AI เป็นก็คือ Computer ชนิดหนึ่ง ตาของมันก็ต้องเป็นกล้อง Camera

บล็อคนี้ผมก็จะแนะนำให้รู้จัก OpenCV มันคือ Library open source สำหรับจับ, ประมวลผล และ แสดงผลภาพ จากกล้อง Camera ในคอมพิวเตอร์ หรือ มือถือของเราครับ

เริ่มทำกันเลยครับ

ก่อนอื่น เราต้องมีกล้อง Camera ก่อนครับ ของผมติดอยู่บนเครื่อง notebook แล้ว สมัยนี้น่าจะมีกันทุกคน

งานนี้ผมจะเขียนด้วย Python ครับ ผมใช้ WinPython เข้าไป download  และ ติดตั้งได้ที่ https://winpython.github.io/ ผมเลือก 64bit

ต่อไปก็ ต้องลง 2 module คือ numpy และ openCV โดยใช้ wheel ครับ ต้อง download 2 file นี้มาก่อนครับ

  1. numpy-1.13.1+mkl-cp36-cp36m-win_amd64.whl
  2. opencv_python-3.3.0-cp36-cp36m-win_amd64.whl

เข้าไป download ได้ที่ http://www.lfd.uci.edu/~gohlke/pythonlibs/

พอได้มาแล้ว copy ทั้ง 2 files ไปวางไว้ที่ folder C:\Users\windows แล้วเข้า Command Prompt พิมพ์คำสั่ง ข้างล่างนี้ลงไปครับ

ติดตั้ง numpy

pip install numpy-1.13.1+mkl-cp36-cp36m-win_amd64.whl

เสร็จแล้ว ติดตั้ง opneCV ต่อ

pip install opencv_python-3.3.0-cp36-cp36m-win_amd64.whl

ติดตั้งเสร็จแล้วก็ เริ่มเขียน code ได้เลย

โปรเจ็คแรกจะทดสอบแสดงภาพ Camera ด้วย openCV กันก่อน

1. สร้าง file first_camera.py แล้วเขียน code ตามข้างล่างนี้ลงไป

2. import numpy และ cv2 เข้ามา

import numpy as np

import cv2

3. จับภาพจากกล้องหน้า

cap = cv2.VideoCapture(0)

4. วนดำเนินการนำภาพ มาแสดงที่ windows เรื่อยๆ ก็คือการแสดงภาพเคลื่อนไหวนั่นเองครับ โดยผมจะ process frame ก่อน เพื่อแสดงเป็นภาพขาวดำครับ

while(True):

    # จับภาพแบบ frame by frame

    ret, frame = cap.read()

    # ตรงนี้ผมจะแสดงภาพออกมาเป็นขาวดำ โดยคำนวนผ่าน function cvtColor

    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)

    # แสดงผล frame ที่เป็นขาวดำครับ

    cv2.imshow(‘frame’, gray)

    if cv2.waitKey(1) & 0xFF == ord(‘q’): # หน้าจอ จะปิดก็ต่อเมื่อ ผมกด q

        break

5. จบวน while แล้วก็ให้ ปล่อย Camera และทุก windows ที่แสดงภาพ  ครับ

cap.release()

cv2.destroyAllWindows()

ภาพตัวอย่าง video ที่แสดงผ่าน openCV โดยแต่งสีภาพเป็นสีเทาก่อนครับ

Code สมบูณร์แล้ว ดูได้ที่ https://github.com/chavp/openCV_with_Python/blob/master/first_camera.py

ลำดับต่อไป ผมจะติดตามวัตถุในภาพ โดยการจับวัตถุบนภาพ ซึ่งเป็นงานประยุกต์ที่เราเห็นทั่ว ไป พอติดตามวัตถุได้ ก็นำไปค้นหา โดยใช้เทคนิควิเคราะห์ข้อมูล Machine learning  ซึ่งผมจะนำเสนอต่อไป เพื่อระบุว่าเป็นวัตถุอะไร มีคุณสมบัติอะไร แล้วก็สามารถทำสิ่งต่างๆ เช่น ส่งผลลัพธ์ให้ AI เพื่อเล่นเกม หรือทำงานต่างๆ ต่อไปนั่นเอง

เทคนิคการติดตามวัตถุ มีหลายเทคนิค สำหรับ บล็อคนี้ ผมจะนำเสนอ การติดตามใบหน้าคนครับ

การติดตามใบหน้า ก็ต้องมีการตรวจจับใบหน้าให้ได้ก่อน โดยใช้เทคนิคการตรวจจับใบหน้าที่เรียกว่า Haar Cascade Classifier โดย feature ที่ใช้ตรวจจับใบหน้า จะอยู่ในรูป format xml ซึ่งมีคน train ไว้ให้แล้ว ผมจะนำมาโหลดเข้าไปใน Haar cascade classifier เพื่อตรวจจับใบหน้าได้เลย อ่านเพิ่มเติมได้ที่ http://www.cs.ubc.ca/~lowe/425/slides/13-ViolaJones.pdf

เข้าไป  download file Haar features มาก่อนครับที่ https://github.com/opencv/opencv/tree/master/data/haarcascades

ผมเลือก download เฉพาะ file haarcascade_frontalface_default.xml เพื่อตรวจจับใบหน้าคนครับ ในนั้นก็มี feature ตรวจจับลูกตา, หน้ายิ้ม และอื่นๆด้วย

ต่อไปก็เขียน code สำหรับติดตามหน้าคนได้แล้วครับ

1. สร้าง file face_detector.py

2. copy code จาก file first_camera.py มาใส่ที่ file นี้ได้เลยครับ

3. สร้าง face cascade โดยโหลด file haarcascade_frontalcatface.xml ที่ download มาครับ

import numpy as np

import cv2

face_cascade = cv2.CascadeClassifier(‘haarcascade_frontalcatface.xml’)

ที่ใส่สีดำไว้ เพื่อให้เห็นความต่างของ code ที่เพิ่มเติมเข้าไปจาก first_camera.py

4. ในวน loop while หลังจากที่สร้าง gray frame แล้ว ให้ใช้ face_cascade เพื่อตรวจจับใบหน้าคนครับ

gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)

face_rects = face_cascade.detectMultiScale(gray, 1.3, 5)

5. จาก code ด้านบนเราจะได้รายการของ frame สี่เหลี่ยมที่จับได้ว่าเป็นใบหน้าคนมา เราก็จะนำ face_rects วาดสี่เหลี่ยมทับไปบน frame ก่อนนำไปแสดงผลครับ

for (x,y,w,h) in face_rects:

                            cv2.rectangle(frame, (x,y), (x+w,y+h), (0,255,0), 3)

        # แสดงผล frame ที่ถูกครอบด้วยสี่เหลี่ยมบนใบหน้าแล้ว

                           cv2.imshow(‘face_detector’, frame)

ภาพตัวอย่าง video ที่แสดงผ่าน openCV โดยเพิ่มการติดตามภาพหน้าคนเข้าไปครับ

Code สมบูณร์แล้ว ดูที่ https://github.com/chavp/openCV_with_Python/blob/master/face_detector.py

เสร็จแล้วครับ การติดตามหน้าคนบน Camera จะเห็นว่า cv2 มันคอยจับภาพ(capture) ออกมาจาก Camera เป็น image แล้ว เราก็ใช้ Image processing เทคนิคที่ชื่อว่า Haar cascade เพื่อจับตำแหน่งหน้าคนบนภาพ ต่อจากนั้นเราก็นำต่ำแหน่งที่ได้มา แก้ไขภาพแล้วแสดงออกอีกที ในทางอื่น เราก็สามารถส่งภาพที่จับได้นี้ไปวิเคราะห์ใน module  หรือ application อื่นได้อีก เช่น Argument reality, Image processing หรือ Service อื่นๆ เพื่อวิเคราะห์เจาะจงลงไปได้อีก หลักการมีเท่านี้ ก็ทำให้ AI ของเรามีดวงตา และฉลาดได้อีกครับ

ขอจบบล็อคไว้เท่านี้ หวังว่าจะมีประโยชน์

ขอบคุณครับ

#:P

ศึกษาเพิ่มเติมได้ที่: http://docs.opencv.org/3.0-beta/doc/py_tutorials/py_tutorials.html

#opencv, #python