Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding in Object Tracking #11

Merged
merged 31 commits into from
Mar 17, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
124 changes: 107 additions & 17 deletions axis-ptz/camera.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,18 @@
cameraZoom = None
cameraMoveSpeed = None
cameraDelay = None
object_topic = None
flight_topic = None
pan = 0
tilt = 0
actualPan = 0
actualTilt = 0
follow_x = 0
follow_y = 0
actualX = 0
actualY = 0
currentPlane=None
object_timeout=0

# https://stackoverflow.com/questions/45659723/calculate-the-difference-between-two-compass-headings-python

Expand All @@ -58,6 +65,14 @@ def getHeadingDiff(h1, h2):
else:
return absDiff - 360

def setXY(x,y):
global follow_x
global follow_y

follow_x = int(x)
follow_y = int(y)


def setPan(bearing):
global pan
camera_bearing = args.bearing
Expand Down Expand Up @@ -109,6 +124,7 @@ def get_jpeg_request(): # 5.2.4.1
"""
payload = {
'resolution': "1920x1080",
'compression': 5,
'camera': 1,
}
url = 'https://' + args.axis_ip + '/axis-cgi/jpg/image.cgi'
Expand All @@ -132,25 +148,84 @@ def get_jpeg_request(): # 5.2.4.1
text += str(resp.text)
return text

def get_bmp_request(): # 5.2.4.1
"""
The requests specified in the JPEG/MJPG section are supported by those video products
that use JPEG and MJPG encoding.
Args:
resolution: Resolution of the returned image. Check the product’s Release notes.
camera: Selects the source camera or the quad stream.
square_pixel: Enable/disable square pixel correction. Applies only to video encoders.
compression: Adjusts the compression level of the image.
clock: Shows/hides the time stamp. (0 = hide, 1 = show)
date: Shows/hides the date. (0 = hide, 1 = show)
text: Shows/hides the text. (0 = hide, 1 = show)
text_string: The text shown in the image, the string must be URL encoded.
text_color: The color of the text shown in the image. (black, white)
text_background_color: The color of the text background shown in the image.
(black, white, transparent, semitransparent)
rotation: Rotate the image clockwise.
text_position: The position of the string shown in the image. (top, bottom)
overlay_image: Enable/disable overlay image.(0 = disable, 1 = enable)
overlay_position:The x and y coordinates defining the position of the overlay image.
(<int>x<int>)
Returns:
Success ('image save' and save the image in the file folder) or Failure (Error and
description).
"""
payload = {
'resolution': "1920x1080",
'camera': 1,
}
url = 'https://' + args.axis_ip + '/axis-cgi/bitmap/image.bmp'
resp = requests.get(url, auth=HTTPDigestAuth(args.axis_username, args.axis_password),
params=payload)

if resp.status_code == 200:
captureDir = "capture/{}".format(currentPlane["type"])
try:
os.makedirs(captureDir)
except OSError as e:
if e.errno != errno.EEXIST:
raise # This was not a "directory exist" error..
filename = "{}/{}_{}.bmp".format(captureDir, currentPlane["icao24"],datetime.now().strftime('%Y-%m-%d-%H-%M-%S'))

with open(filename, 'wb') as var:
var.write(resp.content)
return str('Image saved')

text = str(resp)
text += str(resp.text)
return text

def moveCamera():
global actualPan
global actualTilt
global actualX
global actualY
global camera


while True:
lockedOn = False
if actualTilt != tilt or actualPan != pan:
logging.info("Moving camera to Tilt: %d & Pan: %d"%(tilt, pan))
actualTilt = tilt
actualPan = pan
lockedOn = True
camera.absolute_move(pan, tilt, cameraZoom, cameraMoveSpeed)
time.sleep(0.3)
get_jpeg_request()


if (object_timeout < time.mktime(time.gmtime())):
if actualTilt != tilt or actualPan != pan:
logging.info("Moving camera to Tilt: %d & Pan: %d"%(tilt, pan))
actualTilt = tilt
actualPan = pan
lockedOn = True
camera.absolute_move(pan, tilt, cameraZoom, cameraMoveSpeed)
time.sleep(cameraDelay)
get_jpeg_request()
#get_bmp_request()
else:
if actualX != follow_x or actualY != follow_y:
actualX = follow_x
actualY = follow_y
#camera.center_move(actualX, actualY, cameraMoveSpeed)
pan_tilt = str(actualX) + "," + str(actualY)
camera._camera_command({'center': pan_tilt, 'speed': cameraMoveSpeed, 'imagewidth': '1280', 'imageheight': '720'})
#time.sleep(cameraDelay)
#if lockedOn == True:
# filename = "capture/{}_{}".format(datetime.now().strftime('%Y-%m-%d-%H-%M-%S'), currentPlane)
# camera.capture("{}.jpeg".format(filename))
Expand All @@ -163,6 +238,8 @@ def moveCamera():
#############################################
def on_message(client, userdata, message):
global currentPlane
global object_timeout

command = str(message.payload.decode("utf-8"))
#rint(command)
try:
Expand All @@ -179,10 +256,17 @@ def on_message(client, userdata, message):
except:
print("Caught it!")

logging.info("{}\tBearing: {} \tElevation: {}".format(update["icao24"],update["bearing"],update["elevation"]))
bearingGood = setPan(update["bearing"])
setTilt(update["elevation"])
currentPlane = update
if message.topic == object_topic:
logging.info("Got Object Topic")
setXY(update["x"], update["y"])
object_timeout = time.mktime(time.gmtime()) + 5
elif message.topic == flight_topic:
logging.info("{}\tBearing: {} \tElevation: {}".format(update["icao24"],update["bearing"],update["elevation"]))
bearingGood = setPan(update["bearing"])
setTilt(update["elevation"])
currentPlane = update
else:
logging.info("Message: {} Object: {} Flight: {}".format(message.topic, object_topic, flight_topic))

def main():
global args
Expand All @@ -194,12 +278,15 @@ def main():
global cameraMoveSpeed
global cameraZoom
global cameraConfig
global flight_topic
global object_topic

parser = argparse.ArgumentParser(description='An MQTT based camera controller')

parser.add_argument('-b', '--bearing', help="What bearing is the font of the PI pointed at (0-360)", default=0)
parser.add_argument('-m', '--mqtt-host', help="MQTT broker hostname", default='127.0.0.1')
parser.add_argument('-t', '--mqtt-topic', help="MQTT topic to subscribe to", default="SkyScan")
parser.add_argument('-t', '--mqtt-flight-topic', help="MQTT topic to subscribe to", default="skyscan/flight/json")
parser.add_argument( '--mqtt-object-topic', help="MQTT topic to subscribe to", default="skyscan/object/json")
parser.add_argument('-u', '--axis-username', help="Username for the Axis camera", required=True)
parser.add_argument('-p', '--axis-password', help="Password for the Axis camera", required=True)
parser.add_argument('-a', '--axis-ip', help="IP address for the Axis camera", required=True)
Expand Down Expand Up @@ -237,14 +324,17 @@ def main():
threading.Thread(target = moveCamera, daemon = True).start()
# Sleep for a bit so we're not hammering the HAT with updates
time.sleep(0.005)
print("connecting to MQTT broker at "+ args.mqtt_host+", channel '"+args.mqtt_topic+"'")
flight_topic=args.mqtt_flight_topic
object_topic = args.mqtt_object_topic
print("connecting to MQTT broker at "+ args.mqtt_host+", channel '"+flight_topic+"'")
client = mqtt.Client("skyscan-axis-ptz-camera-" + ID) #create new instance

client.on_message=on_message #attach function to callback

client.connect(args.mqtt_host) #connect to broker
client.loop_start() #start the loop
client.subscribe(args.mqtt_topic+"/#")
client.subscribe(flight_topic+"/#")
client.subscribe(object_topic+"/#")
client.publish("skyscan/registration", "skyscan-axis-ptz-camera-"+ID+" Registration", 0, False)

#############################################
Expand Down
6 changes: 3 additions & 3 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ services:

tracker:
build: ./tracker
entrypoint: "./flighttracker.py -m mqtt -l ${LAT} -L ${LONG} -a ${ALT} -P skyscan/planes/json -T skyscan/tracking/json -M ${MIN_ELEVATION} -c ${CAMERA_LEAD}"
entrypoint: "./flighttracker.py -m mqtt -l ${LAT} -L ${LONG} -a ${ALT} -P skyscan/planes/json -T skyscan/flight/json -M ${MIN_ELEVATION} -c ${CAMERA_LEAD}"
depends_on:
- mqtt
restart: unless-stopped
Expand All @@ -21,7 +21,7 @@ services:

pan-tilt-pi:
build: ./pan-tilt-pi
entrypoint: "./camera.py -m mqtt -t skyscan/tracking/json"
entrypoint: "./camera.py -m mqtt -t skyscan/flight/json"
volumes:
- /opt/vc:/opt/vc
- ./capture:/app/capture
Expand All @@ -36,7 +36,7 @@ services:

axis-ptz:
build: ./axis-ptz
entrypoint: "./camera.py -m mqtt -t skyscan/tracking/json -u ${AXIS_USERNAME} -p ${AXIS_PASSWORD} -a ${AXIS_IP} -z ${CAMERA_ZOOM} -s ${CAMERA_MOVE_SPEED} -d ${CAMERA_DELAY}"
entrypoint: "./camera.py -m mqtt -t skyscan/flight/json -u ${AXIS_USERNAME} -p ${AXIS_PASSWORD} -a ${AXIS_IP} -z ${CAMERA_ZOOM} -s ${CAMERA_MOVE_SPEED} -d ${CAMERA_DELAY}"
volumes:
- ./capture:/app/capture
depends_on:
Expand Down
2 changes: 2 additions & 0 deletions object-tracker/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
all_models/
**__pycache__
28 changes: 28 additions & 0 deletions object-tracker/CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# How to Contribute

We'd love to accept your patches and contributions to this project. There are
just a few small guidelines you need to follow.

## Contributor License Agreement

Contributions to this project must be accompanied by a Contributor License
Agreement. You (or your employer) retain the copyright to your contribution;
this simply gives us permission to use and redistribute your contributions as
part of the project. Head over to <https://cla.developers.google.com/> to see
your current agreements on file or to sign a new one.

You generally only need to submit a CLA once, so if you've already submitted one
(even if it was for a different project), you probably don't need to do it
again.

## Code reviews

All submissions, including submissions by project members, require review. We
use GitHub pull requests for this purpose. Consult
[GitHub Help](https://help.github.com/articles/about-pull-requests/) for more
information on using pull requests.

## Community Guidelines

This project follows [Google's Open Source Community
Guidelines](https://opensource.google.com/conduct/).
Loading