시작은 미약하였으나 , 그 끝은 창대하리라

[Python] MQTT를 이용한 이미지 전송 (raspberry pi 이용) 본문

프로그래밍/MQTT

[Python] MQTT를 이용한 이미지 전송 (raspberry pi 이용)

애플파ol 2022. 8. 29. 11:50

(들어가기전에 -> 이미지를 순수하게 전송할수는 없고 base64를 사용하여 디코드 인코드 하여야 합니다.)

 

 

1. Publisher Code

    간단 알고리즘 설명 : 사진찍음 →  찍은사진 1장 폴더에 저장→  폴더에 있는 파일 open함→  image파일을 인코딩함 →  인코딩된 것을 변수로 할당받음→ 할당받은 변수를 mqtt전송함. 

 

    주의 ※ base64사용시 약 130% 용량이 증가함. 게다가 이미지의 화소가 높으면 인코딩시

                str형식이던 byte형식이던 너무 길어짐으로 mqtt전송에 딜레이가 심하고 전송이 거의 안됨.

                  즉, 찍히는 사진 크기 줄여서 사진을 찍으세요

            상대경로 절대경로 설정 

            ※  다시한번 말한다 , QOS Level은 일치해야한다. (나는 rabbitmq써서 qos level 2지원안함...)

import paho.mqtt.client as mqtt
import base64
import os
import sys
import picamera
import datetime

from time import sleep 

savepath='/home/pi/project_3/picturesave'      # 사직이 찍히면 저장되는 '절대 경로'
readpath='./project_3/picturesave/'            # 찍힌 사진의 폴더 내용물 읽는 '상대경로'


# 저장된 폴더에 이미지 삭제하는 함수.
def delete_All_files(filePath):           
    if os.path.exists(filePath):
        for file in os.scandir(filePath):
            os.remove(file.path)

# 라즈베리파이 카메라 캡쳐 하는 함수            
def kickcameraCapture():
    with picamera.PiCamera() as camera:
        now=datetime.datetime.now()
        filename=now.strftime('%m%d-%H%M%S')
        camera.resolution = (320, 240)   # 촬영되는 사진 크기  (크면 인코딩시 이미지 길이 길어져서 에러남.)
        camera.framerate = 5
        camera.start_preview()
        sleep(1)                # 몇 초 간격으로 찍을지.
        camera.stop_preview()
        camera.capture(output=savepath+'/'+filename+'.jpg')

    
def on_connect(client, userdata, flags, rc):
    if rc == 0:
        print("connected OK")
    else:
        print("Bad connection Returned code=", rc)


def on_disconnect(client, userdata, flags, rc=0):
    print(str(rc))


def on_publish(client, userdata, mid):
    print("In on_pub callback mid= ", mid)



# 새로운 클라이언트 생성
client = mqtt.Client()
# 콜백 함수 설정 on_connect(브로커에 접속), on_disconnect(브로커에 접속중료), on_publish(메세지 발행)
client.on_connect = on_connect
client.on_disconnect = on_disconnect
client.on_publish = on_publish
# address : 111.111.111.119, port: 1883 에 연결
client.connect('111.111.111.119',20518)
client.loop_start()

#kickcameraCapture()

while True:
    delete_All_files(savepath)
    
    kickcameraCapture()
    
    
    file_directory=os.listdir(readpath)  
    myfile='filename.jpg'
    for filename in file_directory:
    
        if filename == myfile:
            continue

    
        with open(readpath+filename, 'rb') as img:
            image_read = img.read()
        img_encoding = base64.b64encode(image_read)           # bytes형식임.
        
        clean=img_encoding.decode('ascii')             # str형식으로 변환해줌.

        client.publish('image_4',clean,1)  # str형식으로 넘어감.(어떤 형식으로 넘기던 상관없더라)
                                           # 용량차이 미비함.   
                                            


client.loop_stop() 
# 연결 종료
client.disconnect()

   

 

 

