Page 1 of 1

Using ZoneMinder to determine vehicle speed

Posted: Thu Sep 17, 2009 8:15 pm
by ooglek
I've got two kids on a street that is often used as a cut-through. Most people drive nicely, but some people fly through here 10mph+ over 25mph and are on their cell phone. All it takes is for my kid just once to go into the street when they come through and boom -- my kid is dead or in the hospital and the driver goes to jail.

I've got ZoneMinder watching my front yard which includes the street. I used it to help get a traffic study done in our neighborhood by the local DOT. Viewing one day of footage from ZM I was able to run through 24 hours of events in about 20 minutes and counted 1200 cars passing down the street, with the peak between 4-7pm of about 100-120 cars per hour. I'd like to do more, capturing proof of speeders, and show that marked police cars are ineffective at gauging actual speed because people slam on the brakes at the first sign of a Crown Vic, marked or unmarked. Our county won't do anything unless the average speed is 30+ or the 85th percentile are doing 35+. Mostly I want to figure out if I'm being an overprotective dad or if there is a real problem with speeding in our neighborhood.

Since I've been able to do some math, measuring and calculating, and at 15 frames per second, with a distance between driveways of 35 feet and adjusted for the camera's view the distance at the center of the road is 29 feet, I can calculate the rough speed of any vehicle using ZoneMinder.

I can even set up zones for this very purpose! The biggest problem is that I haven't yet figured out how to automate it.

The camera faces east, so right-to-left is north, left-to-right is south.

| Z3 | Z2 | Z1 |

Image

For a northbound vehicle, it would enter the frame (Zone 1) and start alarming. The right-most edge of Zone 2 is the line for which I want to start counting. The right-most edge of Zone 3 is the line for which I want to stop counting.

For a southbound vehicle, it would enter the frame (Zone 3) and start alarming. The left-most edge of Zone 2 is the line for which I want to start counting. The left-most edge of Zone 1 is the line for which I want to stop counting.

Using the northbound vehicle as an example, and assuming our zone sensitivity is the same for all zones, if the time delta 1.10 when Zone 2 starts alarming, and the time delta when Zone 3 first alarms is 1.75:

29 feet per 0.65 seconds (1.75-1.10)

We want to solve for x, where we know 29 feet per 0.65 seconds should equal x miles per hour.

29 feet / 5280 feet per mile = 0.0054924 miles
0.65 seconds / 3600 seconds per hour = 0.0001806 hours

0.0054924 miles / 0.0001806 hours == 30.41 miles per hour.

I can do this manually, but what a pain.

I can boil this down to pseudocode:

Code: Select all

$distanceInFeet = 29;
if (Zone 1 alarms before Zone 2) {
  $time = $TDz3 - $TDz2;
} elseif (Zone 3 alarms before Zone 2) {
  $time = $TDz1 - $TDz2;
} else  {
  // not a valid event for speed calc
}

if (!empty($time)) {
  $speed = round(($distanceInFeet/5280)/($time/3600),2);
}
I haven't rustled the code or DB schema yet, so maybe this will be easy to write a cron job to parse all the events. The biggest issue will be setting the sensitivity just right on each of the zones to make sure all three zones alert at the same rate.

Would love to start some discussion here about it. If it is feasible, I may just write the code for it and offer to integrate it into ZM.

Posted: Thu Sep 17, 2009 9:54 pm
by kingofkya
might be plausible in the database under Events
Id MonitorId Name Cause StartTime EndTime
and on mine start and end time go down to the millisecond

you might have to set the post buffer to a minimum so it trigger 2 events for each bench mark

Interesting idea

Posted: Fri Sep 18, 2009 3:14 am
by ooglek
I think my biggest problem will be the alarmed pixel threshold, making sure the alarm is set off first, then making sure the Stats get written and are very accurate. Once I get things working, I'll have to do some tests!

From the looks of it I can find the Events that have Motion in the right zones using the Events.Notes field, then the order of the zones in the Stats should tell me the direction they were going, and then I should be able to find the first even for the zones.
  1. Find all the Events that contain the zone names I'm looking for in the Events.Notes field.
  2. Parse the Stats table for that EventId to determine direction.
  3. Parse the Stats table to get the timestamp of the first occurrence of the correct zone, and then the first occurrence of the next zone.
  4. Do the math.
  5. Update the Notes field?
I'm kind of at a loss for where to put this new data. Should I put it in a new table? I don't want to modify the schema, but I do want to be able to say "Show me all the events that exceeded 29" and that's more cumbersome as you'd have to load all the rows with "Speed: xx" in the Notes and then in code find the ones where the speed exceeded 29.

