pageauc / speed-camera

A Unix, Windows, Raspberry Pi Object Speed Camera using python, opencv, video streaming, motion tracking. Includes a Standalone Web Server Interface, Image Search using opencv template match and a whiptail Admin Menu Interface Includes picam and webcam Plugins for motion track security camera configuration including rclone sync script. watch-app allows remotely controller camera configuration from a remote storage service name. Uses sqlite3 and gnuplot for reporting. Recently added openalpr license plate reader support.
Apache License 2.0
987 stars 173 forks source link

Elasticsearch Integration #36

Closed richiejarvis closed 5 years ago

richiejarvis commented 5 years ago

Hi Claude,

Great work with this package. We in the village of Chailey, UK want to monitor the amount and speed of traffic on the A275. The road is a major trunk road, but single-lane, and 40mph for most of the village. https://www.google.com/maps/place/50°56'18.7"N+0°01'18.8"W

The plan is to have multiple raspberry pi's with your software on running. We then would like to merge the data and display it. I happen to work for elastic.co, so have been playing around on my ElasticStack lab at home. I've put this simple graph-set together by using logstash to read the csv file, and import into elasticsearch. I am having to run logstash on a separate server and xfer the csv file there first at the moment. The fingerprint part gives each detection a unique stamp to avoid duplicates.

For reference, here is the logstash config:

input {
  file {
    path => "/speed-camera/speed-cam.csv"
    sincedb_path => "/tmp/csv.db"
    start_position => "beginning"
  }
}
filter {
  csv {
     separator => ","
     columns => ["Date","Hour","Minute","Speed","Unit","Image","X","Y","W","H","Area","Direction"]
  }
  fingerprint {
          source => "message"
          target => "[@metadata][fingerprint]"
          method => "SHA1"
          key => "ChaileySpeed"
          base64encode => true
  }
  mutate {
          add_field => [ "timestamp","%{Date}%{Hour}:%{Minute}" ]
          remove_field => ["Hour","Minute"]
          remove_field => ["message"]
  }
  mutate {
          convert => { "Speed" => "float" }
  }

  date {
          match => ["timestamp","yyyyMMddHH:mm"]
          target => "Date"
  }
  alter {
          condrewrite => [
                  "Direction","L2R", "Southbound",
                  "Direction","R2L", "Northbound"
          ]
  }
}
output {
  elasticsearch {
    hosts => [ "https://my-host:9243" ]
    cacert => "/etc/logstash/certs/ca.crt"
    user => "redacted"
    password => "redacted"
    index => "speedcam-%{+YYYY.MM.dd}"
    document_id => "%{[@metadata][fingerprint]}"
  }
  #stdout {codec => "rubydebug"}
}

Here is the dashboard from kibana: Screenshot_2019-06-26 SpeedCamera Dashboard - Kibana

I'd like to make it so that the speedcamera program makes a direct REST call to elasticsearch. Is that something you could help with? The REST API call is pretty simple - see https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-index_.html

I would also like to see the ability to calculate speeds differently depending on direction too if possible.

How can I help?

Cheers,

Richie

pageauc commented 5 years ago

Richie

Please note speed data is also stored in a sqlite3 database. You can query that DB for data. I have several scripts that produce graphs using gnuplot graphs. I formatted this data in html format and query can be run from menubox.sh. I have not done much with this project for several months. Extracting data from the sqlite3 db would be easier than using the csv file since you can filter and format the data better by querying the sqlite3 DB. example from sql-speed-gt.sh below. Note $speed is a variable supplied when script is run. I set speed gt to allow eliminating pedestrians or slow moving objects. It can also be used to filter for only faster moving objects. Sometimes my camera picks up birds that fly in front of camera.

sqlite3 data/speed_cam.db -column \ "select log_date, log_hour, count(*) \ from speed \ where ave_speed > $speed \ group by log_date, log_hour \ order by log_date;" > hour_count.txt

The process of sending update data to elastic-search can be a separate process run from a crontab that updates every every specified specified time period eg Minutes, Hours, Days Etc with the latest data updates and need not be part of the speed-cam.py code. If the last transmitted data is recorded then only the delta data records would need to be send. Using the crontab you can schedule elastic-search updates every hour or number of minutes as required.

Not sure what you mean by calculate speeds differently on direction. Currently there is only one calibration for both directions. Not sure if you want separate calibration settings for L2R and R2L or different unit of speed like ft/sec. Needs more explaining.

Note speed camera also includes rclone interface for syncing data to a cloud service. Eg Google Drive, Drop Box etc.

