เก็บข้อมูล 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

 

 

Advertisements

สร้าง 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

เรียนรู้ AI ใช้เล่น Game ในแบบง่ายๆก่อน

สวัสดีครับ

วันหยุดนี้ ผมก็ว่างมาเขียนบล็อค AI เพื่อเล่น Game ด้วย Python ง่ายๆ โดยเริ่มจาก Game ผู้เล่น 2 คนครับ ซึ่งผมคิดว่า วิธีที่ง่ายที่สุด ในการเรียนรู้ ทำความเข้าใจ AI ก็คือ เอามันมาเล่น Game กับเรา หรือ สร้างมันให้เล่น Game กับคนอื่นแทนเราได้

บล็อคนี้ก็จะเป็น บล็อคเริ่มต้นของผมเอง ในการจะนำตัวเองไปพัฒนา AI สำหรับเล่น Game หรือ คิด วิเคราะห์ เพื่อทำงานอื่นๆ แทนผมเอง ซึ่งอาจจะมีความซับซ้อนมากขึ้นเรื่อยๆ Library แรกที่ผมจะนำเสนอนนี้เรียกว่า easyAI

easyAI เป็น game AI เฉพาะ ที่พัฒนาจาก Python สำหรับเกมส์ ผู้เล่น 2 คน เช่น Tic-Tac-Toe, Connect 4, Hexapawn และ อื่นๆ

easyAI ใช้สร้างกลไกของเกมส์ เพื่อให้เราเล่น กับ AI หรือ ให้ AI เล่นกันเอง เล่นกับคนอื่น แะสามารถใช้แก้ปัญหาของเกมส์เพื่อหาทางเลือกที่ดีที่สุดของเกมส์ ใช้เป็นผลฉลยสำหรับ Game ต่อไปได้

ลงมือทำด้วยตัวอย่างง่ายๆ GameOfBones

กฎการเล่นก็คือ มีผู้เล่น 2 คน, มีกองกระดูกอยู่ 20 ชิ้น, ผู้เล่นจะต้องสลับกันหยิบ โดยในตาของผู้เล่น ผู้เล่นสามารถหยิบได้ 1, 2 และ 3 ชิ้น เกมจะจบลงเมื่อกระดูกหมด และ ผู้เล่นที่หยิบกระดูกชิ้นสุดท้าย จะเป็นผู้แพ้

1. เริ่มต้นคุณต้องติดตั้ง Python ในเครื่องก่อน ผมใช้ Windows 10 จึงติดตั้ง WinPython download และ ติดตั้งได้ที่ https://winpython.github.io/

ติดตั้งเสร็จทดสอบ Python เข้า Command Prompt  (Serach windows พิมพ์ cmd) พิมคำสั่ง

py –version

2. ติดตั้ง easyAI พิมคำสั่ง

pip install easyAI

3. สร้าง file game_of_bones.py แล้วเขียน code ตามข้างล่างนี้ โดย import mdule easyAI เข้ามาก่อน แล้วเขียน class Game Rule ต่อไป

from easyAI import TwoPlayerGame, Human_Player, AI_Player, Negamax

class GameOfBones(TwoPlayerGame):

              ”’ ผู้เล่น สามารถเอากระดูกออกได้ 1, 2, 3 ชิ้น จากกอง ผู้เล่นที่เอากระดูกออกชิ้นสุดท้าย จะเป็นผู้แพ้ ”’

               def __init__(self, players):

                              self.players = players

                              self.pile = 20 # เริ่มต้นมีกระดูกในกอง 20 ชิ้น

                           self.nplayer = 1 # ผู้เล่นคนแรก เริ่มเล่นก่อน

             def possible_moves(self): return [ ‘1’, ‘2’, ‘3’ ] # ทางเลือกที่ผู้เล่นสามารถเลือกได้ทั้งหมด

               def make_move(self, move): self.pile -= int(move) # เมื่อผู้เล่นเลือกเล่นแล้ว สถานะเกมจะเปลี่ยน

             def win(self): return self.pile <= 0 # ผู้เล่นที่อยู่ในเงื่อนไขนี้ จะเป็นผู้ชนะ หรือในทางตรงข้ามคือแพ้

             def is_over(self): return self.win() # เกมส์จะจบลงเมื่อมี ผู้เล่นชนะ

             def show(self): print(self.pile, “ bones left in the pile”) # แสดงสถานะของ game ล่าสุด

             def scoring(self): return 100 if game.win() else 0 # ผู้ชนะได้ 100 นอกนั้นได้ 0 สำหรับ AI