Posted: Fri Sep 18, 2009 9:06 am
by kingofkya
there is a notes field that can be used i only copyed part of the data base

Posted: Fri Sep 18, 2009 9:15 am
by kingofkya
Thers also the macgyver solution Radar gun with a camera duck taped to it,

Posted: Fri Sep 18, 2009 7:40 pm
by kylejohnson
This is a very interesting use of ZoneMinder. I would be very interested in any further developments that you make. Keep us posted!

I think I have a solution!

Posted: Wed Sep 30, 2009 8:27 pm
by ooglek
There really isn't much more to do, other than write a cron job after the fact to hit the DB and extract the data. I'll write the code eventually, and maybe we can even integrate it into the base code and have a GUI for it.

Since it only applies to cameras facing a street perpendicularly, and relies on doing some math and geometric calculations to determine actual distance traveled as it relates to the camera view, maybe it won't see the codebase. But what the heck.

Here's an event -- I've uploaded the video, the actual frames, and the frame data from the DB so you can duplicate my work. The files are on drop.io:

http://drop.io/zmspeed [Drop.io]

SQL:

Code: Select all

mysql> select z.Name, f.FrameId, f.Delta 
from Stats s 
inner join Zones z on (s.ZoneId=z.Id) 
inner join Frames f on (s.EventId=f.EventId and s.FrameId=f.FrameId) 
where s.EventId=78886 order by FrameId;
+--------------------------+---------+-------+
| Name                     | FrameId | Delta |
+--------------------------+---------+-------+
| Marshall St North        |      11 |  0.66 | 
| Marshall St North Inside |      11 |  0.66 | 
| Marshall St North Inside |      12 |  0.74 | 
| Marshall St North        |      12 |  0.74 | 
| Marshall St North Inside |      13 |  0.81 | 
| Marshall St North        |      13 |  0.81 | 
| Marshall St North        |      14 |  0.88 | 
| Marshall St North Inside |      14 |  0.88 | 
| Marshall St North Inside |      15 |  0.94 | 
| Marshall St North        |      15 |  0.94 | 
| Marshall St North Inside |      16 |  1.00 | 
| Marshall St South 2      |      19 |  1.20 | 
| Marshall St South        |      20 |  1.27 | 
| Marshall St South 2      |      20 |  1.27 | 
| Marshall St South 2      |      21 |  1.34 | 
| Marshall St South        |      21 |  1.34 | 
| Marshall St South        |      22 |  1.40 | 
| Marshall St South 2      |      22 |  1.40 | 
| Marshall St South 2      |      23 |  1.46 | 
| Marshall St South        |      23 |  1.46 | 
| Marshall St South 2      |      24 |  1.53 | 
| Marshall St South        |      24 |  1.53 | 
| Marshall St South        |      25 |  1.60 | 
+--------------------------+---------+-------+
23 rows in set (0.01 sec)
The zones are set up like this:

Code: Select all

North |                                    Camera View                                            | South
Zones | Marshall North | Marshall North Inside |              | Marshall South 2 | Marshall South |
Image

So for a vehicle traveling North to South, The first Zone, Marshall St North, will alarm. When the Vehicle crosses the line that starts our "zone", the first instance of Marshall St North Inside is the zone we use to subtract the delta.

When the Vehicle first alarms Marshall St South (not 2), it has exited the zone, and we use this delta to get how many seconds it took them to travel the known distance.

For me, that distance is exactly 29 feet. In this case, the last delta is 1.27 (Frame ID 20), and the first delta is 0.66 (FrameID 11).

At 15 frames a second, we expect this time to be 9/15, or 0.6 seconds.

1.27 - 0.66 = 0.61. Excellent.

So 29 feet / 0.61 seconds = 47.5410 feet/second. Divide that by 5280feet/3600 seconds and we get 32.41 mph.

A note about the zones. They are exactly the same size in pixels, set for 30 alarmed pixels each.

Hopefully I can build a proof of concept code that will run a cron job and maybe modify the Event Name or Notes to include the speed.

Posted: Wed Sep 30, 2009 8:45 pm
by ooglek
Upon further review, I suppose I can set up my zones to be in the correct perspective as the camera view. I need to think about it a bit more, because if I change the perspective of the zones, I'll have to change my distance calculations which were modified to account for the fact that the camera view is skewed. Any input from you math majors out there would be good. :-)

To keep it real, I could also average the speeds calculated by the Delta as well as FrameDelta/FrameRate. Theoretically they should be the same, but I don't know how accurate ZM is in the Delta value. On top of it all, I'm running ZM on an Ubuntu VM on my Mac Pro -- who knows if THAT also incorporates some error. However, the rough calculations I've posted above seem to be in the right ballpark.