Regards Claude ...

When I get a chance I will look at the REST API.

On Wed, Jun 26, 2019 at 7:01 PM Richie Jarvis notifications@github.com wrote:

Hi Claude,

Great work with this package. We in the village of Chailey, UK want to monitor the amount and speed of traffic on the A275. The road is a major trunk road, but single-lane, and 40mph for most of the village. https://www.google.com/maps/place/50°56'18.7"N+0°01'18.8"W https://www.google.com/maps/place/50%C2%B056'18.7%22N+0%C2%B001'18.8%22W

The plan is to have multiple raspberry pi's with your software on running. We then would like to merge the data and display it. I happen to work for elastic.co, so have been playing around on my ElasticStack lab at home. I've put this simple graph-set together by using logstash to read the csv file, and import into elasticsearch. I am having to run logstash on a separate server and xfer the csv file there first at the moment. The fingerprint part gives each detection a unique stamp to avoid duplicates.

For reference, here is the logstash config:

input {

file {

path => "/speed-camera/speed-cam.csv"

sincedb_path => "/tmp/csv.db"

start_position => "beginning"

}

}

filter {

csv {

 separator => ","

 columns => ["Date","Hour","Minute","Speed","Unit","Image","X","Y","W","H","Area","Direction"]

}

fingerprint {

      source => "message"

      target => "[@metadata][fingerprint]"

      method => "SHA1"

      key => "ChaileySpeed"

      base64encode => true

}

mutate {

      add_field => [ "timestamp","%{Date}%{Hour}:%{Minute}" ]

      remove_field => ["Hour","Minute"]

      remove_field => ["message"]

}

mutate {

      convert => { "Speed" => "float" }

}

date {

      match => ["timestamp","yyyyMMddHH:mm"]

      target => "Date"

}

alter {

      condrewrite => [

              "Direction","L2R", "Southbound",

              "Direction","R2L", "Northbound"

      ]

}

}

output {

elasticsearch {

hosts => [ "https://my-host:9243" ]

cacert => "/etc/logstash/certs/ca.crt"

user => "redacted"

password => "redacted"

index => "speedcam-%{+YYYY.MM.dd}"

document_id => "%{[@metadata][fingerprint]}"

}

stdout {codec => "rubydebug"}

}

Here is the dashboard from kibana: [image: Screenshot_2019-06-26 SpeedCamera Dashboard - Kibana] https://user-images.githubusercontent.com/900210/60220532-44d2c900-986e-11e9-8a85-815888c47610.png

I'd like to make it so that the speedcamera program makes a direct REST call to elasticsearch. Is that something you could help with? The REST API call is pretty simple - see https://www.elastic.co/guide/en/elasticsearch/reference/current/docs-index_.html

I would also like to see the ability to calculate speeds differently depending on direction too if possible.

How can I help?

Cheers,

Richie

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/pageauc/speed-camera/issues/36?email_source=notifications&email_token=ABNPKZHTVT4CTLJDDONVPFLP4PYNVA5CNFSM4H3WW5T2YY3PNVWWK3TUL52HS4DFUVEXG43VMWVGG33NNVSW45C7NFSM4G35UFGQ, or mute the thread https://github.com/notifications/unsubscribe-auth/ABNPKZE4NCB5X27DZTJ75SLP4PYNVANCNFSM4H3WW5TQ .

-- See my YouTube Channel at http://www.youtube.com/user/pageaucp

richiejarvis commented 5 years ago

Hi Claude,

Many thanks for the quick and in-depth reply. I will investigate the SQL option and cloud storage options. What I will probably do is use your example sql query (thank you), and convert the resultset into json for a curl call to input to elasticsearch for now. That is probably easiest.

Not sure what you mean by calculate speeds differently on direction. Currently there is only one calibration for both directions. Not sure if you want separate calibration settings for L2R and R2L or different unit of speed like ft/sec. Needs more explaining.

By this I mean the direction of travel. Folks going Southbound on my camera at 10 pixels smaller than those going Northbound. I would like to be able to set the calculations correspondingly, for example:

L2R = 45 R2L = 55

I am noticing that when I get friends to drive past at 40mph, the closest lane readings are about 10mph over the furthest lane readings. I presume this is explained by the difference in reality vs pixel size?

The DB facilities in the system are already great. What I am trying to do is summarise a number of camera locations in one page. This is what I have so far. This dashboard is showing data from one camera. Once the other cameras are placed around the village, it will be interesting to watch the dataset grow over time.

image