ai = Negamax(13) # AI จะคิดตาเล่น 13 ครั้งล่วงหน้า เป็นความยากของ AI ลองปรับค่าดู

game = GameOfBones( [Human_Player(), AI_Player(ai)] )

history = game.play()

จาก code จะเห็นว่ากำหนด AI algorithm เป็น Negamax(13) ใน GameOfBones นั่นก็คือ AI จะสร้าง table ผลลัพย์ที่เป็นไปได้ล่วงหน้า 13 ทางเลือก แล้วเลือกทางที่ดีที่สุด ลองปรับค่า แล้วเล่นดูจะเห็นความฉลาดเลือกเดินของ AI

4. เริ่มเล่น game ได้ครับ โดยเข้า Command Prompt แล้วพิมพ์ คำสั่ง run script แบบนี้

python  game_of_bones.py

ผลลัพธ์ที่แสดงออกมา

5. ต่อไปจะเป็นการแก้ปัญหาเกมส์ เพื่อหาตารางทางเลือกที่ดีที่สุดของเกมส์ โดยเพิ่ม method id_solve เข้ามาก่อน แล้ว run id_solve เขียน code แบบนี้ครับ

from easyAI import id_solve

r, d, m = id_solve(GameOfBones, ai_depths=range(2,20), win_score=100)

ตัวแปลมีความหมาย ดังนี้

r มีค่าเท่ากับ 1 ผู้เล่นคนแรกจะชนะเสมอ, ถ้าเท่ากับ -1 ก็คือแพ้เสมอ

d คือ จำนวนการเดินรวมกันมากที่สุด หรือ น้อยกว่านั้น จนเกมส์จบ

m คือ ทางเลือกที่ผู้เล่นคนแรกควรจะเลือกเล่นในตาแรก จึงจะได้ผลลัพย์ออกมาตามค่าที่ id_solve ออกมา

จากผลลัพย์ ของ GameOfBones จะได้ ค่า r, d, m แบบนี้

จากผลเฉลยของเกมส์ จะได้ตารางผลลัพย์ของทางเลือกทั้งหมด เพื่อใช้ตัดสินใจเลือกทางเดินที่ดีที่สุดของเกมส์ เรียกว่า Transposition table (tt) ซึ่งเราจะนำไปใส่ให้ AI ใช้ต่อไป แบบนี้

tt = TT()

GameOfBones.ttentry = lambda game: game.pile # key สำหรับ ทางเลือกเล่นใน table

r, d, m = id_solve(GameOfBones, range(2, 20), win_score=100, tt=tt)

พอเรา run id_solve แล้วเราจะได้ table ทางเลือกที่ดีที่สุด โดยใช้ key ก็คือ pile หรือ กระดูกที่เหลือค้นใน tt แล้วจะได้ทางเลือกที่ดีที่สุดออกมา เมื่อนำไปใส่ AI แล้ว ให้มันเป็นผู้เล่นคนแรก ด้วยมันจึงไม่มีทางแพ้เลย

ลอง save file transposition table ออกมาดู ด้วยคำสั่งนี้

tt.to_json_file(‘game_of_bones.jason’)