- 1. 저장된 폴더에 이미지 삭제하는 함수.

       라즈베리파이 공식홈페이지에서 API를 읽어봤지만 그 어디에도.. 찍히면 폴더에 저장이 되는 기능밖에 가능하지

      않았다. 즉 사진을 찍고, 찍힌 사진을 바로 인코딩을 구현은 불가능 하다는 것이다.

    주의) filePath 는 '절대경로'임

   

    - 2. 라즈베리파이 카메라 캡쳐하는 함수.

          말그대로 함수 실행시 카메라가 작동하며 우리가 지정한 디렉토리에 파일이 '날짜명.jpg' 형식으로 저장됨.

 

    - 3. 폴더안에서 있는 파일 불러오는 코드.

          readpath(상대경로)에 있는 모든 파일들을 리스트 형식으로 불러옴. 불러온 리스트상의 파일들중에 ' .jpg ' 라고

          되어있는 확장자만 일치하면 continue 가 작동함.

    - 4.  ' bytes형식 ' or  ' str형식 ' 으로 인코딩

             bytes형식이던 str형식이던 어떤 형식으로 보내던 상관없다. 둘다 잘 동작함(물론 용량을 찍어보니 bytes가 매우

             조금 더 라이트 했다)

 

2. Subscriber Code

   ->그냥 똑같다...

 

import paho.mqtt.client as mqtt

def on_connect(client, userdata, flags, rc):
    if rc == 0:
        print("connected OK")
    else:
        print("Bad connection Returned code=", rc)

def on_disconnect(client, userdata, flags, rc=0):
    print(str(rc))

def on_subscribe(client, userdata, mid, granted_qos):
    print("subscribed: " + str(mid) + " " + str(granted_qos))

def on_message(client, userdata, msg):
    
    print(str(msg.payload.decode("utf-8")))



# 새로운 클라이언트 생성
client = mqtt.Client()
# 콜백 함수 설정 on_connect(브로커에 접속), on_disconnect(브로커에 접속중료), on_subscribe(topic 구독),
# on_message(발행된 메세지가 들어왔을 때)
client.on_connect = on_connect
client.on_disconnect = on_disconnect
client.on_subscribe = on_subscribe
client.on_message = on_message
# address : localhost, port: 20518 에 연결
client.connect('localhost', 20518)

client.subscribe('image_4',1)    ## QOS 자체설정 주의 0 or 1
client.loop_forever()

아래와 같은 출력물이 터미널에 나오게 된다....(더 긴데 짤림..)

(뭔가 무서운데?..)

 

 

그렇다면 이제 어?..나는 이미지가 필요한건데..

결국..-> 이받은 데이터를 인코딩을 하면 되는것이다. 

 

 

import base64
from PIL import Image
from io image BytesIO
import matplotlib.pyplot as plt

img = Image.open(BytesIO(base64.b64decode(base64_string)))  # 디코딩한 string이 들어가면 됨.
plt.imshow(img)

 

 

-------------------- -------------------- -------------------- -------------------- -------------------- -------------------- -------------------- -------------------- -------------------- -------------------- -------------------- -------------------- -------------------- -------------------- -------------------- -------------------- -------------------- -------------------- -------------------- -------------------- -------------------- -------------------- -------------------- ---

 

DB를 사용하는 방법은 이전 게시물(Gyro) 에서 설명을 하였다..(DB부분 빼버림..) -> 링크: https://put-idea.tistory.com/43

 

[Python] MQTT를 이용한 자이로센서 값 전송 (raspberry pi 이용)

(들어가기전에-> 1. 자이로센서는 MPU6050 사용하였습니다 ) (한국인이 좋아하는 결론부터 말하기) 자이로센서 값(데이터)를 전송하는 코드. ''' Read Gyro and Accelerometer by Interfacing Raspberry Pi with M..

put-idea.tistory.com

 

Comments