Cheers,

Richie

pageauc commented 5 years ago

Yes I though that is what you meant since the near lane is closer and throws out the calculation. I have know this is an issue. I will work on adding the extra variables to config.py and update the speed-cam.py code to use separate values for each direction. Also will need to update the Wiki. Will let you know when this update is available.

Regards Claude ...

On Fri, Jun 28, 2019 at 7:25 PM Richie Jarvis notifications@github.com wrote:

Hi Claude,

Many thanks for the quick and in-depth reply. I will investigate the SQL option and cloud storage options. What I will probably do is write a simple sql query, and convert that to a curl call to input to elasticsearch for now. That is probably easiest.

Not sure what you mean by calculate speeds differently on direction. Currently there is only one calibration for both directions. Not sure if you want separate calibration settings for L2R and R2L or different unit of speed like ft/sec. Needs more explaining.

By this I mean the direction of travel. Folks going Southbound on my camera at 10 pixels smaller than those going Northbound. I would like to be able to set the calculations correspondingly, for example:

L2R = 45 R2L = 55

I am noticing that when I get friends to drive past at 40mph, the closest lane readings are about 10mph over the furthest lane readings. I presume this is explained by the difference in reality vs pixel size?

The DB facilities in the system are already great. What I am trying to do is summarise a number of camera locations in one page. This is what I have so far. This dashboard is showing data from one camera. Once the other cameras are placed around the village, it will be interesting to watch the dataset grow over time.

[image: image] https://user-images.githubusercontent.com/900210/60376003-fce5aa80-9a03-11e9-9f4c-db820fc338de.png

Cheers,

Richie

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/pageauc/speed-camera/issues/36?email_source=notifications&email_token=ABNPKZHFW2IF55JVBTGOASLP42MXJA5CNFSM4H3WW5T2YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGODY3L6BA#issuecomment-506904324, or mute the thread https://github.com/notifications/unsubscribe-auth/ABNPKZCH7TIAY5FEOSDZU23P42MXJANCNFSM4H3WW5TQ .

-- See my YouTube Channel at http://www.youtube.com/user/pageaucp

pageauc commented 5 years ago

Please note I have update speed-cam.py and config.py to allow separate L2R and R2L speed calibration settings Run the menubox.sh UPGRADE menu pick to install changes. Make sure existing config.py has verbose=True then stop speed-cam.py if running in background from terminal session execute the following commands

./speed-cam.sh stop
./speed-cam.py

Follow instructions to backup existing config.py and copy config.py.new to config.py

Note you will need to manually transfer any custom settings from the bak file to the updated config.py You will need to recalibrate for L2R and R2L using similar or same calibration object (eg vehicle) Once calibration is complete it will be good for any size vehicle..

Please test and let me know if this helps. I have tested and sometimes see a few anomolies but generally it seems to work OK. I suggest you run manually and monitor logging messages. Feedback is appreciated Claude ....

On Fri, Jun 28, 2019 at 8:48 PM Claude Pageau pageauc@gmail.com wrote:

Yes I though that is what you meant since the near lane is closer and throws out the calculation. I have know this is an issue. I will work on adding the extra variables to config.py and update the speed-cam.py code to use separate values for each direction. Also will need to update the Wiki. Will let you know when this update is available.

Regards Claude ...

On Fri, Jun 28, 2019 at 7:25 PM Richie Jarvis notifications@github.com wrote:

Hi Claude,

Many thanks for the quick and in-depth reply. I will investigate the SQL option and cloud storage options. What I will probably do is write a simple sql query, and convert that to a curl call to input to elasticsearch for now. That is probably easiest.

Not sure what you mean by calculate speeds differently on direction. Currently there is only one calibration for both directions. Not sure if you want separate calibration settings for L2R and R2L or different unit of speed like ft/sec. Needs more explaining.

By this I mean the direction of travel. Folks going Southbound on my camera at 10 pixels smaller than those going Northbound. I would like to be able to set the calculations correspondingly, for example:

L2R = 45 R2L = 55

I am noticing that when I get friends to drive past at 40mph, the closest lane readings are about 10mph over the furthest lane readings. I presume this is explained by the difference in reality vs pixel size?

The DB facilities in the system are already great. What I am trying to do is summarise a number of camera locations in one page. This is what I have so far. This dashboard is showing data from one camera. Once the other cameras are placed around the village, it will be interesting to watch the dataset grow over time.

[image: image] https://user-images.githubusercontent.com/900210/60376003-fce5aa80-9a03-11e9-9f4c-db820fc338de.png