พอเปิดดูจะเห็น โครงสร้างแบบนี้ (แนะนำให้เอาไป process json format ที่ https://jsonformatter.curiousconcept.com/ จะทำให้อ่านง่ายกว่า)

สำหรับเรียนรู้และพัฒนาต่อๆไป ก็ไปอ่านได้ที่นี่เลยครับ https://github.com/Zulko/easyAI

ก็ขอจบบล็อคไว้เท่านี้ หวังว่าจะมีประโยชน์ให้ผู้เริ่มต้นสนใจพัฒนา AI ต่อไปเพื่อใช้ประโยชน์ทำงานต่อไปครับ

ขอบคุณครับ

#:P

Microservices และ Agile manifesto

​ถ้าทีมงานพัฒนาซอฟแวร์ที่มีสถาปัตยกรรมแบบ Microservices แล้วก็หมายความว่า ทีมนั้นได้เดินตามหลักการทั้ง 12 และมีค่านิยมทั้ง 4 แห่งเอจไจล์ ไปโดยอัตโนมัติเป็นที่เรียบร้อยแล้ว… ไม่ต้องแนะนำอะไรเพิ่มเติมอีกต่อไป

  • #:P –

C#.NET Yield Return

สวัสดีครับ

ขอเขียนบล็อคแบบสั้นๆอธิบายเรื่อง yield return ซึ่งเป็นฟีเจอร์หนึ่ง ในภาษา C#

บ่อยครั้ง เราต้องทำงานกับ collection โดยการ loop เข้าไปใน collection หนึ่ง ตัวอย่าง collection ที่เป็นผลจาก function นี้


public static List GenOdd(int max)
{
     var results = new List();
     foreach (var n in Enumerable.Range(1, max))
    {
        if(n % 3 == 0) results.Add(n);
    }
    return results;
}

ตัวอย่าง ตอนใช้งาน ผมต้องการหยุด หรือ เมื่อเงื่อนไขสำเร็จ ตัวอย่างนี้ผมให้หยุด loop ที่ item == 11


static void Main(string[] args)
{

    foreach (var item in FindOddYield(1000000))
    {
        if (item == 11) break;
    }
}

ซึ่ง code อาจจะไม่เห็นว่า ผม loop[ ไปเท่าไร ผมก็เลยลองเพิ่มตัวแปร Spy เพื่อนับครั้งใน function FindOdd เพื่อจะได้รู้ว่ามัน loop ไปกี่ครั้ง code จะเปลี่ยนไปเล็กน้อย แบบนี้


int SpyCallCount = 0;
public List GenOdd(int max)
{
    var results = new List();
    foreach (var n in Enumerable.Range(1, max))
    {
        ++SpyCallCount;
        if(n % 3 == 0) results.Add(n);
    }
    return results;
}

ทีนี้ลอง ใช้งานดูครับ แต่ให้แสดง SpyCallCount ออกมาด้วย

static void Main(string[] args)
{
    foreach (var item in FindOddYield(1000000))
    {
        if (item == 11) break;

        Console.WriteLine(SpyCallCount); // print 1000000
    }
}

จะเห็นว่า SpyCallCount เท่ากับ 100000 ซึ่งเราต้องการหยุดที่ item เท่ากับ 11 เท่านั้น แต่ function ของเราไม่หยุดทำงาน

เรามาทำให้ function แบบนี้ดีขึ้นดีกว่า โดยการใช้ฟีเจอร์ yield return กลับไป แก้ไข function GenOdd แบบนี้

int SpyCallCount = 0;
public IEnumerable GenOdd(int max)
{
    foreach (var n in Enumerable.Range(1, max))
    {
        ++SpyCallCount;
        if(n % 3 == 0) yield return n;
    }
}

ทีนี้กลับไปลองใช้ใหม่ คุณจะเห็นผลลัพธ์ SpyCallCount เท่ากับ 11 แสดงให้เห็นว่า loop ลดลงเยอะเลยใช่มั้ยครับ นั่นคือลดการใช้ CPU time ลงไปตามงานที่เราต้องการ ไม่วนเล่นสูญเปล่าไปเฉยๆ

ก็ขอแชร์ไว้แค่นี้ ไปลองประยุกต์ใช้ yield return กันดู แนะนำมีประโยชน์ครับ

ขอบคุณครับ

#:P

#iterator-patterns, #yield-return

อธิบาย จิต-รูป แบบนักคอมพิวเตอร์

สวัสดีครับ

วันนี้ก็มาเพิ่มเติมการ อธิบาย นาม-รูป หรือ จิต-รูป ในแบบนัก Computer บล็อค ที่แล้ว อธิบายในแบบนักคณิตศาสตร์ ซึ่งแบบนักคณิตศาสตร์นั้น จะเป็นเชิงสัญลักษ์ ชนิดข้อมูลตัวเลข ที่สามารถวัดค่าได้

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

สรุปก็คือ นักคณิตศาสตร์ จะมองโลก แบบตัวเลขที่คำนวนค่าได้จาก สิ่งต่างๆ แล้วส่งผลต่อกัน ผ่านสมการ ที่ประกอบด้วยตัวดำเนินการ, ค่าคงที่ และตัวแปลที่เป็นตัวเลข สามารถคำนวนได้

แต่นักคอมพิวเตอร์ จะมองโลกแบบมีโครงสร้างลำดับขั้นตอนการทำงาน (process หรือ task) ร่วมกันของสิ่งต่างๆ แล้วแสดงออกมาเป็นผลลัพธ์ (Output) พอส่วนต่างๆรับมาจะเป็น (Input) แล้วมีการทำงาน ออกมาเป็นผลลัพธ์อีกแบบนี้ ต่อเนื่องไป

กำหนดให้ a คือ ส่วนการทำงานของ จิต, b คือ ส่วนการทำงาน ของ รูป และ เส้นเชื่อมแสดงถึง การรับส่งข้อมูล ระหว่างองค์ประกอบต่างๆ ดังรูป

อธิบายเพิ่มเติม เมื่อเปรียบเทียบกับ รูป-จิต ของเราแล้ว จะได้ว่า

  • จิต คือ คำสั่ง ประกอบด้วย เวทนา, สัญญา, สังขาร และ วิญญาณ
  • รูป คือ ร่างกาย ประกอบด้วย สมอง, กระดูก, เลือด, เนื้อ และ อวัยวะทุกส่วนที่ประกอบกันขึ้นเป็นสิ่งมีชีวิต

ถ้าเทียบกับ Computer จิต ก็คือ Code และ รูป ก็คือ Hardware และ Operation System (OS)
ส่วนที่สำคัญที่ประกอบเป็นรูป หรือ Computer ที่สมบูรณ์ ทำงานได้ ก็คือ OS
ซึ่ง OS จะทำหน้าที่เปลี่ยนคำสั่งที่เป็น Code ไปเป็นสิ่งที่เรียกว่างาน (task) แล้วแจกจ่ายไปยังส่วนต่างๆให้ประมวลผลงานได้นั่นก็คือ Hardware หรือ รูปกายส่วนต่างๆ ที่เรามีอยู่นั่นเอง

ตัว OS เป็น รูป มันจะอยู่หลังจาก จิต สร้าง Code แล้วมันจะเปลี่ยนเป็น task แล้วส่งต่อไปยัง รูปกาย ให้ประมวลผลออกมาเป็นการ แสดงออก, คิดปลุงแต่ง หรือ ทำงาน แสดงพฤติกรรม ต่างๆ ทั้งรู้ตัว และไม่รู้ตัว
ทำให้ ร่างกาย เราอยู่ได้ อย่างเช่น หายใจ มันเกิดเสมอ ทั้งๆที่ในเวลานอน หรือทำการงานอื่นๆ จิตเราไม่รู้สึกตัว ไม่ได้สร้าง Code เอง ให้หายใจ แต่มันเกิดได้เองอัตโนมัติ
นั่นแสดงว่า ตัว OS มันฝังอยู่ในรูปของเรา ตั้งแต่เกิดแล้ว และมันสร้าง Code เพื่อสั่งงานตัวมันได้เอง ให้อยู่รอดได้โดยอัตโนมัติ

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

แล้ว จิต สร้าง Code ได้อย่างไร จิต นั้นเกิดจากการทำงานกันของ เวทนา, สัญญา, สังขาร และ วิญญาณ ร่วมกัน หลังจากนั้น รูป จะแปลง Code เป็นงาน (task) แล้วส่งงานผ่านเส้นประสาททำให้ อวัยวะในร่างกาย ส่วนต่างๆทำงานตามที่ จิต ต้องการ

ตอนเราเกิด จิตยังไม่มี สัญญา (ความจำ ความรู้) เกี่ยวกับธรรมมะของพระพุทธเจ้า เรายังมี เวทนาอยู่ เช่น ทุกข์, สุข เฉยๆ เรายังมี วิญญาณ คือ การได้ยิน, มองเห็น, สัมผัส, ได้กลิ่น, รู้รส และเรายังมีสังขาร คือ การคิดปรุงแต่ง เช่น ชอบใจ, ไม่ชอบใจ, กังวล, โกรธ, อิจฉา อยู่ ทั้งหมดนี้ สร้าง Code แล้วจะถูกรูป แปลงออกมาให้เป็นการทำงานแล้วกายของเราจะประมวลผล แสดงออกมา เช่น หายใจ, หัวใจเต้น, ย่อยอาหาร, วิ่ง, กิน, พูด, ร้อง, แสดงหน้าตา รวมถึงการแสดงพฤติกรรมต่างๆ ออกมา ตามที่จิตสั่ง ทั้งหมดนี้เรียกว่า รูป

พอกายเราเติบโตขึ้นมีสัญญาเพิ่มขึ้น และมีความรู้ธรรมของพระพุทธเจ้า พอเราฝึกปฏิบัติธรรม ตามแนวทางของพระพุทธเจ้า มากขึ้น เราก็จะพัฒนาธรรมของเรา และสามารถแยกจิต กับ รูปออกมาได้ อย่างชัดเจนขึ้น ชัดเจนขึ้น เข้าใจมากยิ่งๆขึ้น ตามลำดับไป

a และ b ก็จะทำมุม 90′ และแยกอิสระกันออกไปเป็น นิพพาน

หวังว่าบล็อคนี้จะเป็นประโยชน์ ก็ขอให้สนุกกับการปฏิบัติ พัฒนาธรรมมะในตัวเราครับ

#:P