รู้จักและสมัครเป็นสมาชิก NETPIE
ก่อนจะใช้บริการต้องผ่านขั้นตอนการลงทะเบียนก่อน ข้อมูลตรงนี้ผมจะไม่กล่าวถึง แต่จะละให้ผู้สนใจและยังไม่ทราบข้อมูลไปดูกันเองที่ https://netpie.io/ ครับ การลงทะเบียนขอใช้บริการมีทั้งเสียงเงินและไม่เสียเงินครับ เลือกเอาตามชอบสร้าง Application
หลังจากผ่านขั้นตอนการลงทะเบียนแล้ว ก่อนจะเริ่มต้นทำอะไร เราต้องกำหนดพื้นที่ทำงานสมมุติขึ้นมาก่อนใน NETPIE เขาเรียกว่า Application สิ่งที่ได้จากขั้นตอนนี้คือ- Application key และ
- Secret Key
ภาพตัวอย่างที่เราจะได้เห็นหลังการสร้าง Application แล้ว |
keys ที่เราต้องนำไปใช้ในการสื่อสารอุปกรณ์ของเรากับ NETPIE |
ติดตั้ง Application Interface
ปัจจุบันทาง NETPIE ได้เตรียม API การสื่อสารไว้ 2 รูปแบบคือ (https://netpie.io/#services) REST, Pub-Sub pattern ผมเลือกใช้ Pub-Sub pattern ครับ เพราะใช้งานกับ Python ได้สะดวก ซึ่งต้องทำการติดตั้งก่อนโดย- ใช้คำสั่ง $sudo pip install microgear หรือ
- ดาวน์โหลด source code มาไว้ก่อนแล้วค่อยติดตั้ง ก็ได้ เขาก็อธิบายขั้นตอนไว้อีกเช่นเคย
เขียน Python Code
หลังจากลงทะเบียนเข้าใช้งาน สร้าง Application ได้ Key มาและติดตั้ง API แล้ว ขั้นตอนต่อไปก็คือการสร้าง Python code ครับ หลักการในการส่งข้อมูลภาพนั้นไม่มีอะไรซับซ้อน หากท่านดูจากเอกสารประกอบการใช้งานภาษา Python จะพบว่าทาง NETPIE อนุญาตให้ส่งข้อมูลระหว่างอุปกรณ์ผ่าน Pub-Sub Pattern ในรูปแบบของ String เท่านั้น แต่ไม่ได้กำหนดขนาดของข้อมูลไว้ ดังนั้นหากเราเปลี่ยนข้อมูล Binary ของภาพถ่ายให้เป็น String ก่อนแล้วทำการส่งออกไปก็ย่อมจะทำได้ ซึ่งในภาษา Python มี library ชื่อ base64 ช่วยเรื่องนี้อยู่แล้วส่วนที่ 1
import microgear.client as netpie
import base64, zlib, time
ส่วนนี้เป็นการนำเข้า Library ที่จำเป็นต้องใช้งาน จะเห็นว่านอกจาก microgear แล้วก็มีการนำ base64 เข้ามาร่วมใช้งานด้วยส่วนที่ 2
key = '[your key]'
secret = '[your secret key]'
app = '[your application name]'
netpie.create(key,secret,app,{'debugmode': True})
กำหนด Key ต่าง ๆ ที่ NETPIE กำหนดให้มี
netpie.create(key,secret,app,{'debugmode': True})
สร้างตัวแปรที่รองรับการเชื่อมต่อระหว่างอุปกรณ์ของเรากับ NETPIEส่วนที่ 3
def connection():
print("Connected")
def subscription(topic,msg):
global running
#print(topic+" : "+msg)
decode_base64(msg)
running = False
def callback_error(msg) :
print(msg)
def callback_reject(msg) :
print msg
print "Script exited"
exit(0)
เป็นการประกาศ callback function ตามคู่มือของ NETPIE ในโครงสร้างของระบบงานนี้จะแบ่ง party ในระบบออกเป็นสองส่วนคือ Sender และ Receiver จึงมีการเพิ่มเติม code ใน subscription เป็นSender :
def subscription(topic,msg):
global ready_to_send
if not ready_to_send :
if msg =='iamok':
ready_to_send = True
print "Reciever is ready"
จะมีการตรวจสอบความพร้อมของฝั่งผู้รับก่อนหากยังไม่มีคำตอบก็จะยังคงสถานะ False ไว้กับตัวแปร ready_to_send
ส่วนที่ 4
Sender :
def encode_base64(img_data):
encoded = None
try:
#compress it first.
compressed_data = zlib.compress(img_data.getvalue(),9)
#encode it to base64 string
encoded = base64.b64encode(compressed_data)
except:
pass
return encoded
หน้าที่ของฟังก์ชั่น encode_base64() คือการเปลี่ยนข้อมูลภาพที่ได้จากกล้องให้อยู่ในรูปแบบ base64 string เพื่อให้สามารถส่งข้อมูลผ่าน NETPIE ได้ แต่เนื่องจากการเปลี่ยนจากข้อมูล binary ไปเป็น string จะทำให้ขนาดของข้อมูลโตขึ้นมาหลายเท่าตัว ดังนั้นการเพิ่มเรื่อง compression data เข้าไปก็จะช่วยลดเรื่องขนาดของ bandwidth ลงไปได้เยอะ ที่นี้ขอให้สังเกตุขั้นตอนนะครับ ว่าผมใช้การ compress ข้อมูลภาพก่อนทำการ encode ให้เป็น base64 string ทั้งนี้ก็เพราะข้อกำหนดของ NETPIE ที่ให้เราต้องส่งข้อมูลแบบ string เราจึงต้องสร้างข้อมูล string ในขั้นตอนสุดท้ายก่อนส่งไปยัง NETPIE นั่นเอง
Receiver :
def decode_base64(compressed_b64str=None,save_to_file=None):
try :
#firstly, decode it
decoded = base64.decodestring(compressed_b64str)
decompr = zlib.decompress(decoded)
#save it if is needed.
if save_to_file is not None:
with open(save_to_file,"wb") as fh:
fh.write(decompr)
else:
#just display on screen
w,h = 640,480
image = Image.open(BytesIO(decompr))
image.show()
except:
pass
หน้าที่ของฟังก์ชั่น dencode_base64() ทำงานตรงข้ามกับ encode_base64() เพื่อนำ base64 string กลับมาอยู่ในรูปแบบ binary เพื่อจะได้นำไปประมวลผลต่อได้
ส่วนที่ 5
เป็นส่วนที่ทำหน้าที่ถ่ายภาพนิ่งด้วย Raspberry Pi Camera Module ซึ่งจะมีอยู่เฉพาะทาง Sender เท่านั้น
def snap():
global camera
str_img = BytesIO()
camera.start_preview()
time.sleep(2)
camera.capture(str_img,format='jpeg')
camera.stop_preview()
str_img.seek(0)
return str_img
code ในส่วนนี้ได้มีการนำเอา BytesIO ซึ่งเป็น in memory buffer แบบหนึ่ง มีวิธีการใช้งานคล้ายกับ File ทั่วไปเพียงแต่จะอยู่ในหน่วยความจำแทนที่จะอยู่ใน storage media ในชุดคำสั่ง camera.capture(str_img,format='jpeg') เป็นการสั่งให้นำข้อมูลภาพจากกล้องไปเก็บไว้ในตัวแปร str_img ซึ่งเป็น BytesIO ในรูปแบบการ encode แบบ Jpeg ครับ และคำสั่ง str_img.seek(0) เป็นการสั่งให้เลื่อน pointer กลับไปที่ Byte แรก ตรงนี้ก็เพื่อให้มั่นใจว่าข้อมูลที่เราจะจัดส่งออกไปนั้นได้ถูกส่งไปออกไปตั้งแต่ Byte แรกนั่นเอง
ฉบับสมบูรณ์
Sender : (Raspberry Pi)
import microgear.client as netpie
import time
import base64
from picamera import PiCamera
from io import BytesIO
import zlib
key = 'xxxxxxxx'
secret = 'xxxxxxxxxxxx'
app = 'N3AFarm01'
netpie.create(key,secret,app,{'debugmode': True})
connected = False
def connection():
global connected
connected = True
print("Connected")
def subscription(topic,msg):
global this_role,ready_to_send
if this_role == 'reciever' :
decode_base64(msg,None) # don't need to save on disk
running=False
else :
if not ready_to_send :
if msg =='iamok':
ready_to_send = True
print "Reciever is ready"
def callback_error(msg) :
print(msg)
def callback_reject(msg) :
print (msg)
print ("Script exited")
exit(0)
def encode_base64(img_data):
encoded = None
try:
#compress it first.
compressed_data = zlib.compress(img_data.getvalue(),9)
#encode it to base64 string
encoded = base64.b64encode(compressed_data)
except:
pass
return encoded
def decode_base64(compressed_b64str=None,save_to_file=None):
try :
#firstly, decode it
decoded = base64.decodestring(compressed_b64str)
decompr = zlib.decompress(decoded)
#save it if is needed.
if save_to_file is not None:
with open(save_to_file,"wb") as fh:
fh.write(decompr)
else:
#just display on screen
w,h = 640,480
image = Image.fromstring('RGB',(w,h),decompr)
image.show()
except:
pass
def snap():
global camera
str_img = BytesIO()
camera.start_preview()
time.sleep(2)
camera.capture(str_img,format='jpeg')
camera.stop_preview()
str_img.seek(0)
return str_img
camera = PiCamera()
camera.resolution=(640,480)
this_name = 'n3a2'
those_name = 'n3a1'
this_role = 'sender'
running = True
ready_to_send = False
netpie.setname(this_name)
netpie.on_reject = callback_reject
netpie.on_connect = connection
netpie.on_message = subscription
netpie.on_error = callback_error
netpie.subscribe("/test")
netpie.connect(False)
if this_role=='sender':
while not ready_to_send :
netpie.chat(those_name,'ruok')
time.sleep(2)
snap_shot = snap()
b64 = encode_base64(snap_shot)
netpie.chat(those_name,b64)
time.sleep(2)
else :
while running:
pass
Receiver :(ใครก็ได้)
import microgear.client as netpie
import time
import base64
from PIL import Image
from io import BytesIO
import zlib
key = 'xxxxxxxx'
secret = 'xxxxxxxxxxx'
app = 'N3AFarm01'
netpie.create(key,secret,app,{'debugmode': True})
connected = False
def connection():
global connected
connected = True
print("Connected")
def subscription(topic,msg):
global this_role,ready_to_receive
if this_role == 'receiver' :
if not ready_to_receive :
if msg=='ruok' :
netpie.chat(those_name,'iamok')
ready_to_receive = True
else :
print "recieving image data."
decode_base64(msg,None) # don't need to save on disk
print "process is done"
else :
print(topic+":"+msg)
def callback_error(msg) :
print(msg)
def callback_reject(msg) :
print (msg)
print ("Script exited")
exit(0)
def encode_base64(img_data):
encoded = None
try:
#compress it first.
compressed_data = zlib.compress(img_data.getvalue(),9)
#encode it to base64 string
encoded = base64.b64encode(compressed_data)
except:
pass
return encoded
def decode_base64(compressed_b64str=None,save_to_file=None):
try :
#firstly, decode it
decoded = base64.decodestring(compressed_b64str)
decompr = zlib.decompress(decoded)
#save it if is needed.
if save_to_file is not None:
with open(save_to_file,"wb") as fh:
fh.write(decompr)
else:
#just display on screen
w,h = 640,480
image = Image.open(BytesIO(decompr))
image.show()
except:
pass
this_name = 'n3a1'
those_name = 'n3a2'
this_role = 'receiver'
running = True
ready_to_receive = False
netpie.setname(this_name)
netpie.on_reject = callback_reject
netpie.on_connect = connection
netpie.on_message = subscription
netpie.on_error = callback_error
netpie.subscribe("/test")
netpie.connect(False)
try :
while running:
pass
except KeyboardInterrupt :
running=False
Download Code at Github
ทดสอบกัน
ผลการทดสอบออกมาเป็นที่น่าพอใจครับ ผมใช้การส่งภาพจาก Raspberry Pi B3 ไปยังเครื่องคอมพิวเตอร์ Notebook โดยให้ทั้งสองใช้บริการ internet provider คนละเจ้ากัน รวมเวลาที่ใช้ตั้งแต่เริ่มต้นจนเห็นภาพทั้งหมดก็ประมาณ 3 วินาที ถือว่าดีมากทีเดียวครับภาพฝั่งผู้รับซึ่งใช้คอมพิวเตอร์ Notebook รับภาพจากผู้ส่ง (Raspberry Pi) ขนาดของภาพคือ 640 x 480 pixels |
แนวทางการพัฒนาต่อไป
เท่าที่ทำมานี่ก็เพียงการเริ่มต้นและเพื่อทดสอบว่าทำได้หรือไม่ การพัฒนาต่อยอดต้องมีต่อไป เช่น การทำให้ระบบทำงานโต้ตอบกันระหว่างอุปกรณ์แบบอัตโนมัติ, ใช้สร้าง data log ที่เป็น image หรือแม้แต่เรื่องทางสันทนาการ ฯล สำหรับแนวคิดที่จะทำเป็นภาพต่อเนื่องแบบวิดีโอนั้นอาจไม่ค่อยจะดีสักเท่าไหร่ เพราะจะมี overhead ในระบบสูงมาก ครับเอกสารอ่านเพิ่มเติม
1. https://picamera.readthedocs.io/en/release-1.12/recipes1.html2. https://docs.python.org/3/library/zlib.html
3. https://docs.python.org/3/library/base64.html?highlight=base64#module-base64
4. http://effbot.org/imagingbook/image.htm
Sign up here with your email
17 ความคิดเห็น
Write ความคิดเห็นต้องแบ่ง เป็น 2ไฟล์ หรอ ครับ
Replyแล้วโค้ด ตรงผู้รับอะ ครับใช้ ยังงัย คครับ
Replyไม่แน่ใจในคำถามที่ว่า "ต้องแบ่งเป็น 2 ไฟล์" ถ้าหมายถึง Python code แล้วที่ต้องแบ่งเป็นสองไฟล์ เพื่อให้ทำงานแยกกันระหว่าง ผู้ส่ง กับ ผู้รับ ในกรณีที่ต้องการให้ทำงานแบบสองทาง ก็สามารถประยกุกต์นำทั้งสองไฟล์มารวมกันได้
Replyบันทึก code ไว้ในไฟล์ ที่มีนามสกุลเป็น .py แล้วใช้คำสั่ง python [ชื่อไฟล์ที่ตั้งไว้].py
Replyใน receiver.py ของ เครื่องรับผมรันจาก Windows แล้วขึ้น Error ว่า
ReplyTraceback (most recent call last):
File "receiver.py", line 4, in
from PIL import Image
ImportError: No module named 'PIL'
ผมต้องทำอย่า่งไรครับ ขอบคุณครับ
การใช้งาน PIL หรือ Pillow บน Windows คงต้องออกแรงนิดหน่อย เพราะไม่มีlibrary ที่ออกอย่างเป็นทางการ แต่ก็มีข้อมูลให้ทำตามได้ เช่น https://pillow.readthedocs.io/en/latest/installation.html หรือ http://christianakesson.com/compiling-pil/
Replyในกรณีที่ไม่ต้องการติดตั้ง PIL ก็มีทางเลือกให้ทำการเขียนข้อมูลเก็บไว้เป็นไฟล์ก่อนได้ เมื่อมีไฟล์แล้วแล้วค่อยใช้เครื่องมืออื่นที่มีอยู่แล้วทำการต่อก็ได้ ในการทำงานเกี่ยวกับรูปภาพในภาษา Python ก็มีอีกหลายตัวนอกจาก PIL เช่น OpenCV, Matplotlib,ฯล ลองพิจารณาดู ครับ
ReplyRPi3(ส่ง) ->>>>> Computer(ubuntu)รับ
Replyมันแปลงรูปไม่ออกครับ
เยี่ยมเลยครับ
Replyผมลองทำตามแล้วครับ คือภาพมัน ขึ้นแสดงที่ด้านผู้ส่ง ประมาณ 2วิ จากนั้นด้านผู่รับ จะขึ้นข้อความ
Replyrecieving image data
process is done
คือมันไม่แสดงภาพเลยครับ เกิดจากอะไรหรือครับ
ลองกำหนดให้ฝั่งผู้รับบันทึกข้อมูลลงไฟล์ โดยกำหนด save_to_file = True ใน
Replydef decode_base64(compressed_b64str=None,save_to_file=None)
....
เพราะเป็นไปได้ว่าในบางสภาพแวดล้อมการแสดงภาพด้วย PIL ไม่สามารถทำได้
การบันทึกลงไฟล์จะทำให้เราสามารถนำภาพไปแสดงในสภาพแวดล้อมหรือ Image Viewer อื่นได้
ขอบคุณคับ ตอบกลับไวมากคับ ผมลองทำตามที่พี่เเนะนำ ก็ยังคงเป็นเหมือนเดิมคับ
Replyเก็บข้อมูลภาพไม่ได้ ?
Replyเปลี่ยน code ใน def decode_base64(...)
except:
pass
เป็น
except Exception as err:
print(err)
เพื่อให้แสดง error message ออกมา อาจจะรู้ว่าต้นเหตุของปัญหา ครับ
ผมทดลองทำตามโดยให้ฝ่ายส่งเป็นบอร์ด Raspberry Pi บอร์ดที่ 1 และฝ่ายรับเป็นบอร์ด Raspberry Pi บอร์ดที่ 2 ได้ผลเหมือนของคุณเกรียงไกรเลยครับ
Replyลองทำตามแล้วค่ะ รันpython ผ่านแต่ภาพไม่ขึ้นเลยค่ะแล้วเราจะดูไฟล์ภาพได้จากไหนเหรอค่ะ
Reply>>ขอบคุณค่ะ
ของผมมันขึ้นว่า
Replyrecieving image data.
process is done
สลับกันไปเรื่อยๆ ไม่แสดงภาพมาเลย เป็นเพราะอะไรหรอครับ
ConversionConversion EmoticonEmoticon