Cheers,

Richie

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/pageauc/speed-camera/issues/36?email_source=notifications&email_token=ABNPKZHFW2IF55JVBTGOASLP42MXJA5CNFSM4H3WW5T2YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGODY3L6BA#issuecomment-506904324, or mute the thread https://github.com/notifications/unsubscribe-auth/ABNPKZCH7TIAY5FEOSDZU23P42MXJANCNFSM4H3WW5TQ .

-- See my YouTube Channel at http://www.youtube.com/user/pageaucp

-- See my YouTube Channel at http://www.youtube.com/user/pageaucp

richiejarvis commented 5 years ago

Hi Claude,

I've tested it. Unfortunately, it stops working after one capture. Please see output below:

pi@pi5:~/speed-camera $ ./speed-cam.py
Loading ...
----------------------------------------------------------------------
speed-cam.py 9.20   written by Claude Pageau
----------------------------------------------------------------------
Note: To Send Full Output to File Use command
python -u ./speed-cam.py | tee -a log.txt
Set log_data_to_file=True to Send speed_Data to CSV File speed-cam.log
----------------------------------------------------------------------

Debug Messages .. verbose=True  display_fps=False calibrate=False
                  show_out_range=True
Plugins ......... pluginEnable=False  pluginName=picam720
Calibration ..... cal_obj_px_L2R=60 px  cal_obj_mm_L2R=5182 mm  speed_conv_L2R=0.19320
                  cal_obj_px_R2L=75 px  cal_obj_mm_R2L=5182 mm  speed_conv_R2L=0.15456
                  (Change Settings in /home/pi/speed-camera/config.py)
Logging ......... Log_data_to_CSV=True  log_filename=speed-cam.csv (CSV format)
                  loggingToFile=True  logFilePath=speed-cam.log
                  SQLITE3 DB_PATH=/home/pi/speed-camera/data/speed_cam.db  DB_TABLE=speed
Speed Trigger ... Log only if max_speed_over > 10 mph
                  and track_counter >= 10 consecutive motion events
Exclude Events .. If  x_diff_min < 1 or x_diff_max > 20 px
                  If  y_upper < 130 or y_lower > 180 px
                  or  x_left < 20 or x_right > 300 px
                  If  max_speed_over < 10 mph
                  If  event_timeout > 0.30 seconds Start New Track
                  track_timeout=0.00 sec wait after Track Ends (avoid retrack of same object)
Speed Photo ..... Size=960x720 px  image_bigger=3.0  rotation=0  VFlip=False  HFlip=False
                  image_path=media/images  image_Prefix=speed-
                  image_font_size=12 px high  image_text_bottom=True
Motion Settings . Size=320x240 px  px_to_kph_L2R=0.310920  px_to_kph_R2L=0.248736 speed_units=mph
OpenCV Settings . MIN_AREA=100 sq-px  BLUR_SIZE=10  THRESHOLD_SENSITIVITY=20  CIRCLE_SIZE=5 px
                  WINDOW_BIGGER=1 gui_window_on=False (Display OpenCV Status Windows on GUI Desktop)
                  CAMERA_FRAMERATE=20 fps video stream speed
Sub-Directories . imageSubDirMaxHours=0 (0=off)  imageSubDirMaxFiles=1000 (0=off)
                  imageRecentDir=media/recent imageRecentMax=100 (0=off)
Disk Space  ..... Disabled - spaceTimerHrs=0  Manage Target Free Disk Space. Delete Oldest jpg Files
                  spaceTimerHrs=0 (0=Off) Target spaceFreeMB=500 (min=100 MB)

----------------------------------------------------------------------
Logging to File speed-cam.log (Console Messages Disabled)
Traceback (most recent call last):
  File "./speed-cam.py", line 1466, in <module>
    speed_camera() # run main speed camera processing loop
  File "./speed-cam.py", line 1268, in speed_camera
    % (quote,
NameError: global name 'quote' is not defined

Oh, and I've almost got the sqlite3 => elasticsearch finished. Tomorrow... I'll share it after some zzzz :D

Have a good day!

Cheers,

Richie

richiejarvis commented 5 years ago

Hi Claude,

I think I've found it - this line was missing:

quote = '"'  # Used for creating quote delimited log file of speed data

Waiting for a car at 1.30am ;) ... ... Just had one - and it worked :)

Cheers,

Richie

richiejarvis commented 5 years ago

Btw - this is what I have so-far:

