In a previous post, I described the construction of a simple networked sky camera built with a Raspberry Pi. I was pleased with how well it worked, but quickly figured out that manually reviewing the more than 5000 frames it generated per night was a drag.
The following describes a Python script that makes uses calls to ImageMagick on a Linux computer to identify frames that potentially contain something interesting. In a typical night, it reduces the frames for manual review from 5000+ to a dozen or so, and it picks up lighter streaks than I tend to see rapidly skimming through by hand. There are certainly more sophisticated ways to do that, but I was surprised at how well this works. Specific drawbacks are mentioned below.
Things you are likely to catch with a sky camera
My sky camera exposes for 10 seconds per frame. As a result, a bright moving object will leave a line. You can interpret the line to determine what it is.
Airplanes that fly at night are required to have strobe lights on the wing tips and vertical stabilizer. As a result, they produce a distictive dot-dash-dot-dash pattern as the fly across the sky during a long exposure, which is easy for a person to identify.
Also, they cross from one side of the frame to the other, and take several frames to do it - typically three to four. They don't appear in the middle of the sky and then disappear after short distances.
2) Satellites. Fewer than airplanes, and they are fairly easily distinguished. First, they don't have strobes, so they appear as a fairly consistent brightness that travels in a straight line. They also usually take 3-4 frames to cross from one side to the other. If it's a straight, non-strobing line that lasts more than two frames, it's not a meteor, since it is travelling too slowly across the sky.
A satellite - 1 frame of 3
3) Meteors. If it appears in no more than 2 frames (since it's possible for it to occur at the end of one frame and the start of another) and it doesn't cross the whole frame, congratulations - you've probably captured a rock from space! An airplane or satellite is not expected to start in the middle of the sky, but a meteor can.
Orionid meteor - Oct 2017. Stack of two sequential images that the meteor was on, cropped
Finding frames with possible objects in them
If you are running your sky camera on a clear night, detection of things moving can be as easy as identifying frames that are significantly different from the frame before it. Since the Earth is rotating, we expect it to change a little, so a "fuzz" factor is applied - a small amount of change is ignored. If that threshold is exceeded, though, we should set the frame aside for a person to look at. This simple operation sifts through thousands of frames and pulls out the ones that have something happening remarkably well.
Of course, if you have trees or clouds in your frame, it's likely that motion of those will trigger it too - a false positive. Clouds can be identified reasonably well by simply dropping frames that exceed a certain brightness threshold, but that increases computing time and you might miss a frame that has something interesting in it that also has a cloud. It's a tradeoff. At the moment, I only capture images on pretty clear nights, so I leave the brightness thresholding turned off to speed processing. If you want the function enabled, you can just uncomment the line in main() that calls it.
Running the program
The program currently runs on a Linux computer with Python and ImageMagick installed. Most distros have these already. An example command looks like this. The first argument is the directory containing the images you captured, and the second is your desired output directory. Frames that the program determines may be interesting are copied to the output folder.
./findMeteors.py 20-10-2017 20-10-2017-output/
You must assign the program file execute privileges on your Linux box - usually, this will do the trick:
chmod u+x findmeteors.py
Program listing and download
Listing follows... please let me know if you find it useful.
#!/usr/bin/env python import sys import os import subprocess import shutil def removeBrightImages(images, threshold): #The average graylevel of an image may be found using the string format "%[mean]" #convert image -format "%[mean]" info: count = 0 darkFrames =  for image in images: if count > 0: command = 'convert ' + image + ' -format "%[mean]" info:' try: val = subprocess.check_output(command,shell=True) except subprocess.CalledProcessError as e: val = e.output if float(val) < threshold: print "Adding " + image + ": brightness under threshold, queued for comparison" darkFrames.append(image) else: print "Rejecting " + image + ": brightness exceeds threshold" count = count + 1 print darkFrames return darkFrames def findChanges(images, fuzz, threshold): #fuzz is currently hardcoded below count = 0 changedFrames =  commandList =  for image in images: if count > 0: #Trying to do this the better way with arguments and no shell=True results in the conversion #of the output to an int failing below, and I have not figured out why. #print "Comparing " + lastImage + " to " + image command = "compare -metric ae -fuzz 15% " + lastImage + " " + image + " null: 2>&1" try: val = subprocess.check_output(command,shell=True) #The compare program returns 2 on error otherwise 0 if the images are similar or 1 if they are dissimilar. except subprocess.CalledProcessError as e: val = e.output if e.returncode == 2: print "Error in image comparison routine." sys.exit() if int(val) > threshold: print image + ": " + val + ", item found" changedFrames.append(image) else: print "Checking: " + image lastImage = image count = count + 1 return changedFrames def saveImages(images): for image in images: shutil.copy2(image, sys.argv) def genImageList(): imageList =  for file in os.listdir(sys.argv): if file.endswith(".jpg"): imageList.append(os.path.join(sys.argv, file)) imageList.sort() return imageList def checkArgs(): if len(sys.argv) != 3: print "Usage: ./findMeteors.py