KkoRack 화성탐사
한국형 퍼서비어런스 꼬꼬락 화성 착륙
https://youtu.be/1ULRCABQgUI?si=RDhXUKBAGHkVIwxe
- 실행 동영상
- Main
- StepMotor
- VideoStream
- Noise Detector
- Listen Speech
- GPIO
- Etc
- StreamingVideo
- StreamingSound
- TensorFlow OpenCV Slack Flack
실행 동영상
실행 동영상
Main
KKorack GPIO setting.pptx
startKK.sh
-
#!/bin/bash echo echo '##### START KKo KKo Rack #####' echo wpa_cli -i wlan0 status rm /home/pi/work/opencv/nohup.out cd /home/pi/work/opencv/ #nohup python3 /home/pi/work/opencv/KKserver.py & sleep 3 #tail -f /home/pi/work/opencv/nohup.out echo echo '##### STARTed KKo KKo Rack #####' echo
stopKK.sh
-
#!/bin/bash echo echo '##### STOP KKo KKo Rack #####' echo ps -ef | grep KKserver.py | grep -v grep KILLPID=`ps -ef | grep KKserver.py | grep -v grep | awk '{print($2)}'` echo "Process = " $KILLPID kill -9 $KILLPID echo echo '##### STOPed KKo KKo Rack #####' echo
statusKK.sh
-
#!/bin/bash echo echo '##### STATUS KKo KKo Rack #####' echo ps -ef | grep KKserver.py | grep -v grep KKORACKPID=`ps -ef | grep KKserver.py | grep -v grep | awk '{print($2)}'` echo "Process = " $KKORACKPID echo echo '##### STATUS KKo KKo Rack #####' echo
KKserver.py
-
from flask import Flask, render_template, send_from_directory, Response, send_file, request, redirect, url_for from flask_socketio import SocketIO from pathlib import Path from capture import capture_and_save from usbwebcamera import UsbWebCamera from piwebcamera import PiWebCamera import argparse, logging, logging.config, conf import os from gpioControl import GpioControl from listenSpeech import ListenSpeech from urllib.parse import parse_qs from power import PowerStatus from noise_detector import Noise_Detector app = Flask(__name__) socketio = SocketIO(app) archive_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'archive') gpio = GpioControl() listenspeech = ListenSpeech() power = PowerStatus() logging.config.dictConfig(conf.dictConfig) logger = logging.getLogger(__name__) usbcamera = UsbWebCamera(video_source=1, do_display=False) usbcamera.start() picamera = PiWebCamera(video_source=0, do_display=False) picamera.start() # Setup detectors nd = Noise_Detector() nd.start() @app.after_request def add_header(r): """ Add headers to both force latest IE rendering or Chrome Frame, and also to cache the rendered page for 10 minutes """ r.headers["Cache-Control"] = "no-cache, no-store, must-revalidate" r.headers["Pragma"] = "no-cache" r.headers["Expires"] = "0" r.headers["Cache-Control"] = "public, max-age=0" return r @app.route("/") def entrypoint1(): logger.debug("Requested /") return render_template("index.html") @app.route("/index.html") def entrypoint2(): logger.debug("Requested /") return render_template("index.html") @app.route("/usbcapture") def captureusb(): logger.debug("Requested USB capture") im = usbcamera.get_frame(_bytes=False) capture_and_save(im) return render_template("send_to_init.html") @app.route("/picapture") def capturepi(): logger.debug("Requested PICAM capture") im = picamera.get_frame(_bytes=False) capture_and_save(im) return render_template("send_to_init.html") @app.route("/video/last_video") def last_video(): logger.debug("Requested last video") for filename in sorted(os.listdir(archive_path), reverse=True): if not filename.startswith('.'): type = get_type(filename) if type == "video": return send_from_directory(archive_path, filename) @app.route("/audio/last_audio") def last_wav(): logger.debug("Requested last video") for filename in sorted(os.listdir(archive_path), reverse=True): if not filename.startswith('.'): type = get_type(filename) if type == "audio": return send_from_directory(archive_path, filename) ''' ##### Achive File Section ##### ''' @app.route('/archive') def archive(): return render_template('archive.html') def get_type(filename): name, extension = os.path.splitext(filename) return 'video' if extension == '.mp4' else 'audio' if extension == '.wav' else 'audio' if extension == '.mp3' else 'photo' @app.route('/archive/<string:filename>') def archive_item(filename): name, extension = os.path.splitext(filename) type = get_type(filename) return render_template('record.html', filename=filename, type=type) @app.route('/archive/delete/<string:filename>') def archive_delete(filename): os.remove(archive_path + "/" + filename) return redirect(url_for('archive')) @app.route('/archive/play/<string:filename>') def archive_play(filename): return send_file('archive/' + filename) def get_records(): records = [] for filename in sorted(os.listdir(archive_path), reverse=True): if not filename.startswith('.'): type = get_type(filename) size = byte_to_mb(os.path.getsize(archive_path + "/" + filename)) record = {"filename": filename, 'size': size, 'type': type} records.append(record) return records def byte_to_mb(byte): mb = "{:.2f}".format(byte / 1024 / 1024) return str(mb) + " MB" app.jinja_env.globals.update(get_records=get_records) ''' ##### Achive File Section ##### ''' def genpi(picamera): logger.debug("Starting PI stream") while True: frame = picamera.get_frame() yield (b'--frame\r\n' b'Content-Type: image/png\r\n\r\n' + frame + b'\r\n') @app.route("/bothstream") def bothstream_page(): logger.debug("Requested stream page") return render_template("bothstream.html") @app.route("/usbstream") def usbstream_page(): logger.debug("Requested stream page") return render_template("usbstream.html") @app.route("/pistream") def pistream_page(): logger.debug("Requested stream page") return render_template("pistream.html") @app.route("/video_usb_feed") def video_usb_feed(): # return Response(genusb(usbcamera), # mimetype="multipart/x-mixed-replace; boundary=frame") def gen_video(): while True: frame = usbcamera.get_frame() if frame is not None: yield (b'--frame\r\n' b'Content-Type: image/jpeg\r\n\r\n' + frame + b'\r\n\r\n') return Response( gen_video(), mimetype='multipart/x-mixed-replace; boundary=frame' ) @app.route("/video_pi_feed") def video_pi_feed(): return Response(genpi(picamera), mimetype="multipart/x-mixed-replace; boundary=frame") @app.route("/temperature") def temperature(): content = os.popen("vcgencmd measure_temp").readline() content = content.replace("temp=", "") powerstatus = power.getPowerStatus() return Response(content+"["+powerstatus+"]", mimetype='text/xml') ''' ##### Control Camera Section ##### ''' @app.route("/msg", methods=['GET', 'POST']) def msg(): if request.method == 'POST': message = request.form['textinput'] if not message : reString = "No received message" else : reString = listenspeech.speech(message) print("Get Message = " + reString) return reString @app.route("/init") def init(): reString = "Camera position init : " + gpio.initMotorPosition() print("Move init ...") return Response(reString, mimetype='text/html') @app.route("/up") def up(): reString = gpio.moveUp() print("Move Up ...") return Response(reString, mimetype='text/html') @app.route("/down") def down(): reString = gpio.moveDown() print("Move Down ...") return Response(reString, mimetype='text/html') @app.route("/left") def left(): reString = gpio.moveLeft() print("Move Left ...") return Response(reString, mimetype='text/html') @app.route("/right") def right(): reString = gpio.moveRight() print("Move Right ...") return Response(reString, mimetype='text/html') ''' ##### Caterpillar Tracks ##### ''' @app.route("/forward", methods=['GET', 'POST']) def forward(): if request.method == 'POST': moveOrder = request.form['moveCnt'] if not moveOrder : reString = "Do not move" else : reString = gpio.goForward(moveOrder) print("Go Forward ...("+moveOrder+")") return reString @app.route("/backward", methods=['GET', 'POST']) def backward(): if request.method == 'POST': moveOrder = request.form['moveCnt'] if not moveOrder : reString = "Do not move" else : reString = gpio.goBackward(moveOrder) print("Go Backward ...("+moveOrder+")") return reString @app.route("/turnleftback", methods=['GET', 'POST']) def turnleftback(): if request.method == 'POST': moveOrder = request.form['moveCnt'] if not moveOrder : reString = "Do not move" else : reString = gpio.goTurnleftback(moveOrder) print("Go Turn Left back...("+moveOrder+")") return reString @app.route("/turnrightback", methods=['GET', 'POST']) def turnrightback(): if request.method == 'POST': moveOrder = request.form['moveCnt'] if not moveOrder : reString = "Do not move" else : reString = gpio.goTurnrightback(moveOrder) print("Go Turn Right back...("+moveOrder+")") return reString @app.route("/turnleftforward", methods=['GET', 'POST']) def turnleftforward(): if request.method == 'POST': moveOrder = request.form['moveCnt'] if not moveOrder : reString = "Do not move" else : reString = gpio.goTurnleftforward(moveOrder) print("Go Turn Left forward...("+moveOrder+")") return reString @app.route("/turnrightforward", methods=['GET', 'POST']) def turnrightforward(): if request.method == 'POST': moveOrder = request.form['moveCnt'] if not moveOrder : reString = "Do not move" else : reString = gpio.goTurnrightforward(moveOrder) print("Go Turn Right forward...("+moveOrder+")") return reString @app.route("/favorit.ico") def favorit_ico(): logger.debug("Requested favorit.ico image") filename = "favorit.ico" return send_file(filename) if __name__=="__main__": # socketio.run(app,host="0.0.0.0",port="3005",threaded=True) parser = argparse.ArgumentParser() parser.add_argument('-p','--port',type=int,default=8081, help="Running port") parser.add_argument("-H","--host",type=str,default='0.0.0.0', help="Address to broadcast") args = parser.parse_args() logger.debug("Starting server") # When it change , it was reloaded #app.run(host=args.host,port=args.port) #app.run(host=args.host,port=args.port,debug=True,use_reloader=False) socketio.run(app, log_output=True, host='0.0.0.0', port=8081, debug=True, use_reloader=False)
StepMotor
#!/usr/bin/python
# -*- coding: UTF-8 -*-
import RPi.GPIO as gpio
from time import sleep
gpio.setmode(gpio.BOARD)
gpio.setup(12, gpio.OUT)
gpio.setup(18, gpio.OUT)
p1 = gpio.PWM(12, 50) # 50 Hz
p2 = gpio.PWM(18, 50) # 50 Hz
p1.start(0)
p2.start(0)
# 0도
#p.ChangeDutyCycle(3)
#sleep(1)
# 180도
#p.ChangeDutyCycle(12)
#sleep(1)
# 90도
p1.ChangeDutyCycle(7.5)
p2.ChangeDutyCycle(7.5)
p1.ChangeDutyCycle(0)
p2.ChangeDutyCycle(0)
exitLoop = True
while(exitLoop):
val = input("input(3~7.5~12), #좌우(6), #상하(6) = ")
if val == "-1":
exitLoop = False
else :
p1.ChangeDutyCycle(float(val.split(',')[0]))
p2.ChangeDutyCycle(float(val.split(',')[1]))
sleep(0.2)
p1.ChangeDutyCycle(0)
p2.ChangeDutyCycle(0)
p1.stop()
p2.stop()
gpio.cleanup()
VideoStream
usbwebcamera.py
import os
import sys
import time
import math
import getopt
import numpy as np
import cv2
import threading
import subprocess
from collections import deque
from lock_manager import Lock_Manager
from util import Util
class UsbWebCamera(threading.Thread):
def __init__(self, video_source=1, source=None, do_record=True, do_display=True, do_add_contours=True, do_add_target=False):
threading.Thread.__init__(self)
self.name = self.__class__.__name__
self.archive = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'archive')
self.writer = None
self.current_frame = None
self.codec = cv2.VideoWriter_fourcc('M','J', 'P', 'G')
self.OBSERVER_LENGTH = 5 # Time in seconds to be observed for motion
self.threshold = 15
self.CAMERA_SOURCE = video_source
self.REMAIN_RECORDING_FILES = 3 # 10이상 부터 삭제 후 저장
self.do_display = do_display
self.do_record = do_record
self.do_add_contours = do_add_contours
self.do_add_target = do_add_target
self.current_file = None
self.source = cv2.VideoCapture(source) if source is not None else self.init_camera()
self.fps = self.find_fps(self.source)
self.height, self.width = self.get_dimensions(self.source)
Util.log(self.name, "Initializing usb camera class with video_source=" + str(self.CAMERA_SOURCE))
Util.log(self.name,"width: {"+str(self.width)+"}, height : {"+str(self.height)+"}")
self.lock_manager = Lock_Manager("motion")
def __del__(self):
# Release camera
self.source.release()
# Close all windows
cv2.destroyAllWindows()
# Remove lock if exists
self.lock_manager.remove()
def get_frame(self):
"""
Return the current frame
@return bytes
"""
return self.frame_to_jpg(self.current_frame) if self.current_frame is not None else None
def frame_to_jpg(self, frame):
"""
Convert video frame to jpg
@param array frame
@return bytes
"""
ret, jpeg = cv2.imencode('.jpg', self.current_frame)
return jpeg.tobytes()
def get_dimensions(self, source):
"""
Determine height and width of the video source
@return tuple(int, int)
"""
frame = cv2.cvtColor(source.read()[1],cv2.COLOR_RGB2GRAY)
return frame.shape[0: 2]
def find_fps(self, source):
"""
Determine frames per second of the video source
@param video source
@return int
"""
Util.log(self.name, "Determining FPS...")
# How many frames to capture
num_frames = 120
# Start time
start = time.time()
# Grab a few frames
for i in range(0, num_frames):
ret, frame = source.read()
# End time
end = time.time()
# Calculate frames per second
fps = int(math.floor(num_frames / (end - start)))
Util.log(self.name, "Setting FPS to " + str(fps))
return fps
def init_camera(self):
"""
Start the camera
@return cv2.VideoCapture
"""
# Init camera
camera = cv2.VideoCapture(self.CAMERA_SOURCE)
#camera.set(3, 320)
#camera.set(4, 240)
# Wait half a second for light adjustment
time.sleep(0.5)
return camera
def start_recording(self):
"""
Setup the recorder
"""
self.current_file = self.archive + "/" + self.detected_at + "-usb.avi"
Util.log(self.name, "Motion detected! Recording...")
# Set path and FPS
self.writer = cv2.VideoWriter(self.current_file, self.codec, self.fps, (self.width, self.height))
def stop_recording(self):
"""
Reset values to default
"""
self.writer = None
self.current_file = None
self.detected_at = None
def convert_to_mp4(self, path):
"""
Convert video file to mp4 using ffmpeg
@param string path
"""
try:
Util.log(self.name, "Converting video...")
destination = os.path.splitext(path)[0] + '.mp4'
cmd = 'ffmpeg -i "{}" "{}" 2> /dev/null && rm "{}"'.format(path, destination, path)
#cmd = 'for i in ' + self.archive + '/*.avi; do ffmpeg -i "$i" "${i%.*}.mp4" 2> /dev/null && rm "$i"; done'
p = subprocess.Popen(cmd, shell=True)
(output, err) = p.communicate()
except subprocess.CalledProcessError:
Util.log(self.name, "Error converting video")
def run(self):
"""
Main worker
"""
observer = deque(maxlen=self.fps * self.OBSERVER_LENGTH)
previous_frame = None
while True:
# Grab a frame
(grabbed, self.current_frame) = self.source.read()
# End of feed
if not grabbed:
break
# Gray frame
frame_gray = cv2.cvtColor(self.current_frame, cv2.COLOR_BGR2GRAY)
# Blur frame
frame_blur = cv2.GaussianBlur(frame_gray, (21, 21), 0)
# If there's no previous frame, us the current one
if previous_frame is None:
previous_frame = frame_blur
continue
# Delta frame
delta_frame = cv2.absdiff(previous_frame, frame_blur)
# Threshold frame
threshold_frame = cv2.threshold(delta_frame, 15, 255, cv2.THRESH_BINARY)[1]
# Dilate the thresholded image to fill in holes
kernel = np.ones((5, 5), np.uint8)
dilated_frame = cv2.dilate(threshold_frame, kernel, iterations=4)
# Find difference in percent
res = dilated_frame.astype(np.uint8)
movement = (np.count_nonzero(res) * 100) / res.size
# Add movement percentage to observer
observer.append(movement)
if self.do_add_contours or self.do_add_target:
self.current_frame, targets = self.add_contours(self.current_frame, dilated_frame)
if self.do_add_target:
self.current_frame = self.add_target(self.current_frame, targets)
if self.do_record and self.detected(sum([x > self.threshold for x in observer]) > 0):
if not self.recording():
self.start_recording()
self.writer.write(self.current_frame)
elif self.recording():
# Delete Old files
self.delete()
# Convert
self.convert_to_mp4(self.current_file)
# Reset all
self.stop_recording()
Util.log(self.name, "Observing...")
# Set blurred frame as new previous frame
previous_frame = frame_blur
# Display
if self.do_display:
cv2.imshow("Current frame:", self.current_frame)
# Exit on 'q'
key = cv2.waitKey(1) & 0xFF
if key == ord('q'):
break
def delete(self):
"""
delete mic data to a mp4 file.
@param list data
"""
count = 0
Util.log(self.name, "Delete USB Cam video...")
file_list = sorted(os.listdir(self.archive), reverse=True)
for filename in [file for file in file_list if file.endswith("usb.mp4")]:
if not filename.startswith('.'):
type = self.get_type(filename)
if type == "video":
count = count + 1
if self.REMAIN_RECORDING_FILES < count:
Util.log(self.name, "Delete USB video filename=" + filename + ", type=" + type + ", count=" + str(count))
os.remove(self.archive + "/" + filename)
def get_type(self, filename):
name, extension = os.path.splitext(filename)
return 'video' if extension == '.mp4' else 'video' if extension == '.avi' else 'audio' if extension == '.wav' else 'audio' if extension == '.mp3' else 'photo'
def add_contours(self, raw_frame, dilated_frame):
"""
Add contours to frame
@param array raw_frame
@param array dilated_frame
@return tuple(array, list)
"""
# Find contours on thresholded image
_, contours, nada = cv2.findContours(dilated_frame.copy(),cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
# Make coutour frame
contour_frame = raw_frame.copy()
# Target contours
targets = []
# Loop over the contour
for c in contours:
# If the contour is too small, ignore it
if cv2.contourArea(c) < 500:
# Make sure this has a less than sign, not an html escape
continue
# Contour data
M = cv2.moments(c)
cx = int(M['m10']/M['m00'])
cy = int(M['m01']/M['m00'])
x, y, w, h = cv2.boundingRect(c)
rx = x + int(w / 2)
ry = y + int(h / 2)
ca = cv2.contourArea(c)
# plot contours
cv2.drawContours(contour_frame,[c],0,(0,0,255),2)
cv2.rectangle(contour_frame,(x,y),(x+w,y+h),(0,255,0),2)
cv2.circle(contour_frame,(cx,cy),2,(0,0,255),2)
cv2.circle(contour_frame,(rx,ry),2,(0,255,0),2)
# save target contours
targets.append((rx,ry,ca))
return contour_frame, targets
def add_target(self, raw_frame, targets):
"""
Add crosshairs to frame
@param array raw_frame
@param list targets
@return array
"""
# Make target
area = sum([x[2] for x in targets])
mx = 0
my = 0
if targets:
for x, y, a in targets:
mx += x
my += y
mx = int(round(mx / len(targets), 0))
my = int(round(my / len(targets), 0))
# Plot target
tr = 50
target_frame = raw_frame.copy()
if targets:
cv2.circle(target_frame, (mx, my), tr, (0, 0, 255, 0), 2)
cv2.line(target_frame, (mx - tr, my), (mx + tr, my), (0, 0, 255, 0), 2)
cv2.line(target_frame, (mx, my - tr), (mx, my + tr), (0, 0, 255, 0), 2)
return target_frame
def detected(self, has_motion):
"""
Check if this or another detector detected something
@param boolean has_motion
@return boolean
"""
if has_motion:
self.lock_manager.set()
else:
self.lock_manager.remove()
self.detected_at = self.lock_manager.get_lock_time()
return self.detected_at is not None
def recording(self):
"""
Check if currently recording
@return boolean
"""
return self.writer is not None
if __name__ == "__main__":
args = sys.argv[1:]
source = None
do_display = False
try:
opts, args = getopt.getopt(args, "hs:d",["source=", "display"])
except getopt.GetoptError:
print('python3 motion_detector.py -s <source> [-d]')
sys.exit(2)
for opt, arg in opts:
if opt == '-h':
print('python3 motion_detector.py -s <source> [-d]')
sys.exit()
elif opt in ("-s", "--source"):
source = arg.strip()
elif opt in ("-d", "--display"):
do_display = True
if source is not None:
print('Input: ', source)
else:
print('Input: Camera')
if source is not None and not os.path.isfile(source):
print(str(source) + " does not exist")
else:
md = Motion_Detector(source=source, do_display=do_display, do_add_contours=True)
md.start()
piwebcamera.py
import os
import sys
import time
import math
import getopt
import numpy as np
import cv2
import threading
import subprocess
from collections import deque
from lock_manager import Lock_Manager
from util import Util
class PiWebCamera(threading.Thread):
def __init__(self, video_source=0, source=None, do_record=True, do_display=True, do_add_contours=True, do_add_target=False):
threading.Thread.__init__(self)
self.name = self.__class__.__name__
self.archive = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'archive')
self.writer = None
self.current_frame = None
self.codec = cv2.VideoWriter_fourcc('M','J', 'P', 'G')
self.OBSERVER_LENGTH = 5 # Time in seconds to be observed for motion
self.threshold = 15
self.CAMERA_SOURCE = video_source
self.REMAIN_RECORDING_FILES = 3 # 10이상 부터 삭제 후 저장
self.do_display = do_display
self.do_record = do_record
self.do_add_contours = do_add_contours
self.do_add_target = do_add_target
self.current_file = None
self.source = cv2.VideoCapture(source) if source is not None else self.init_camera()
self.fps = self.find_fps(self.source)
self.height, self.width = self.get_dimensions(self.source)
Util.log(self.name, "Initializing pi camera class with video_source=" + str(self.CAMERA_SOURCE))
Util.log(self.name,"width: {"+str(self.width)+"}, height : {"+str(self.height)+"}")
self.lock_manager = Lock_Manager("motion")
def __del__(self):
# Release camera
self.source.release()
# Close all windows
cv2.destroyAllWindows()
# Remove lock if exists
self.lock_manager.remove()
def get_frame(self):
"""
Return the current frame
@return bytes
"""
return self.frame_to_jpg(self.current_frame) if self.current_frame is not None else None
def frame_to_jpg(self, frame):
"""
Convert video frame to jpg
@param array frame
@return bytes
"""
ret, jpeg = cv2.imencode('.jpg', self.current_frame)
return jpeg.tobytes()
def get_dimensions(self, source):
"""
Determine height and width of the video source
@return tuple(int, int)
"""
frame = cv2.cvtColor(source.read()[1],cv2.COLOR_RGB2GRAY)
return frame.shape[0: 2]
def find_fps(self, source):
"""
Determine frames per second of the video source
@param video source
@return int
"""
Util.log(self.name, "Determining FPS...")
# How many frames to capture
num_frames = 120
# Start time
start = time.time()
# Grab a few frames
for i in range(0, num_frames):
ret, frame = source.read()
# End time
end = time.time()
# Calculate frames per second
fps = int(math.floor(num_frames / (end - start)))
Util.log(self.name, "Setting FPS to " + str(fps))
return fps
def init_camera(self):
"""
Start the camera
@return cv2.VideoCapture
"""
# Init camera
camera = cv2.VideoCapture(self.CAMERA_SOURCE)
#camera.set(3, 320)
#camera.set(4, 240)
# Wait half a second for light adjustment
time.sleep(0.5)
return camera
def start_recording(self):
"""
Setup the recorder
"""
self.current_file = self.archive + "/" + self.detected_at + "-pic.avi"
Util.log(self.name, "Motion detected! Recording...")
# Set path and FPS
self.writer = cv2.VideoWriter(self.current_file, self.codec, self.fps, (self.width, self.height))
def stop_recording(self):
"""
Reset values to default
"""
self.writer = None
self.current_file = None
self.detected_at = None
def convert_to_mp4(self, path):
"""
Convert video file to mp4 using ffmpeg
@param string path
"""
try:
Util.log(self.name, "Converting video...")
destination = os.path.splitext(path)[0] + '.mp4'
cmd = 'ffmpeg -i "{}" "{}" 2> /dev/null && rm "{}"'.format(path, destination, path)
#cmd = 'for i in ' + self.archive + '/*.avi; do ffmpeg -i "$i" "${i%.*}.mp4" 2> /dev/null && rm "$i"; done'
p = subprocess.Popen(cmd, shell=True)
(output, err) = p.communicate()
except subprocess.CalledProcessError:
Util.log(self.name, "Error converting video")
def run(self):
"""
Main worker
"""
observer = deque(maxlen=self.fps * self.OBSERVER_LENGTH)
previous_frame = None
while True:
# Grab a frame
(grabbed, self.current_frame) = self.source.read()
# End of feed
if not grabbed:
break
# Gray frame
frame_gray = cv2.cvtColor(self.current_frame, cv2.COLOR_BGR2GRAY)
# Blur frame
frame_blur = cv2.GaussianBlur(frame_gray, (21, 21), 0)
# If there's no previous frame, us the current one
if previous_frame is None:
previous_frame = frame_blur
continue
# Delta frame
delta_frame = cv2.absdiff(previous_frame, frame_blur)
# Threshold frame
threshold_frame = cv2.threshold(delta_frame, 15, 255, cv2.THRESH_BINARY)[1]
# Dilate the thresholded image to fill in holes
kernel = np.ones((5, 5), np.uint8)
dilated_frame = cv2.dilate(threshold_frame, kernel, iterations=4)
# Find difference in percent
res = dilated_frame.astype(np.uint8)
movement = (np.count_nonzero(res) * 100) / res.size
# Add movement percentage to observer
observer.append(movement)
if self.do_add_contours or self.do_add_target:
self.current_frame, targets = self.add_contours(self.current_frame, dilated_frame)
if self.do_add_target:
self.current_frame = self.add_target(self.current_frame, targets)
if self.do_record and self.detected(sum([x > self.threshold for x in observer]) > 0):
if not self.recording():
self.start_recording()
self.writer.write(self.current_frame)
elif self.recording():
# Delete Old files
self.delete()
# Convert
self.convert_to_mp4(self.current_file)
# Reset all
self.stop_recording()
Util.log(self.name, "Observing...")
# Set blurred frame as new previous frame
previous_frame = frame_blur
# Display
if self.do_display:
cv2.imshow("Current frame:", self.current_frame)
# Exit on 'q'
key = cv2.waitKey(1) & 0xFF
if key == ord('q'):
break
def delete(self):
"""
delete mic data to a mp4 file.
@param list data
"""
count = 0
Util.log(self.name, "Delete PI Cam video...")
file_list = sorted(os.listdir(self.archive), reverse=True)
for filename in [file for file in file_list if file.endswith("pic.mp4")]:
if not filename.startswith('.'):
type = self.get_type(filename)
if type == "video":
count = count + 1
if self.REMAIN_RECORDING_FILES < count:
Util.log(self.name, "Delete PIC video filename=" + filename + ", type=" + type + ", count=" + str(count))
os.remove(self.archive + "/" + filename)
def get_type(self, filename):
name, extension = os.path.splitext(filename)
return 'video' if extension == '.mp4' else 'video' if extension == '.avi' else 'audio' if extension == '.wav' else 'audio' if extension == '.mp3' else 'photo'
def add_contours(self, raw_frame, dilated_frame):
"""
Add contours to frame
@param array raw_frame
@param array dilated_frame
@return tuple(array, list)
"""
# Find contours on thresholded image
_, contours, nada = cv2.findContours(dilated_frame.copy(),cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
# Make coutour frame
contour_frame = raw_frame.copy()
# Target contours
targets = []
# Loop over the contour
for c in contours:
# If the contour is too small, ignore it
if cv2.contourArea(c) < 500:
# Make sure this has a less than sign, not an html escape
continue
# Contour data
M = cv2.moments(c)
cx = int(M['m10']/M['m00'])
cy = int(M['m01']/M['m00'])
x, y, w, h = cv2.boundingRect(c)
rx = x + int(w / 2)
ry = y + int(h / 2)
ca = cv2.contourArea(c)
# plot contours
cv2.drawContours(contour_frame,[c],0,(0,0,255),2)
cv2.rectangle(contour_frame,(x,y),(x+w,y+h),(0,255,0),2)
cv2.circle(contour_frame,(cx,cy),2,(0,0,255),2)
cv2.circle(contour_frame,(rx,ry),2,(0,255,0),2)
# save target contours
targets.append((rx,ry,ca))
return contour_frame, targets
def add_target(self, raw_frame, targets):
"""
Add crosshairs to frame
@param array raw_frame
@param list targets
@return array
"""
# Make target
area = sum([x[2] for x in targets])
mx = 0
my = 0
if targets:
for x, y, a in targets:
mx += x
my += y
mx = int(round(mx / len(targets), 0))
my = int(round(my / len(targets), 0))
# Plot target
tr = 50
target_frame = raw_frame.copy()
if targets:
cv2.circle(target_frame, (mx, my), tr, (0, 0, 255, 0), 2)
cv2.line(target_frame, (mx - tr, my), (mx + tr, my), (0, 0, 255, 0), 2)
cv2.line(target_frame, (mx, my - tr), (mx, my + tr), (0, 0, 255, 0), 2)
return target_frame
def detected(self, has_motion):
"""
Check if this or another detector detected something
@param boolean has_motion
@return boolean
"""
if has_motion:
self.lock_manager.set()
else:
self.lock_manager.remove()
self.detected_at = self.lock_manager.get_lock_time()
return self.detected_at is not None
def recording(self):
"""
Check if currently recording
@return boolean
"""
return self.writer is not None
if __name__ == "__main__":
args = sys.argv[1:]
source = None
do_display = False
try:
opts, args = getopt.getopt(args, "hs:d",["source=", "display"])
except getopt.GetoptError:
print('python3 motion_detector.py -s <source> [-d]')
sys.exit(2)
for opt, arg in opts:
if opt == '-h':
print('python3 motion_detector.py -s <source> [-d]')
sys.exit()
elif opt in ("-s", "--source"):
source = arg.strip()
elif opt in ("-d", "--display"):
do_display = True
if source is not None:
print('Input: ', source)
else:
print('Input: Camera')
if source is not None and not os.path.isfile(source):
print(str(source) + " does not exist")
else:
md = Motion_Detector(source=source, do_display=do_display, do_add_contours=True)
md.start()
capture.py
import os
import cv2
import datetime, time
from pathlib import Path
archive_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'archive')
def capture_and_save(im):
s = im.shape
# Add a timestamp
font = cv2.FONT_HERSHEY_SIMPLEX
bottomLeftCornerOfText = (10,s[0]-10)
fontScale = 1
fontColor = (20,20,20)
lineType = 2
cv2.putText(im,datetime.datetime.now().isoformat().split(".")[0],bottomLeftCornerOfText,font,fontScale,fontColor, lineType)
m = 0
p = Path(archive_path)
for imp in p.iterdir():
if imp.suffix == ".png" and imp.stem != "last":
num = imp.stem.split("_")[1]
try:
num = int(num)
if num>m:
m = num
except:
print("Error reading image number for",str(imp))
m +=1
lp = Path(archive_path + "/last.png")
if lp.exists() and lp.is_file():
np = Path(archive_path + "/img_{}.png".format(m))
np.write_bytes(lp.read_bytes())
cv2.imwrite(archive_path + "/last.png",im)
if __name__=="__main__":
capture_and_save()
print("done")
Noise Detector
noise_detector.py
# Threshold / Sliding window
# https://raw.githubusercontent.com/jeysonmc/python-google-speech-scripts/master/stt_google.py
# WebSocket streaming:
# https://gist.github.com/fopina/3cefaed1b2d2d79984ad7894aef39a68
import pyaudio
import wave
import audioop
import subprocess
import os
import time
import math
import struct
import threading
import io
import numpy as np
from collections import deque
from lock_manager import Lock_Manager
from util import Util
class Noise_Detector(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
self.name = self.__class__.__name__
self.FORMAT = pyaudio.paFloat32
self.RATE = 48000 # Hz, so samples (bytes) per second
self.CHUNK_SIZE = 8192 # How many bytes to read from mic each time (stream.read())
self.CHUNKS_PER_SEC = math.floor(self.RATE / self.CHUNK_SIZE) # How many chunks make a second? (16.000 bytes/s, each chunk is 1.024 bytes, so 1s is 15 chunks)
self.CHANNELS = 1
self.HISTORY_LENGTH = 2 # Seconds of audio cache for prepending to records to prevent chopped phrases (history length + observer length = min record length)
self.OBSERVER_LENGTH = 5 # Time in seconds to be observed for noise
self.NOTIFICATION_LIMIT = 1 # Seconds before a notification is sent
self.LIMIT_RECODING = 100 # 최대 Recoding chunk 수
self.CURRENT_RECODING_TIME = 0 # 현재 Recoding chunk 수
self.REMAIN_RECORDING_FILES = 3 # 10이상 부터 삭제 후 저장
self.RECODING_OVER_THRESHOLD = 5 # Recoding 임계값을 연속 넘는 회수로 저장 여부 판단
self.archive = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'archive')
self.current_file = None
self.chunk = None
self.record = [] # Stores audio chunks
self.notified = False # If we already sent a notification
self.audio = pyaudio.PyAudio()
self.stream = self.get_stream()
self.threshold = self.determine_threshold()
self.lock_manager = Lock_Manager("noise")
self.detected_at = None
def __del__(self):
# Stop recording
if self.stream:
self.stream.close()
if self.audio:
self.audio.terminate()
# Remove lock if exists
self.lock_manager.remove()
def get_stream(self):
"""
Open audio stream
@return PyAudio
"""
return self.audio.open(
format=self.FORMAT,
channels=self.CHANNELS,
rate=self.RATE,
input=True,
frames_per_buffer=self.CHUNK_SIZE
)
def determine_threshold(self):
"""
Determine threshold noise intensity using RMS
Anything below the threshold is considered silence
@return float
"""
Util.log(self.name, "Determining threshold...")
res = []
for x in range(50):
block = self.stream.read(self.CHUNK_SIZE)
rms = self.get_rms(block)
res.append(rms)
# Set threshold to 20% above avergae
threshold = (sum(res) / len(res)) * 1.7 #1.2
Util.log(self.name, "Setting threshold to: " + str(threshold))
return threshold
def get_rms(self, block):
"""
Calculate Root Mean Square (noise level) for audio chunk
@param bytes block
@return float
"""
d = np.frombuffer(block, np.float32).astype(np.float)
return np.sqrt((d * d).sum() / len(d))
def start_recording(self):
"""
Setup the recorder
"""
self.current_file = self.archive + "/" + self.detected_at + ".wav"
Util.log(self.name, "Noise detected! Recording...")
def stop_recording(self):
"""
Reset variables to default
"""
self.current_file = None
self.detected_at = None
self.notified = False
self.record = []
self.CURRENT_RECODING_TIME = 0
def run(self):
"""
Detect noise from microphone and record
Noise is defined as sound surrounded by silence (according to threshold)
"""
# Stores audio intensity of previous sound-chunks
# If one of these chunks is above threshold, recording gets triggered
# Keep the last {OBSERVER_LENGTH} seconds in observer
observer = deque(maxlen=self.OBSERVER_LENGTH * self.CHUNKS_PER_SEC)
# Prepend audio from before noise was detected
# Keep the last {HISTORY_LENGTH} seconds in history
history = deque(maxlen=self.HISTORY_LENGTH * self.CHUNKS_PER_SEC)
Util.log(self.name, "Listening...")
try:
while True:
# Current chunk of audio data
self.chunk = self.stream.read(self.CHUNK_SIZE, exception_on_overflow = False)
history.append(self.chunk)
# Add noise level of this chunk to the sliding-window
rms = self.get_rms(self.chunk)
#Util.log(self.name, "Noise threshold=" + str(rms))
observer.append(rms)
if self.detected(sum([x > self.threshold for x in observer]) > self.RECODING_OVER_THRESHOLD) and self.LIMIT_RECODING > self.CURRENT_RECODING_TIME:
self.CURRENT_RECODING_TIME = self.CURRENT_RECODING_TIME + 1
# There's at least one chunk in the sliding-window above threshold
if not self.recording():
self.start_recording()
#Util.log(self.name, "Record.append noise level="+ str(sum([x > self.threshold for x in observer])) + ", time=" + str(self.CURRENT_RECODING_TIME) )
self.record.append(self.chunk)
if not self.notified and len(self.record) > self.NOTIFICATION_LIMIT * self.CHUNKS_PER_SEC:
self.notify()
elif self.recording():
# Silence limit was reached, finish recording and save
self.delete()
self.save(list(history) + self.record)
self.stop_recording()
Util.log(self.name, "Listening...")
except KeyboardInterrupt:
Util.log(self.name, "Interrupted.")
def get_chunk(self):
"""
Return the current chunk of audio data
@return bytes
"""
return self.chunk
def delete(self):
"""
delete mic data to a WAV file.
@param list data
"""
count = 0
Util.log(self.name, "Delete audio...")
for filename in sorted(os.listdir(self.archive), reverse=True):
if not filename.startswith('.'):
type = self.get_type(filename)
if type == "audio":
count = count + 1
if self.REMAIN_RECORDING_FILES < count:
Util.log(self.name, "Delete audio filename=" + filename + ", type=" + type + ", count=" + str(count))
os.remove(self.archive + "/" + filename)
def get_type(self, filename):
name, extension = os.path.splitext(filename)
return 'video' if extension == '.mp4' else 'video' if extension == '.avi' else 'audio' if extension == '.wav' else 'audio' if extension == '.mp3' else 'photo'
def save(self, data):
"""
Save mic data to a WAV file.
@param list data
"""
Util.log(self.name, "Saving audio...")
# Flatten the list
data = b''.join(data)
# Write converted data to file
with open(self.current_file, "wb+") as file:
file.write(self.generate_wav(data))
# Convert 음질 개떡
#self.convert_to_mp3(self.current_file)
def bytes_to_array(self, bytes, type):
"""
Convert raw audio data to TypedArray
@param bytes bytes
@return numpy-Array
"""
return np.frombuffer(bytes, dtype=type)
def generate_wav(self, raw):
"""
Create WAVE-file from raw audio chunks
@param bytes raw
@return bytes
"""
# Check if input format is supported
if self.FORMAT not in (pyaudio.paFloat32, pyaudio.paInt16):
print("Unsupported format")
return
# Convert raw audio bytes to typed array
samples = self.bytes_to_array(raw, np.float32)
# Get sample size
sample_size = pyaudio.get_sample_size(self.FORMAT)
# Get data-length
byte_count = (len(samples)) * sample_size
# Get bits/sample
bits_per_sample = sample_size * 8
# Calculate frame-size
frame_size = int(self.CHANNELS * ((bits_per_sample + 7) / 8))
# Container for WAVE-content
wav = bytearray()
# Start RIFF-Header
wav.extend(struct.pack('<cccc', b'R', b'I', b'F', b'F'))
# Add chunk size (data-size minus 8)
wav.extend(struct.pack('<I', byte_count + 0x2c - 8))
# Add RIFF-type ("WAVE")
wav.extend(struct.pack('<cccc', b'W', b'A', b'V', b'E'))
# Start "Format"-part
wav.extend(struct.pack('<cccc', b'f', b'm', b't', b' '))
# Add header length (16 bytes)
wav.extend(struct.pack('<I', 0x10))
# Add format-tag (e.g. 1 = PCM, 3 = FLOAT)
wav.extend(struct.pack('<H', 3))
# Add channel count
wav.extend(struct.pack('<H', self.CHANNELS))
# Add sample rate
wav.extend(struct.pack('<I', self.RATE))
# Add bytes/second
wav.extend(struct.pack('<I', self.RATE * frame_size))
# Add frame size
wav.extend(struct.pack('<H', frame_size))
# Add bits/sample
wav.extend(struct.pack('<H', bits_per_sample))
# Start data-part
wav.extend(struct.pack('<cccc', b'd', b'a', b't', b'a'))
# Add data-length
wav.extend(struct.pack('<I', byte_count))
# Add data
for sample in samples:
wav.extend(struct.pack("<f", sample))
return bytes(wav)
def convert_to_mp3(self, path):
"""
Convert wav-file to mp3
@param string path
"""
Util.log(self.name, "Converting audio...")
try:
cmd = 'lame --preset insane "{}" 2> /dev/null && rm "{}"'.format(path, path)
p = subprocess.Popen(cmd, shell=True)
(output, err) = p.communicate()
except subprocess.CalledProcessError:
Util.log(self.name, "Error converting audio")
def detected(self, has_noise):
"""
Check if this or another detector detected something
@param boolean has_noise
@return boolean
"""
if has_noise:
self.lock_manager.set()
else:
self.lock_manager.remove()
self.detected_at = self.lock_manager.get_lock_time()
return self.detected_at is not None
def recording(self):
"""
Check if currently recording
@return boolean
"""
return len(self.record) > 0
def notify(self):
"""
Notify
"""
Util.log(self.name, "Notifying")
self.notified = True
if __name__ == "__main__":
nd = Noise_Detector()
nd.start()
Listen Speech
listenSpeech.py
from google_trans_new import google_translator
from google_speech import Speech
from time import sleep
translator = google_translator()
class ListenSpeech(object):
def speech(self,message):
ko_result = translator.translate(message, lang_tgt='ko')
print(' -> ', ko_result)
speech = Speech(ko_result, 'ko')
speech.play()
en_result = translator.translate(message, lang_tgt='en')
speech = Speech(en_result, 'en')
speech.play()
it_result = translator.translate(message, lang_tgt='it')
speech = Speech(it_result, 'it')
speech.play()
ja_result = translator.translate(message, lang_tgt='ja')
speech = Speech(ja_result, 'ja')
speech.play()
returnString = ko_result + " , EN=" + en_result + " , IT=" + it_result + ", JA=" + ja_result
print("Return message = " + returnString)
return returnString
GPIO
gpioControl.py
import RPi.GPIO as GPIO
from time import sleep
ina1 = 33
ina2 = 35
ena = 37
inb1 = 31
inb2 = 29
enb = 23
GPIO.setmode(GPIO.BOARD)
GPIO.setup(12, GPIO.OUT, initial=1)
GPIO.setup(18, GPIO.OUT, initial=1)
GPIO.output(12, GPIO.LOW)
GPIO.output(18, GPIO.LOW)
GPIO.setup(ina1,GPIO.OUT)
GPIO.setup(ina2,GPIO.OUT)
GPIO.setup(ena,GPIO.OUT)
GPIO.setup(inb1,GPIO.OUT)
GPIO.setup(inb2,GPIO.OUT)
GPIO.setup(enb,GPIO.OUT)
p1 = GPIO.PWM(18, 50) # 50 Hz
p2 = GPIO.PWM(12, 50) # 50
pa = GPIO.PWM(ena,1000)
pb = GPIO.PWM(enb,1000)
cameraInitPositionY = 6.0
cameraInitPositionX = 6.5
verticalVal = cameraInitPositionY
horizontalVal = cameraInitPositionX
class GpioControl(object):
def __init__(self):
global verticalVal
global horizontalVal
global p1
global p2
p1.start(0)
p2.start(0)
pa.start(25)
pb.start(25)
p1.ChangeDutyCycle(cameraInitPositionY)
p2.ChangeDutyCycle(cameraInitPositionX)
sleep(0.1)
p1.ChangeDutyCycle(0)
p2.ChangeDutyCycle(0)
verticalVal = cameraInitPositionY
horizontalVal = cameraInitPositionX
#print("> Init Vert=" + str(verticalVal) + ",Hort=" + str(horizontalVal))
def __del__(self):
global p1
global p2
p1.stop()
p2.stop()
print(" GPIO.__del__() ")
GPIO.cleanup()
def cleanUp(self):
global p1
global p2
p1.stop()
p2.stop()
print(" GPIO.cleanUp() ")
GPIO.cleanup()
sleep(2)
def initMotorPosition(self):
# Init
global verticalVal
global horizontalVal
global cameraInitPositionY
global cameraInitPositionX
global p1
global p2
p1.ChangeDutyCycle(float(cameraInitPositionY))
p2.ChangeDutyCycle(float(cameraInitPositionX))
sleep(0.1)
p1.ChangeDutyCycle(float(0.0))
p2.ChangeDutyCycle(float(0.0))
verticalVal = cameraInitPositionY
horizontalVal = cameraInitPositionX
#print("> Init Vert=" + str(verticalVal) + ",Hort=" + str(horizontalVal))
return "Vert=" + str(verticalVal) + ",Hort=" + str(horizontalVal)
def moveUp(self):
global verticalVal
global horizontalVal
global p2
if(verticalVal>3.5):
verticalVal = float(round(verticalVal-0.2, 1))
p2.ChangeDutyCycle(verticalVal)
#print("> UP Vert=%.2f ,Hort=%.2f" % (verticalVal, horizontalVal))
sleep(0.1)
p2.ChangeDutyCycle(0)
return "UP Vert=" + str(verticalVal) + ",Hort=" + str(horizontalVal)
def moveDown(self):
global verticalVal
global horizontalVal
global p2
if(verticalVal<8.1):
verticalVal = float(round(verticalVal+0.2, 1))
p2.ChangeDutyCycle(verticalVal)
#print("> Down Vert=" + str(verticalVal) + ",Hort=" + str(horizontalVal))
sleep(0.1)
p2.ChangeDutyCycle(0)
return "DOWN Vert=" + str(verticalVal) + ",Hort=" + str(horizontalVal)
def moveRight(self):
global verticalVal
global horizontalVal
global p1
if(horizontalVal>2.5):
horizontalVal = float(round(horizontalVal-0.2, 1))
p1.ChangeDutyCycle(horizontalVal)
#print("> Right Vert=" + str(verticalVal) + ",Hort=" + str(horizontalVal))
sleep(0.1)
p1.ChangeDutyCycle(0)
return "RIGHT Vert=" + str(verticalVal) + ",Hort=" + str(horizontalVal)
def moveLeft(self):
global verticalVal
global horizontalVal
global p1
if(horizontalVal<10):
horizontalVal = float(round(horizontalVal+0.2, 1))
p1.ChangeDutyCycle(horizontalVal)
#print("> Left Vert=" + str(verticalVal) + ",Hort=" + str(horizontalVal))
sleep(0.1)
p1.ChangeDutyCycle(0)
return "LEFT Vert=" + str(verticalVal) + ",Hort=" + str(horizontalVal)
def goForward(self,time):
print("Go forward")
pa.ChangeDutyCycle(75)
GPIO.output(ina1,GPIO.HIGH)
GPIO.output(ina2,GPIO.LOW)
pb.ChangeDutyCycle(75)
GPIO.output(inb1,GPIO.HIGH)
GPIO.output(inb2,GPIO.LOW)
sleep(0.1*int(time))
GPIO.output(ina1,GPIO.LOW)
GPIO.output(ina2,GPIO.LOW)
GPIO.output(inb1,GPIO.LOW)
GPIO.output(inb2,GPIO.LOW)
return " >> Go Forward"
def goBackward(self,time):
print("Go backward")
pa.ChangeDutyCycle(75)
GPIO.output(ina1,GPIO.LOW)
GPIO.output(ina2,GPIO.HIGH)
pb.ChangeDutyCycle(75)
GPIO.output(inb1,GPIO.LOW)
GPIO.output(inb2,GPIO.HIGH)
sleep(0.1*int(time))
GPIO.output(ina1,GPIO.LOW)
GPIO.output(ina2,GPIO.LOW)
GPIO.output(inb1,GPIO.LOW)
GPIO.output(inb2,GPIO.LOW)
return " >> Go Backward"
def goTurnleftback(self,time):
print("Go Turn Left back")
pa.ChangeDutyCycle(75)
GPIO.output(inb1,GPIO.LOW)
GPIO.output(inb2,GPIO.HIGH)
sleep(0.1*int(time))
GPIO.output(ina1,GPIO.LOW)
GPIO.output(ina2,GPIO.LOW)
GPIO.output(inb1,GPIO.LOW)
GPIO.output(inb2,GPIO.LOW)
return " >> Go Turn Left back"
def goTurnrightback(self,time):
print("Go Turn Right back")
pb.ChangeDutyCycle(75)
GPIO.output(ina1,GPIO.LOW)
GPIO.output(ina2,GPIO.HIGH)
sleep(0.1*int(time))
GPIO.output(ina1,GPIO.LOW)
GPIO.output(ina2,GPIO.LOW)
GPIO.output(inb1,GPIO.LOW)
GPIO.output(inb2,GPIO.LOW)
return " >> Go Turn Right back"
def goTurnleftforward(self,time):
print("Go Turn Left forward")
pa.ChangeDutyCycle(75)
GPIO.output(inb1,GPIO.HIGH)
GPIO.output(inb2,GPIO.LOW)
sleep(0.1*int(time))
GPIO.output(ina1,GPIO.LOW)
GPIO.output(ina2,GPIO.LOW)
GPIO.output(inb1,GPIO.LOW)
GPIO.output(inb2,GPIO.LOW)
return " >> Go Turn Left forward"
def goTurnrightforward(self,time):
print("Go Turn Right forward")
pb.ChangeDutyCycle(75)
GPIO.output(ina1,GPIO.HIGH)
GPIO.output(ina2,GPIO.LOW)
sleep(0.1*int(time))
GPIO.output(ina1,GPIO.LOW)
GPIO.output(ina2,GPIO.LOW)
GPIO.output(inb1,GPIO.LOW)
GPIO.output(inb2,GPIO.LOW)
return " >> Go Turn Right forward"
Etc
util.py
from datetime import datetime
class Util:
def log(source, msg):
print(datetime.now().strftime("%Y%m%d_%H%M%S") + " | " + source + " | " + str(msg))
lock_manager.py
import os
import datetime
class Lock_Manager:
def __init__(self, name):
self.locks = os.path.dirname(os.path.realpath(__file__)) + "/locks"
self.name = name + ".lock"
self.path = os.path.join(self.locks, self.name)
if not os.path.exists(self.locks):
os.makedirs(self.locks)
def set(self):
if not os.path.exists(self.path):
print("Setting {} lock".format(self.name))
timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
with open(self.path, 'w+') as f:
f.write(timestamp)
def remove(self):
if os.path.isfile(self.path):
os.remove(self.path)
def get_lock_time(self):
for filename in os.listdir(self.locks):
with open(self.locks + "/" + filename) as f:
return f.read()
return None
conf.py
from pathlib import Path
p = Path("logs")
if not p.exists():
p.mkdir()
dictConfig = {
'version': 1,
'disable_existing_loggers': True,
'formatters': {
'standard': {
'format': '%(asctime)s [%(levelname)s] %(name)s:: %(message)s',
},
},
'handlers': {
'default': {
'level': 'DEBUG',
'formatter': 'standard',
'class': 'logging.StreamHandler',
'stream': 'ext://sys.stdout',
},
'file': {
'class': 'logging.handlers.RotatingFileHandler',
'level': 'DEBUG',
'formatter': 'standard',
'filename': 'logs/logfile.log',
'mode': 'a',
'maxBytes': 5_242_880,
'backupCount': 3,
'encoding': 'utf-8',
},
},
'loggers': {
'__main__': {
'handlers': ['default','file'],
'level': 'DEBUG',
'propagate': False,
},
'camera': {
'handlers': ['default', 'file'],
'level': 'DEBUG',
'propagate': False,
},
}
}
power.py
from vcgencmd import Vcgencmd
vcgm = Vcgencmd()
class PowerStatus(object):
def __init__(self):
print("> vcgm.version()=" + vcgm.version())
def getPowerStatus(self):
output=vcgm.get_throttled()
print("raw_data=" + output['raw_data'])
print("binary=" + output['binary'])
return "raw_data=" + output['raw_data'] + ", binary=" + output['binary']
StreamingVideo
작성 중
StreamingVideo
import io
import picamera
import logging
import socketserver
from threading import Condition
from http import server
import os
from gpioControl import GpioControl
from listenSpeech import ListenSpeech
from urllib.parse import parse_qs
ONAIR = True
gpio = GpioControl()
listenspeech = ListenSpeech()
# Load HTML file
file = open("view.html", "r")
PAGE=file.read()
file.close()
class StreamingOutput(object):
def __init__(self):
self.frame = None
self.buffer = io.BytesIO()
self.condition = Condition()
def write(self, buf):
if buf.startswith(b'\xff\xd8'):
# New frame, copy the existing buffer's content and notify all
# clients it's available
self.buffer.truncate()
with self.condition:
self.frame = self.buffer.getvalue()
self.condition.notify_all()
self.buffer.seek(0)
return self.buffer.write(buf)
class StreamingHandler(server.BaseHTTPRequestHandler):
def do_POST(self):
if self.path == '/msg':
self.data_string = self.rfile.read(int(self.headers['Content-Length']))
getMessage=self.data_string.decode()
reString = listenspeech.speech(getMessage);
self.send_response(200)
self.send_header('Content-Type', 'text/html')
self.send_header('Content-Length', len(reString.encode()))
self.end_headers()
print("Return message = " + reString)
self.wfile.write(reString.encode())
elif self.path == '/forward':
self.data_int = self.rfile.read(int(self.headers['Content-Length']))
moveOrder=self.data_int
self.send_response(200)
if not moveOrder :
reString = "Do not move"
else :
reString = gpio.goForward(moveOrder)
self.send_header('Content-Type', 'text/html')
self.send_header('Content-Length', len(reString.encode()))
self.end_headers()
self.wfile.write(reString.encode())
print("Go Forward ...")
elif self.path == '/backward':
self.data_int = self.rfile.read(int(self.headers['Content-Length']))
moveOrder=self.data_int
self.send_response(200)
if not moveOrder :
reString = "Do not move"
else :
reString = gpio.goBackward(moveOrder)
self.send_header('Content-Type', 'text/html')
self.send_header('Content-Length', len(reString.encode()))
self.end_headers()
self.wfile.write(reString.encode())
print("Go Backward ...")
elif self.path == '/turnleftback':
self.data_int = self.rfile.read(int(self.headers['Content-Length']))
moveOrder=self.data_int
self.send_response(200)
if not moveOrder :
reString = "Do not move"
else :
reString = gpio.goTurnleftback(moveOrder)
self.send_header('Content-Type', 'text/html')
self.send_header('Content-Length', len(reString.encode()))
self.end_headers()
self.wfile.write(reString.encode())
print("Go Turn Left back...")
elif self.path == '/turnrightback':
self.data_int = self.rfile.read(int(self.headers['Content-Length']))
moveOrder=self.data_int
self.send_response(200)
if not moveOrder :
reString = "Do not move"
else :
reString = gpio.goTurnrightback(moveOrder)
self.send_header('Content-Type', 'text/html')
self.send_header('Content-Length', len(reString.encode()))
self.end_headers()
self.wfile.write(reString.encode())
print("Go Turn Right back...")
elif self.path == '/turnleftforward':
self.data_int = self.rfile.read(int(self.headers['Content-Length']))
moveOrder=self.data_int
self.send_response(200)
if not moveOrder :
reString = "Do not move"
else :
reString = gpio.goTurnleftforward(moveOrder)
self.send_header('Content-Type', 'text/html')
self.send_header('Content-Length', len(reString.encode()))
self.end_headers()
self.wfile.write(reString.encode())
print("Go Turn Left forward...")
elif self.path == '/turnrightforward':
self.data_int = self.rfile.read(int(self.headers['Content-Length']))
moveOrder=self.data_int
self.send_response(200)
if not moveOrder :
reString = "Do not move"
else :
reString = gpio.goTurnrightforward(moveOrder)
self.send_header('Content-Type', 'text/html')
self.send_header('Content-Length', len(reString.encode()))
self.end_headers()
self.wfile.write(reString.encode())
print("Go Turn Right forward...")
def do_GET(self):
global ONAIR
if self.path == '/':
self.send_response(301)
self.send_header('Location', '/index.html')
self.end_headers()
elif self.path == '/index.html':
content = PAGE.encode('utf-8')
self.send_response(200)
self.send_header('Content-Type', 'text/html')
self.send_header('Content-Length', len(content))
self.end_headers()
self.wfile.write(content)
elif self.path == '/stream.mjpg':
self.send_response(200)
self.send_header('Age', 0)
self.send_header('Cache-Control', 'no-cache, private')
self.send_header('Pragma', 'no-cache')
self.send_header('Content-Type', 'multipart/x-mixed-replace; boundary=FRAME')
self.end_headers()
try:
while ONAIR:
with output.condition:
output.condition.wait()
frame = output.frame
self.wfile.write(b'--FRAME\r\n')
self.send_header('Content-Type', 'image/jpeg')
self.send_header('Content-Length', len(frame))
self.end_headers()
self.wfile.write(frame)
self.wfile.write(b'\r\n')
except Exception as e:
logging.warning(
'Removed streaming client %s: %s',
self.client_address, str(e))
elif self.path == '/init':
self.send_response(200)
reString = "Camera position init : " + gpio.initMotorPosition()
self.send_header('Content-Type', 'text/html')
self.send_header('Content-Length', len(reString.encode()))
self.end_headers()
self.wfile.write(reString.encode())
print("Init Cam ...")
elif self.path == '/up':
self.send_response(200)
reString = gpio.moveUp()
self.send_header('Content-Type', 'text/html')
self.send_header('Content-Length', len(reString.encode()))
self.end_headers()
self.wfile.write(reString.encode())
print("Move Up ...")
elif self.path == '/down':
self.send_response(200)
reString = gpio.moveDown()
self.send_header('Content-Type', 'text/html')
self.send_header('Content-Length', len(reString.encode()))
self.end_headers()
self.wfile.write(reString.encode())
print("Move Down ...")
elif self.path == '/left':
self.send_response(200)
reString = gpio.moveLeft()
self.send_header('Content-Type', 'text/html')
self.send_header('Content-Length', len(reString.encode()))
self.end_headers()
self.wfile.write(reString.encode())
print("Move Left ...")
elif self.path == '/right':
self.send_response(200)
reString = gpio.moveRight()
self.send_header('Content-Type', 'text/html')
self.send_header('Content-Length', len(reString.encode()))
self.end_headers()
self.wfile.write(reString.encode())
print("Move Right ...")
elif self.path == '/temperature':
content = os.popen("vcgencmd measure_temp").readline()
content = content.replace("temp=", "")
self.send_response(200)
self.send_header('Content-Type', 'text/html')
self.send_header('Content-Length', len(content.encode()))
self.end_headers()
self.wfile.write(content.encode())
else:
self.send_error(404)
self.end_headers()
class StreamingServer(socketserver.ThreadingMixIn, server.HTTPServer):
allow_reuse_address = True
daemon_threads = True
with picamera.PiCamera(resolution='640x480', framerate=24) as camera:
output = StreamingOutput()
#Uncomment the next line to change your Pi's Camera rotation (in degrees)
camera.rotation = 180
camera.start_recording(output, format='mjpeg')
try:
address = ('', 8081)
server = StreamingServer(address, StreamingHandler)
server.serve_forever()
finally:
camera.stop_recording()
gpioControl.py
import RPi.GPIO as GPIO
from time import sleep
GPIO.setmode(GPIO.BOARD)
ina1 = 33
ina2 = 35
ena = 37
inb1 = 31
inb2 = 29
enb = 23
GPIO.setup(12, GPIO.OUT, initial=1)
GPIO.setup(18, GPIO.OUT, initial=1)
GPIO.setup(11, GPIO.OUT, initial=1) # light
GPIO.setup(ina1,GPIO.OUT)
GPIO.setup(ina2,GPIO.OUT)
GPIO.setup(ena,GPIO.OUT)
GPIO.setup(inb1,GPIO.OUT)
GPIO.setup(inb2,GPIO.OUT)
GPIO.setup(enb,GPIO.OUT)
p1 = GPIO.PWM(18, 50) # 50 Hz
p2 = GPIO.PWM(12, 50) # 50
p1.start(0)
p2.start(0)
p1.ChangeDutyCycle(0)
p2.ChangeDutyCycle(0)
pa=GPIO.PWM(ena,1000)
pa.start(25)
pb=GPIO.PWM(enb,1000)
pb.start(25)
verticalVal = 6.5
horizontalVal = 6
cameraPositionX = 6.5
cameraPositionY = 6
# Set up camera constants
IM_WIDTH = 640
IM_HEIGHT = 480
class GpioControl(object):
def __init__(self):
global verticalVal
global horizontalVal
global p1
global p2
p1.ChangeDutyCycle(6.5)
p2.ChangeDutyCycle(6)
sleep(0.1)
p1.ChangeDutyCycle(0)
p2.ChangeDutyCycle(0)
verticalVal = 6.5
horizontalVal = 6
print("> Init Vert=" + str(verticalVal) + ",Hort=" + str(horizontalVal))
def click(self):
GPIO.output(11, GPIO.LOW)
sleep(0.5)
GPIO.output(11, GPIO.HIGH)
sleep(1)
def __del__(self):
global p1
global p2
p1.stop()
p2.stop()
print(" GPIO.__del__() ")
GPIO.cleanup()
def cleanUp(self):
global p1
global p2
p1.stop()
p2.stop()
print(" GPIO.cleanUp() ")
GPIO.cleanup()
sleep(2)
def initMotorPosition(self):
# Init
global verticalVal
global horizontalVal
global p1
global p2
p1.ChangeDutyCycle(6.5)
p2.ChangeDutyCycle(6)
sleep(0.1)
p1.ChangeDutyCycle(0)
p2.ChangeDutyCycle(0)
verticalVal = 6.5
horizontalVal = 6
print("> Init Vert=" + str(verticalVal) + ",Hort=" + str(horizontalVal))
return "Vert=" + str(verticalVal) + ",Hort=" + str(horizontalVal)
def moveUp(self):
global verticalVal
global horizontalVal
global p2
verticalVal = round(verticalVal-0.2, 1)
p2.ChangeDutyCycle(verticalVal)
print("> UP Vert=" + str(verticalVal) + ",Hort=" + str(horizontalVal))
sleep(0.1)
p2.ChangeDutyCycle(0)
return "Vert=" + str(verticalVal) + ",Hort=" + str(horizontalVal)
def moveDown(self):
global verticalVal
global horizontalVal
global p2
verticalVal = round(verticalVal+0.2, 1)
p2.ChangeDutyCycle(verticalVal)
print("> Down Vert=" + str(verticalVal) + ",Hort=" + str(horizontalVal))
sleep(0.1)
p2.ChangeDutyCycle(0)
return "Vert=" + str(verticalVal) + ",Hort=" + str(horizontalVal)
def moveRight(self):
global verticalVal
global horizontalVal
global p1
horizontalVal = round(horizontalVal-0.2, 1)
p1.ChangeDutyCycle(horizontalVal)
print("> Right Vert=" + str(verticalVal) + ",Hort=" + str(horizontalVal))
sleep(0.1)
p1.ChangeDutyCycle(0)
return "Vert=" + str(verticalVal) + ",Hort=" + str(horizontalVal)
def moveLeft(self):
global verticalVal
global horizontalVal
global p1
horizontalVal = round(horizontalVal+0.2, 1)
p1.ChangeDutyCycle(horizontalVal)
print("> Left Vert=" + str(verticalVal) + ",Hort=" + str(horizontalVal))
sleep(0.1)
p1.ChangeDutyCycle(0)
return "Vert=" + str(verticalVal) + ",Hort=" + str(horizontalVal)
def goForward(self,time):
print("Go forward")
pa.ChangeDutyCycle(75)
GPIO.output(ina1,GPIO.HIGH)
GPIO.output(ina2,GPIO.LOW)
pb.ChangeDutyCycle(75)
GPIO.output(inb1,GPIO.HIGH)
GPIO.output(inb2,GPIO.LOW)
sleep(0.1*int(time))
GPIO.output(ina1,GPIO.LOW)
GPIO.output(ina2,GPIO.LOW)
GPIO.output(inb1,GPIO.LOW)
GPIO.output(inb2,GPIO.LOW)
return " >> Go Forward"
def goBackward(self,time):
print("Go backward")
pa.ChangeDutyCycle(75)
GPIO.output(ina1,GPIO.LOW)
GPIO.output(ina2,GPIO.HIGH)
pb.ChangeDutyCycle(75)
GPIO.output(inb1,GPIO.LOW)
GPIO.output(inb2,GPIO.HIGH)
sleep(0.1*int(time))
GPIO.output(ina1,GPIO.LOW)
GPIO.output(ina2,GPIO.LOW)
GPIO.output(inb1,GPIO.LOW)
GPIO.output(inb2,GPIO.LOW)
return " >> Go Backward"
def goTurnleftback(self,time):
print("Go Turn Left back")
pa.ChangeDutyCycle(75)
GPIO.output(inb1,GPIO.LOW)
GPIO.output(inb2,GPIO.HIGH)
sleep(0.1*int(time))
GPIO.output(ina1,GPIO.LOW)
GPIO.output(ina2,GPIO.LOW)
GPIO.output(inb1,GPIO.LOW)
GPIO.output(inb2,GPIO.LOW)
return " >> Go Turn Left back"
def goTurnrightback(self,time):
print("Go Turn Right back")
pb.ChangeDutyCycle(75)
GPIO.output(ina1,GPIO.LOW)
GPIO.output(ina2,GPIO.HIGH)
sleep(0.1*int(time))
GPIO.output(ina1,GPIO.LOW)
GPIO.output(ina2,GPIO.LOW)
GPIO.output(inb1,GPIO.LOW)
GPIO.output(inb2,GPIO.LOW)
return " >> Go Turn Right back"
def goTurnleftforward(self,time):
print("Go Turn Left forward")
pa.ChangeDutyCycle(75)
GPIO.output(inb1,GPIO.HIGH)
GPIO.output(inb2,GPIO.LOW)
sleep(0.1*int(time))
GPIO.output(ina1,GPIO.LOW)
GPIO.output(ina2,GPIO.LOW)
GPIO.output(inb1,GPIO.LOW)
GPIO.output(inb2,GPIO.LOW)
return " >> Go Turn Left forward"
def goTurnrightforward(self,time):
print("Go Turn Right forward")
pb.ChangeDutyCycle(75)
GPIO.output(ina1,GPIO.HIGH)
GPIO.output(ina2,GPIO.LOW)
sleep(0.1*int(time))
GPIO.output(ina1,GPIO.LOW)
GPIO.output(ina2,GPIO.LOW)
GPIO.output(inb1,GPIO.LOW)
GPIO.output(inb2,GPIO.LOW)
return " >> Go Turn Right forward"
def move_to_position(self,ObjX, ObjY):
global cameraPositionX
global cameraPositionY
global p1
global p2
print(" >> Move to location x="+str(ObjX)+", y="+str(ObjY))
moveLoop = True
movX = int(IM_WIDTH/2)-ObjX
movY = int(IM_HEIGHT/2)-ObjY
print(" >> Center location movX="+str(movX)+", movY="+str(movY))
xx = 1
xy = 0
yx = 1
yy = 0
while(moveLoop):
if( xy < abs(movX) ):
p1.ChangeDutyCycle(cameraPositionX)
sleep(0.1)
p1.ChangeDutyCycle(0)
print("xx=" + str(xx) + ", xy="+ str(xy) +" cameraPositionX=" +str(round(cameraPositionX,1)))
xy = xx*xx * 12
xx = xx + 1
if(movX > 0): cameraPositionX = cameraPositionX - 0.2
else: cameraPositionX = cameraPositionX + 0.2
if( yy < abs(movY) ):
p2.ChangeDutyCycle(cameraPositionY)
sleep(0.1)
p2.ChangeDutyCycle(0)
print("yx=" + str(yx) + ", yy="+ str(yy) +" cameraPositionY=" +str(round(cameraPositionY,1)))
yy = yx*yx * 12
yx = yx + 1
if(movY > 0): cameraPositionY = cameraPositionY + 0.2
else: cameraPositionY = cameraPositionY - 0.2
elif( xy >= movX and yy >= movY):
print(" >> Center location movX="+str(movX)+", movY=" + str(movY))
print(" >> Position location xy="+str(xy)
+", yy="+str(yy)
+" cameraPositionX="+str(round(cameraPositionX,1))+
" cameraPositionY="+str(round(cameraPositionY,1))+" .. ")
moveLoop = False
ListenSpeech
from googletrans import Translator
from google_speech import Speech
from time import sleep
translator = Translator()
class ListenSpeech(object):
def speech(self,message):
ko_result = translator.translate(message, dest='ko')
print(' -> ', ko_result.text)
speech = Speech(ko_result.text, 'ko')
speech.play()
en_result = translator.translate(message, dest='en')
speech = Speech(en_result.text, 'en')
speech.play()
it_result = translator.translate(message, dest='it')
speech = Speech(it_result.text, 'it')
speech.play()
ja_result = translator.translate(message, dest='ja')
speech = Speech(ja_result.text, 'ja')
speech.play()
returnString = ko_result.text + " , EN=" + en_result.text + " , IT=" + it_result.text + ", JA=" + ja_result.text
print("Return message = " + returnString)
return returnString
view.html
<html>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta charset="UTF-8" />
<head>
<title>Raspberry Pi - HYUNSU Camera</title>
<script type="text/javascript">
window.onload = function() {
document.getElementById("btnInit").onclick = getInitFunc;
document.getElementById("btnMsg").onclick = getMsgFunc;
document.getElementById("btnLeft").onclick = getLeftFunc;
document.getElementById("btnRight").onclick = getRightFunc;
document.getElementById("btnUp").onclick = getUpFunc;
document.getElementById("btnDown").onclick = getDownFunc;
document.getElementById("btnForward").onclick = getForwardFunc;
document.getElementById("btnBackward").onclick = getBackwardFunc;
document.getElementById("btnTurnrightback").onclick = getTurnrightbackFunc;
document.getElementById("btnTurnleftback").onclick = getTurnleftbackFunc;
document.getElementById("btnTurnrightforward").onclick = getTurnrightforwardFunc;
document.getElementById("btnTurnleftforward").onclick = getTurnleftforwardFunc;
}
var xhr;
function getInitFunc() {
var fName = "/init";
xhr = new XMLHttpRequest();
xhr.open("get", fName, true);
xhr.onreadystatechange = function() {
if (xhr.readyState == 4) {
if (xhr.status == 200) {
document.getElementById("systemMessage").innerHTML = xhr.responseText;
} else {
alert("ERROR : " + xhr.status);
}
}
}
xhr.send(null);
}
function getMsgFunc() {
var fName = "/msg";
// var data = new FormData();
data=document.getElementById("textInput").value;
xhr = new XMLHttpRequest();
xhr.open("post", fName, true);
xhr.onreadystatechange = function() {
if (xhr.readyState == 4) {
if (xhr.status == 200) {
document.getElementById("systemMessage").innerHTML = xhr.responseText;
} else {
alert("ERROR : " + xhr.status);
}
}
}
xhr.send(data);
}
function getLeftFunc() {
var fName = "/left";
xhr = new XMLHttpRequest();
xhr.open("get", fName, true);
xhr.onreadystatechange = function() {
if (xhr.readyState == 4) {
if (xhr.status == 200) {
document.getElementById("systemMessage").innerHTML = xhr.responseText;
} else {
alert("ERROR : " + xhr.status);
}
}
}
xhr.send(null);
}
function getRightFunc() {
var fName = "/right";
xhr = new XMLHttpRequest();
xhr.open("get", fName, true);
xhr.onreadystatechange = function() {
if (xhr.readyState == 4) {
if (xhr.status == 200) {
document.getElementById("systemMessage").innerHTML = xhr.responseText;
} else {
alert("ERROR : " + xhr.status);
}
}
}
xhr.send(null);
}
function getUpFunc() {
var fName = "/up";
xhr = new XMLHttpRequest();
xhr.open("get", fName, true);
xhr.onreadystatechange = function() {
if (xhr.readyState == 4) {
if (xhr.status == 200) {
document.getElementById("systemMessage").innerHTML = xhr.responseText;
} else {
alert("ERROR : " + xhr.status);
}
}
}
xhr.send(null);
}
function getDownFunc() {
var fName = "/down";
xhr = new XMLHttpRequest();
xhr.open("get", fName, true);
xhr.onreadystatechange = function() {
if (xhr.readyState == 4) {
if (xhr.status == 200) {
document.getElementById("systemMessage").innerHTML = xhr.responseText;
} else {
alert("ERROR : " + xhr.status);
}
}
}
xhr.send(null);
}
function getForwardFunc() {
var fName = "/forward";
data=document.getElementById("moveCnt").value;
xhr = new XMLHttpRequest();
xhr.open("post", fName, true);
xhr.onreadystatechange = function() {
if (xhr.readyState == 4) {
if (xhr.status == 200) {
document.getElementById("systemMessage").innerHTML = xhr.responseText;
} else {
alert("ERROR : " + xhr.status);
}
}
}
xhr.send(data);
}
function getBackwardFunc() {
var fName = "/backward";
data=document.getElementById("moveCnt").value;
xhr = new XMLHttpRequest();
xhr.open("post", fName, true);
xhr.onreadystatechange = function() {
if (xhr.readyState == 4) {
if (xhr.status == 200) {
document.getElementById("systemMessage").innerHTML = xhr.responseText;
} else {
alert("ERROR : " + xhr.status);
}
}
}
xhr.send(data);
}
function getTurnleftbackFunc() {
var fName = "/turnleftback";
data=document.getElementById("moveCnt").value;
xhr = new XMLHttpRequest();
xhr.open("post", fName, true);
xhr.onreadystatechange = function() {
if (xhr.readyState == 4) {
if (xhr.status == 200) {
document.getElementById("systemMessage").innerHTML = xhr.responseText;
} else {
alert("ERROR : " + xhr.status);
}
}
}
xhr.send(data);
}
function getTurnrightbackFunc() {
var fName = "/turnrightback";
data=document.getElementById("moveCnt").value;
xhr = new XMLHttpRequest();
xhr.open("post", fName, true);
xhr.onreadystatechange = function() {
if (xhr.readyState == 4) {
if (xhr.status == 200) {
document.getElementById("systemMessage").innerHTML = xhr.responseText;
} else {
alert("ERROR : " + xhr.status);
}
}
}
xhr.send(data);
}
function getTurnleftforwardFunc() {
var fName = "/turnleftforward";
data=document.getElementById("moveCnt").value;
xhr = new XMLHttpRequest();
xhr.open("post", fName, true);
xhr.onreadystatechange = function() {
if (xhr.readyState == 4) {
if (xhr.status == 200) {
document.getElementById("systemMessage").innerHTML = xhr.responseText;
} else {
alert("ERROR : " + xhr.status);
}
}
}
xhr.send(data);
}
function getTurnrightforwardFunc() {
var fName = "/turnrightforward";
data=document.getElementById("moveCnt").value;
xhr = new XMLHttpRequest();
xhr.open("post", fName, true);
xhr.onreadystatechange = function() {
if (xhr.readyState == 4) {
if (xhr.status == 200) {
document.getElementById("systemMessage").innerHTML = xhr.responseText;
} else {
alert("ERROR : " + xhr.status);
}
}
}
xhr.send(data);
}
function getTemperatureFunc() {
var fName = "/temperature";
xhr = new XMLHttpRequest();
xhr.open("get", fName, true);
xhr.onreadystatechange = function() {
if (xhr.readyState == 4) {
if (xhr.status == 200) {
document.getElementById("temperature").innerHTML = xhr.responseText;
} else {
alert(" temperature ERROR : " + xhr.status);
}
}
}
xhr.send(null);
}
var timerId = null;
timerId = setInterval(getTemperatureFunc, 30000);
//clearInterval(timerId);
</script>
</head>
<body>
<form name="frm">
<center>
<h3>꼬꼬락 KKoLack</h3>
<h4>system temperature = <span id="temperature">...</span></h4>
<table border=0>
<tr>
<!-- Image section -->
<td>
<img src="stream.mjpg" width="300">
</td>
<!-- Control section -->
<td>
<table border=0>
<tr>
<td>
Message = <span id="systemMessage"> ... </span>
<br>
<input type=text id="textInput" lenght=20 ><input type="button" value="Send Message" id="btnMsg" />
</td>
</tr>
<tr>
<td>
<center>
Control Camera Section
<table>
<tr>
<td></td>
<td><input type="button" value=" ^ " id="btnUp" /></td>
<td></td>
</tr>
<tr>
<td><input type="button" value=" < " id="btnLeft" /></td>
<td></td>
<td><input type="button" value=" > " id="btnRight" /></td>
</tr>
<tr>
<td></td>
<td><input type="button" value=" v " id="btnDown" /></td>
<td></td>
</tr>
</table>
</center>
</td>
</tr>
<tr>
<td>
<center>
Control Engine Section
<br>Move cont (0.1 ~ 10) = <input type=text id="moveCnt" maxlength="5" size="5" />
<br>
<table>
<tr>
<td></td>
<td><input type="button" value=" ^ " id="btnForward" /></td>
<td></td>
</tr>
<tr>
<td><input type="button" value=" ^ " id="btnTurnleftforward" /></td>
<td></td>
<td><input type="button" value=" ^ " id="btnTurnrightforward" /></td>
</tr>
<tr>
<td><input type="button" value=" v " id="btnTurnleftback" /></td>
<td></td>
<td><input type="button" value=" v " id="btnTurnrightback" /></td>
</tr>
<tr>
<td></td>
<td><input type="button" value=" v " id="btnBackward" /></td>
<td></td>
</tr>
</table>
</center>
</td>
</tr>
</table>
</td>
</tr>
</table>
<input type="button" value="Initialization Camera" id="btnInit" /><br>
</center>
</form>
</body>
</html>
http://web.joang.com:9000/jcook/StreamingVideo
StreamingSound
StreamingSound
- aplay -L
-
hw:X,Y --> X is the card number, while Y is the device number.
- USB 마이크 자동 선택하기
micHw=`pactl list | grep -A 7 'device.bus = "usb"' | grep 'hw'` hwString=`echo ${micHw#*:} | cut -c 1` echo -e "\n Mic HW = [$hwString]" echo -e '\n##### START CCTV #####' cd /home/hyunsu/webcam nohup python3 /home/hyunsu/webcam/serverUsb.py & echo -e '\n##### START BROD SOUND #####' nohup cvlc -vvv alsa://plughw:$hwString --sout '#transcode{acodec=mp3,ab=64,channels=1}:standard{access=http,dst=0.0.0.0:8080/out.mp3}' 1> /dev/null 2>&1 & -
안방은 plughw:1 꼬꼬락은 plughw:2
echo '##### START BROD SOUND #####' nohup cvlc -vvv alsa://plughw:2 --sout '#transcode{acodec=mp3,ab=64,channels=1}:standard{access=http,dst=0.0.0.0:9999/out.mp3}' 1> /dev/null 2>&1 & -
<audio autoplay controls> <source src="http://localhost:9999/out.mp3" type="audio/mp3"> </audio>
TensorFlow OpenCV Slack Flack
TensorFlow OpenCV Slack Flack
main.py : 웹 메인 서버
#!/usr/bin/env python
#
# Project: Streaming Tensorflow image with Flask
# Author: jframework@gmail.com
# Date: 2020/05/11
# Website: http://www.joang.com
# Description:
# Publishing a video from room , Tensorflow and OpenCV and GPIO, Flask
# Usage:
# 1. Install Python dependencies: tensorflow, opencv, gpio, cv2, flask. (wish that pip install works like a charm)
# 2. Run "python3 main.py".
# 3. Navigate the browser to the local webpage. http://web.joang.com:8081/
#
#
from flask import Flask, render_template, Response, request
from camera import VideoCamera
from gpioControl import GpioControl
import os
app = Flask(__name__)
def shutdown_server():
func = request.environ.get('werkzeug.server.shutdown')
if func is None:
raise RuntimeError('Not running with the kkoRack Server')
func()
ONAIR = True
gpio = GpioControl()
@app.route('/')
def index():
temp = os.popen("vcgencmd measure_temp").readline()
return render_template('index.html', temperature=(temp.replace("temp=","")))
## Video ##
def gen(camera):
global ONAIR
while(ONAIR):
frame = camera.get_frame()
yield (b'--frame\r\n'
b'Content-Type: image/jpeg\r\n\r\n' + frame + b'\r\n\r\n')
@app.route('/video_feed')
def video_feed():
return Response(gen(VideoCamera()),
mimetype='multipart/x-mixed-replace; boundary=frame')
## Init ##
@app.route('/init')
def video_init():
print("video_init")
reString = gpio.initMotorPosition()
return "Camera position init : " + reString
## camera movement ##
@app.route('/right')
def video_right():
print("video_right")
reString = gpio.moveRight()
return "Camera Move right : " + reString
@app.route('/left')
def video_left():
print("video_left")
reString = gpio.moveLeft()
return "Camera Move left : " + reString
@app.route('/up')
def video_up():
print("video_up")
reString = gpio.moveUp()
return "Camera Move up : " + reString
@app.route('/down')
def video_down():
print("video_down")
reString = gpio.moveDown()
return "Camera Move down : " + reString
## gpio on/off ##
@app.route('/clicklight')
def clicklight():
reString = gpio.click()
return "Light Click"
## System ##
@app.route('/stop')
def video_stop():
global ONAIR
ONAIR = False
print("video_stop")
return render_template('index.html')
@app.route('/start')
def video_start():
global ONAIR
ONAIR = True
return render_template('index.html')
@app.route('/shutdown')
def shutdown():
gpio.cleanUp()
shutdown_server()
return 'Server shutting down...'
@app.route('/temperature')
def temperature():
temp = os.popen("vcgencmd measure_temp").readline()
return (temp.replace("temp=",""))
if __name__ == '__main__':
app.run(host='0.0.0.0', port='8081', debug=True)
camera.py : 카메라 기능
import cv2
from objectDetector import ObjectDetector
# Set up camera constants
IM_WIDTH = 640
IM_HEIGHT = 480
# Initialize frame rate calculation
frame_rate_calc = 1
freq = cv2.getTickFrequency()
font = cv2.FONT_HERSHEY_SIMPLEX
objDetector = ObjectDetector()
class VideoCamera(object):
def __init__(self):
# Using OpenCV to capture from device 0. If you have trouble capturing
# from a webcam, comment the line below out and use a video file
# instead.
self.video = cv2.VideoCapture(0)
# If you decide to use video.mp4, you must have this file in the folder
# as the main.py.
# self.video = cv2.VideoCapture('video.mp4')
print( "width: {}, height : {}".format(self.video.get(3), self.video.get(4) ) )
ret = self.video.set(3,IM_WIDTH)
ret = self.video.set(4,IM_HEIGHT)
def __del__(self):
self.video.release()
def get_frame(self):
global frame_rate_calc
t1 = cv2.getTickCount()
success, frame = self.video.read()
# We are using Motion JPEG, but OpenCV defaults to capture raw images,
# so we must encode it into JPEG in order to correctly display the
# video stream.
frame = objDetector.objectDetector(frame)
# Draw FPS
cv2.putText(frame,"FPS: {0:.2f}".format(frame_rate_calc),(30,50),font,1,(255,255,0),2,cv2.LINE_AA)
# FPS calculation
t2 = cv2.getTickCount()
time1 = (t2-t1)/freq
frame_rate_calc = 1/time1
ret, jpeg = cv2.imencode('.jpg', frame)
return jpeg.tobytes()
objectDetector.py : 이미지 식별
#!/usr/bin/python
# -*- coding: UTF-8 -*-
# Import packages
import os
import cv2
import numpy as np
import tensorflow as tf
from time import sleep
import sys
from gpioControl import GpioControl
from slackmsg import SlackMsg
# Set up camera constants
IM_WIDTH = 640
IM_HEIGHT = 480
# This is needed since the working directory is the object_detection folder.
sys.path.append('/home/pi/tensorflow1/models/research')
sys.path.append('/home/pi/tensorflow1/models/research/object_detection')
# Import utilites
from utils import label_map_util
from utils import visualization_utils as vis_util
# Grab path to current working directory
#CWD_PATH = os.getcwd() + "/tensorflow1/models/research/object_detection"
CWD_PATH = "/home/pi/tensorflow1/models/research/object_detection"
#IMG_PATH = os.getcwd() + "/Pictures"
IMG_PATH = "/home/pi/Pictures"
print(" \n ###################################")
print(" Base Path %s" % CWD_PATH)
print(" Image Path %s" % IMG_PATH)
print(" ###################################")
#### Initialize TensorFlow model ####
# Name of the directory containing the object detection module we're using
MODEL_NAME = 'ssdlite_mobilenet_v2_coco_2018_05_09'
# Path to frozen detection graph .pb file, which contains the model that is used
# for object detection.
PATH_TO_CKPT = os.path.join(CWD_PATH,MODEL_NAME,'frozen_inference_graph.pb')
# Path to label map file
PATH_TO_LABELS = os.path.join(CWD_PATH,'data','mscoco_label_map.pbtxt')
# Number of classes the object detector can identify
NUM_CLASSES = 80
print(" \n ###################################")
print(" Load the label map ")
print(" ###################################")
## Load the label map.
# Label maps map indices to category names, so that when the convolution
# network predicts `5`, we know that this corresponds to `airplane`.
# Here we use internal utility functions, but anything that returns a
# dictionary mapping integers to appropriate string labels would be fine
label_map = label_map_util.load_labelmap(PATH_TO_LABELS)
categories = label_map_util.convert_label_map_to_categories(label_map, max_num_classes=NUM_CLASSES, use_display_name=True)
category_index = label_map_util.create_category_index(categories)
# Load the Tensorflow model into memory.
detection_graph = tf.Graph()
with detection_graph.as_default():
od_graph_def = tf.compat.v1.GraphDef()
with tf.io.gfile.GFile(PATH_TO_CKPT, 'rb') as fid:
serialized_graph = fid.read()
od_graph_def.ParseFromString(serialized_graph)
tf.import_graph_def(od_graph_def, name='')
sess = tf.compat.v1.Session(graph=detection_graph)
print(" \n ###################################")
print(" Define input and output tensors (i.e. data) for the object detection classifier ")
print(" ###################################")
# Input tensor is the image
image_tensor = detection_graph.get_tensor_by_name('image_tensor:0')
# Output tensors are the detection boxes, scores, and classes
# Each box represents a part of the image where a particular object was detected
detection_boxes = detection_graph.get_tensor_by_name('detection_boxes:0')
# Each score represents level of confidence for each of the objects.
# The score is shown on the result image, together with the class label.
detection_scores = detection_graph.get_tensor_by_name('detection_scores:0')
detection_classes = detection_graph.get_tensor_by_name('detection_classes:0')
# Number of objects detected
num_detections = detection_graph.get_tensor_by_name('num_detections:0')
print(" \n ###################################")
print(" Initialize other parameters ")
print(" ###################################")
# Initialize frame rate calculation
frame_rate_calc = 1
freq = cv2.getTickFrequency()
font = cv2.FONT_HERSHEY_SIMPLEX
# Initialize control variables used for pet detector
detected_inside = False
detected_outside = False
inside_counter = 0
outside_counter = 0
ObjX = 0
ObjY = 0
pause = 0
pause_counter = 0
gpio = GpioControl()
slackmsg = SlackMsg()
class ObjectDetector(object):
def objectDetector(self, frame):
# Use globals for the control variables so they retain their value after function exits
global detected_inside, detected_outside
global inside_counter, outside_counter
global pause, pause_counter
frame_expanded = np.expand_dims(frame, axis=0)
# Perform the actual detection by running the model with the image as input
(boxes, scores, classes, num) = sess.run(
[detection_boxes, detection_scores, detection_classes, num_detections],
feed_dict={image_tensor: frame_expanded})
# Draw the results of the detection (aka 'visulaize the results')
vis_util.visualize_boxes_and_labels_on_image_array(
frame,
np.squeeze(boxes),
np.squeeze(classes).astype(np.int32),
np.squeeze(scores),
category_index,
use_normalized_coordinates=True,
line_thickness=8,
min_score_thresh=0.40)
# Draw boxes defining "outside" locations.
TL_outside = (int(IM_WIDTH*0.6),int(IM_HEIGHT*0.25))
BR_outside = (int(IM_WIDTH*0.85),int(IM_HEIGHT*.85))
cv2.rectangle(frame,TL_outside,BR_outside,(255,20,20),3)
cv2.putText(frame,"Outside room",(TL_outside[0]+10,TL_outside[1]-10),font,1,(255,20,255),3,cv2.LINE_AA)
# Check the class of the top detected object by looking at classes[0][0].
# If the top detected object is a person (73)
# boxes ( xmin, xmax, ymin, ymax )
threshold = 0.8
for index, value in enumerate(classes[0]):
ymin = boxes[0][index][0] * IM_HEIGHT
xmin = boxes[0][index][1] * IM_WIDTH
ymax = boxes[0][index][2] * IM_HEIGHT
xmax = boxes[0][index][3] * IM_WIDTH
#print(' =1= %s' % category_index.get(value) )
if category_index.get(value) != None:
personclassname = (category_index.get(value)).get('name')
widthvalue = int((xmax - xmin) / 2) # width 길이
heightvalue = int((ymax - ymin) / 2) # height 길이
if( scores[0, index] != 0.0 ): print('> Score = %s, Object = %s , pause = %s' % (scores[0, index], personclassname, pause) )
if scores[0, index] > threshold and personclassname == 'person' and pause == 0:
print('> Detected %s' % personclassname )
ObjX = int(((boxes[0][0][1]+boxes[0][0][3])/2)*IM_WIDTH)
ObjY = int(((boxes[0][0][0]+boxes[0][0][2])/2)*IM_HEIGHT)
# Draw a circle at center of object
cv2.circle(frame,(ObjX,ObjY), 5, (75,13,180), -1)
# If object is in outside box, increment outside counter variable
if ((ObjX > TL_outside[0]) and (ObjX < BR_outside[0]) and (ObjY > TL_outside[1]) and (ObjY < BR_outside[1])):
outside_counter = outside_counter + 1
else :
inside_counter = inside_counter + 1
# If pet has been detected inside for more than 10 frames, set detected_inside flag
# and send a text to the phone.
if inside_counter > 10:
detected_inside = True
cv2.imwrite(IMG_PATH+'/inside_' + str(ObjX) + '_' + str(ObjY) + '_counter.jpg', frame)
captureImg=os.path.join(IMG_PATH, 'inside_' + str(ObjX) + '_' + str(ObjY) + '_counter.jpg')
response = slackmsg.uploadImage("Inside Photo", captureImg)
captureImg=response['file']['permalink']
slackmsg.sendMsg("Inside", captureImg)
# Set move to detected object
gpio.move_to_position(ObjX, ObjY)
inside_counter = 0
outside_counter = 0
ObjX = 0
ObjY = 0
# Pause pet detection by setting "pause" flag
pause = 1
# If pet has been detected outside for more than 10 frames, set detected_outside flag
# and send a text to the phone.
if outside_counter > 10:
detected_outside = True
cv2.imwrite(IMG_PATH+'/outside_' + str(ObjX) + '_' + str(ObjY) + '_counter.jpg', frame)
captureImg=os.path.join(IMG_PATH, 'outside_' + str(ObjX) + '_' + str(ObjY) + '_counter.jpg')
response = slackmsg.uploadImage("Outside Photo", captureImg)
captureImg=response['file']['permalink']
slackmsg.sendMsg("Outside", captureImg)
# Set move to detected object
gpio.move_to_position(ObjX, ObjY)
inside_counter = 0
outside_counter = 0
ObjX = 0
ObjY = 0
# Pause pet detection by setting "pause" flag
pause = 1
# If pause flag is set, draw message on screen.
if pause == 1:
if detected_inside == True:
cv2.putText(frame,'Inside!',(int(IM_WIDTH*.1),int(IM_HEIGHT*.5)),font,1,(0,0,0),7,cv2.LINE_AA)
cv2.putText(frame,'Inside!',(int(IM_WIDTH*.1),int(IM_HEIGHT*.5)),font,1,(95,176,23),5,cv2.LINE_AA)
if detected_outside == True:
cv2.putText(frame,'Outside!',(int(IM_WIDTH*.1),int(IM_HEIGHT*.5)),font,1,(0,0,0),7,cv2.LINE_AA)
cv2.putText(frame,'Outside!',(int(IM_WIDTH*.1),int(IM_HEIGHT*.5)),font,1,(95,176,23),5,cv2.LINE_AA)
# Increment pause counter until it reaches 30 (for a framerate of 1.5 FPS, this is about 20 seconds),
# then unpause the application (set pause flag to 0).
pause_counter = pause_counter + 1
if pause_counter > 30:
pause = 0
pause_counter = 0
detected_inside = False
detected_outside = False
# Draw counter info
cv2.putText(frame,'Detection counter: ' + str(max(inside_counter,outside_counter)),(10,100),font,0.5,(255,255,0),1,cv2.LINE_AA)
cv2.putText(frame,'Pause counter: ' + str(pause_counter),(10,150),font,0.5,(255,255,0),1,cv2.LINE_AA)
return frame
gpioControl.py : 등 켜기 등 릴ㄹ레이 모듈 , 카메라 위치 조정 스텝 모터 조정
import RPi.GPIO as GPIO
from time import sleep
GPIO.setmode(GPIO.BOARD)
GPIO.setup(12, GPIO.OUT, initial=1)
GPIO.setup(18, GPIO.OUT, initial=1)
GPIO.setup(11, GPIO.OUT, initial=1) # light
p1 = GPIO.PWM(12, 50) # 50 Hz
p2 = GPIO.PWM(18, 50) # 50
p1.start(0)
p2.start(0)
p1.ChangeDutyCycle(0)
p2.ChangeDutyCycle(0)
verticalVal = 6.5
horizontalVal = 6.5
cameraPositionX = 6.5
cameraPositionY = 6.5
# Set up camera constants
IM_WIDTH = 640
IM_HEIGHT = 480
class GpioControl(object):
def __init__(self):
global verticalVal
global horizontalVal
global p1
global p2
p1.ChangeDutyCycle(6.5)
p2.ChangeDutyCycle(6.5)
sleep(0.1)
p1.ChangeDutyCycle(0)
p2.ChangeDutyCycle(0)
verticalVal = 6.5
horizontalVal = 6.5
print("> Init Vert=" + str(verticalVal) + ",Hort=" + str(horizontalVal))
def click(self):
GPIO.output(11, GPIO.LOW)
sleep(0.5)
GPIO.output(11, GPIO.HIGH)
sleep(1)
def __del__(self):
global p1
global p2
p1.stop()
p2.stop()
print(" GPIO.__del__() ")
GPIO.cleanup()
def cleanUp(self):
global p1
global p2
p1.stop()
p2.stop()
print(" GPIO.cleanUp() ")
GPIO.cleanup()
sleep(2)
def initMotorPosition(self):
# Init
global verticalVal
global horizontalVal
global p1
global p2
p1.ChangeDutyCycle(6.5)
p2.ChangeDutyCycle(6.5)
sleep(0.1)
p1.ChangeDutyCycle(0)
p2.ChangeDutyCycle(0)
verticalVal = 6.5
horizontalVal = 6.5
print("> Init Vert=" + str(verticalVal) + ",Hort=" + str(horizontalVal))
return "Vert=" + str(verticalVal) + ",Hort=" + str(horizontalVal)
def moveUp(self):
global verticalVal
global horizontalVal
global p2
verticalVal = round(verticalVal+0.2, 1)
p2.ChangeDutyCycle(verticalVal)
print("> UP Vert=" + str(verticalVal) + ",Hort=" + str(horizontalVal))
sleep(0.1)
p2.ChangeDutyCycle(0)
return "Vert=" + str(verticalVal) + ",Hort=" + str(horizontalVal)
def moveDown(self):
global verticalVal
global horizontalVal
global p2
verticalVal = round(verticalVal-0.2, 1)
p2.ChangeDutyCycle(verticalVal)
print("> Down Vert=" + str(verticalVal) + ",Hort=" + str(horizontalVal))
sleep(0.1)
p2.ChangeDutyCycle(0)
return "Vert=" + str(verticalVal) + ",Hort=" + str(horizontalVal)
def moveRight(self):
global verticalVal
global horizontalVal
global p1
horizontalVal = round(horizontalVal+0.2, 1)
p1.ChangeDutyCycle(horizontalVal)
print("> Right Vert=" + str(verticalVal) + ",Hort=" + str(horizontalVal))
sleep(0.1)
p1.ChangeDutyCycle(0)
return "Vert=" + str(verticalVal) + ",Hort=" + str(horizontalVal)
def moveLeft(self):
global verticalVal
global horizontalVal
global p1
horizontalVal = round(horizontalVal-0.2, 1)
p1.ChangeDutyCycle(horizontalVal)
print("> Left Vert=" + str(verticalVal) + ",Hort=" + str(horizontalVal))
sleep(0.1)
p1.ChangeDutyCycle(0)
return "Vert=" + str(verticalVal) + ",Hort=" + str(horizontalVal)
def move_to_position(self,ObjX, ObjY):
global cameraPositionX
global cameraPositionY
global p1
global p2
print(" >> Move to location x=%s, y=%s" % (ObjX, ObjY))
moveLoop = True
movX = int(IM_WIDTH/2)-ObjX
movY = int(IM_HEIGHT/2)-ObjY
print(" >> Center location movX=%s, movY=%s" % (movX, movY))
xx = 1
xy = 0
yx = 1
yy = 0
while(moveLoop):
if( xy < abs(movX) ):
p1.ChangeDutyCycle(cameraPositionX)
sleep(0.1)
p1.ChangeDutyCycle(0)
print("xx=" + str(xx) + ", xy="+ str(xy) +" cameraPositionX=" +str(round(cameraPositionX,1)))
xy = xx*xx * 12
xx = xx + 1
if(movX > 0): cameraPositionX = cameraPositionX - 0.2
else: cameraPositionX = cameraPositionX + 0.2
if( yy < abs(movY) ):
p2.ChangeDutyCycle(cameraPositionY)
sleep(0.1)
p2.ChangeDutyCycle(0)
print("yx=" + str(yx) + ", yy="+ str(yy) +" cameraPositionY=" +str(round(cameraPositionY,1)))
yy = yx*yx * 12
yx = yx + 1
if(movY > 0): cameraPositionY = cameraPositionY + 0.2
else: cameraPositionY = cameraPositionY - 0.2
elif( xy >= movX and yy >= movY):
print(" >> Center location movX="+str(movX)+", movY=" + str(movY))
print(" >> Position location xy="+str(xy)
+", yy="+str(yy)
+" cameraPositionX="+str(round(cameraPositionX,1))+
" cameraPositionY="+str(round(cameraPositionY,1))+" .. ")
moveLoop = False