import simplejson
import sqlite3
import time
import sys
import os
import hashlib
import requests
import urllib3
import socket
from subprocess import check_output
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)

DB_PATH = '/home/pi/speed-camera/data/speed_cam.db'
DB_TABLE = 'speed'
REPORT_QUERY = ('''select * from speed''')
IP_ADDRESS = check_output(['hostname', '--all-ip-addresses']).strip()

def Main():
    connection = sqlite3.connect(DB_PATH)
    connection.row_factory = sqlite3.Row
    cursor = connection.cursor()
    cursor.execute(REPORT_QUERY)
    while True:
        row = cursor.fetchone()
        if row is None:
            break
        unique_hash = hashlib.sha1(str(tuple(row)) + IP_ADDRESS).hexdigest()
        if row["direction"] == "L2R":
            direction = "Southbound"
        else:
            direction = "Northbound"
        record = {
                '@timestamp' : make_date(row["log_date"]) + "T" + row["log_hour"] + ":" + row["log_minute"] + ":00",
                'speed' : row["ave_speed"],
                'direction' : direction,
                'source' : IP_ADDRESS
                }
        #print(repr(record))
        url = 'https://172.24.42.100:9243/chailey-' + IP_ADDRESS + '-' + make_date(row["log_date"]) + '/record/'+unique_hash
        resp = requests.post(url,auth=('elastic','yF1PQEh8AfiMquEG0sSW'),verify=False,json=record)
        print(unique_hash + ": " + str(resp.status_code))

    last_date = "log_date"
    cursor.close()
    connection.close

def make_date(string):
    string = string[:4] + '-' + string[4:]
    return string[:7] + '-' + string[7:]

if __name__ == "__main__":
    Main()
pageauc commented 5 years ago

Sorry for the mistake. I tested on my speed camera and added variables to config.py manually and CSV was turned off Release 9.21 fixes problem I have tested with CSV True and it works OK. Just do a menubox.sh UPGRADE menu pick. Your current config.py will not be changed so you should be OK after upgrade. speed-cam.py should work OK but would still appreciate feedback. Thanks Regards Claude ...

On Sat, Jun 29, 2019 at 8:31 PM Richie Jarvis notifications@github.com wrote:

Btw - this is what I have so-far:

import simplejson import sqlite3 import time import sys import os import hashlib import requests import urllib3 import socket from subprocess import check_output urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)

DB_PATH = '/home/pi/speed-camera/data/speed_cam.db' DB_TABLE = 'speed' REPORT_QUERY = ('''select * from speed''') IP_ADDRESS = check_output(['hostname', '--all-ip-addresses']).strip()

def Main(): connection = sqlite3.connect(DB_PATH) connection.row_factory = sqlite3.Row cursor = connection.cursor() cursor.execute(REPORT_QUERY) while True: row = cursor.fetchone() if row is None: break unique_hash = hashlib.sha1(str(tuple(row)) + IP_ADDRESS).hexdigest() if row["direction"] == "L2R": direction = "Southbound" else: direction = "Northbound" record = { '@timestamp' : make_date(row["log_date"]) + "T" + row["log_hour"] + ":" + row["log_minute"] + ":00", 'speed' : row["ave_speed"], 'direction' : direction, 'source' : IP_ADDRESS }

print(repr(record))

    url = 'https://172.24.42.100:9243/chailey-' + IP_ADDRESS + '-' + make_date(row["log_date"]) + '/record/'+unique_hash
    resp = requests.post(url,auth=('elastic','yF1PQEh8AfiMquEG0sSW'),verify=False,json=record)
    print(unique_hash + ": " + str(resp.status_code))

last_date = "log_date"
cursor.close()
connection.close

def make_date(string): string = string[:4] + '-' + string[4:] return string[:7] + '-' + string[7:]

if name == "main": Main()

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/pageauc/speed-camera/issues/36?email_source=notifications&email_token=ABNPKZGCBVO6AFRNQLTZALTP475E5A5CNFSM4H3WW5T2YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGODY4CR6Y#issuecomment-506996987, or mute the thread https://github.com/notifications/unsubscribe-auth/ABNPKZBPKY6BA2HLL2HFXK3P475E5ANCNFSM4H3WW5TQ .

-- See my YouTube Channel at http://www.youtube.com/user/pageaucp

richiejarvis commented 5 years ago

Hi Claude,

Many many thanks. I am testing at the moment. I will let you know how it goes.

I've put my speedcam2es into: https://github.com/richiejarvis/speedcam2es

I'll report back once I have more updates.

Cheers,

Richie