Lightning strikes and Python

2015-05-10 13:58 | Categories: In English, Nuotraukos, Projektai, Time-lapse, Užrašai, Video

Gp027304.mp4_045126_I was always fascinated by lightning. Long time ago, being a teenager I shoot lightning strikes many times with some degree of success. At that time I was using my dad’s old Zenit camera. While lightnings are always the same, equipment advances very rapidly. Today I will talk about spotting interesting frames from video and detecting exact moment of strike using Python.


This is relatively simple task for a script – measure difference between two frames and if it exceeds predetermined value assume this is lightning. Key idea – lightning produces lots of light and alters many pixels. So, being Python and computer vision enthusiast, I wrote quick and dirty OpenCV script to find specific frames in long video to spot lightning strikes.

It happened that script is pretty fast and processes 15minutes of raw 1080p@60 footage from GOPRO camera in 8 minutes on i7 CPU.

Besides spotting interesting pictures it saves motion values to CSV file for further analysis using other tools. Here is how motion time-chart looks like. In general this is fast motion detection script without localization feature.

Setup was out of the box – GOPRO 3 black action camera without any harness. It was just recording plain video. All processing was done on computer.


Motion chart shows 12 possible lightning strikes



Animated GIF – Lightning strike to TV tower


Animated GIF – lightning in Pašilaičiai


Animated GIF – Lightning over Gariūai


Lightning strike to TV tower

Making GIF with ImageMagick

convert.exe -resize 640x360 -layers OptimizeTransparency +map -delay 15 -loop 0 gif\*.jpg gif\out.gif

Quick and dirty Python script for motion detection

import sys
import cv2
import as cv
import numpy as np
from PIL import Image
import progress
import time

SCALE = 0.5

start = time.time()

def count_diff(img1, img2):
    small1 = cv2.resize(img1, (0,0), fx=SCALE, fy=SCALE)
    small2 = cv2.resize(img2, (0,0), fx=SCALE, fy=SCALE)
    #cv2.imshow('frame', small2)
    diff = cv2.absdiff(small1, small2)
    diff = cv2.cvtColor(diff, cv2.COLOR_RGB2GRAY)
    frame_delta1 = cv2.threshold(diff, NOISE_CUTOFF, 255, 3)[1]
    frame_delta1_color = cv2.cvtColor(frame_delta1, cv2.COLOR_GRAY2RGB)
    delta_count1 = cv2.countNonZero(frame_delta1)

    return delta_count1

filename = sys.argv[1]
video = cv2.VideoCapture(filename)

nframes = (int)(video.get(cv.CV_CAP_PROP_FRAME_COUNT))
width = (int)(video.get(cv.CV_CAP_PROP_FRAME_WIDTH))
height = (int)(video.get(cv.CV_CAP_PROP_FRAME_HEIGHT))
fps= (int)(video.get(cv.CV_CAP_PROP_FPS))

frame_count = 0

print "[i] Frame size: ", width, height
print "[i] Total frames:", nframes
print "[i] Fps:", fps

fff = open(filename+".csv", 'w')

flag, frame0 =
treshold = int(sys.argv[2])
strikes = 0

p0 = progress.AnimatedProgressBar(end=nframes, width=80)
for f in xrange(nframes-1):
    p0 + 1

    flag, frame1 =
    diff1  = count_diff(frame0, frame1)
    name = filename+"_%06d.jpg" % f

    if diff1 > treshold:
        cv2.imwrite(name, frame1)
        strikes = strikes + 1

        #small = cv2.resize(frame1, (0,0), fx=SCALE, fy=SCALE)
        #cv2.imshow('frame', small)

    text = str(f)+', '+str(diff1)
    #print text
    fff.write(text  + '\n')
    frame0 = frame1

print '[i] Strikes: ', strikes
print '[i] elapsed time:', time.time() - start


GOPRO CMOS sensor has rolling shutter and has no manual exposure settings. That’s why many frames are split into dark and light areas. There a good thing to use ND filter to make exposure longer. In this way whole lightning strike will fit into frame and picture will be higher quality. Of course best option is to use global shutter camera, but it is difficult to find this kind of camera.

  1. 2015-05-10 21:12
    Reply | Quote | #1

    It looks fantastic!!! :)

    • Saulius Lukšė
      2015-05-10 21:19
      Reply | Quote | #2

      Thank you Deividai :)

  2. Regiscruzbr
    2015-05-12 02:14
    Reply | Quote | #3

    Great project!!!

    Are you thinking about keep publishing the shots here on your blog?

    I’m also fascinated by lightning, I can stay hours and hours looking at it. We have pretty nice ones here in Brazil.

    Thanks for share.

    • Saulius Lukšė
      2015-05-12 06:25
      Reply | Quote | #4

      Hi Regiscruzbr, thank you for your support! Of course I will publish new shots on my blog. Waiting for new lightning storm. Feel free to share your pictures, Brazil is an amazing place and you should have impressive pictures too.

  3. Ivan
    2015-05-12 06:46
    Reply | Quote | #5

    Great! Here some suggestions: you can also try to capture only frames that are lighter then previous, also OpenCV stores pixels in BGR format (it affects how grayscale image gets computed), blurring images that needs to be compared can also reduce a lot of noise on your graphs.

    small1 = blur(small1,5)
    small2 = blur(small2,5)
    gray1 = cvtColor(small1, cv2.COLOR_BGR2GRAY)
    gray2 = cvtColor(small2, cv2.COLOR_BGR2GRAY)
    mask = compare(gray1, gray2, CMP_GT)
    diff = absdiff(gray1,gray2)
    diff = min(diff,mask)
    frame_delta = cv2.threshold(diff, NOISE_CUTOFF, 255, 3)[1]
    delta_count = cv2.countNonZero(frame_delta)

    • Saulius Lukšė
      2015-05-12 06:47
      Reply | Quote | #6

      Hi Ivan, thank you for suggestion. Will try later today!

  4. Julius
    2015-05-14 16:19
    Reply | Quote | #7

    Awesome project! I have a lot of various videos with lightning strikes from GoPro and other cameras that I would love to use your script on. I have been trying to get the script to run, I have added OpenCV,numphy and Pillow of the modules to Python 2.7 on Windows but I am missing the import progress module could you point me in the right direction where to find it?

  5. stein
    2016-06-10 14:07
    Reply | Quote | #9

    How do I import my video’s to the script.
    (Sorry, I’m a still a noob with programming)

    • Saulius Lukšė
      2016-06-10 14:50

      python script takes two arguments first is video file name, second is threshold. For example [ video.avi 10000] threshold has to be chosen by guess depending on results.

  6. Nathan
    2016-06-25 19:33

    Can’t get it to work. Error that pops up:

    p0 = progress.AnimatedProgressBar(end=nframes, width=80)
    AttributeError: ‘module’ object has no attribute ‘AnimatedProgressBar’

    • Saulius Lukšė
      2016-06-25 22:04

      Hi Nathan,

      I used old animated progress bar library for more visual indication. Feel free to remove it. Lines:

      import progress

      p0 = progress.AnimatedProgressBar(end=nframes, width=80)

      p0 + 1

  7. Daniel
    2016-07-08 23:28

    Hey, this is great! I would love to be able to do this as well, but I have very basic coding experience. I have Python and all the modules loaded, but I get the following message when I try to execute the script:

    File “C:/Users/DDavidson/Desktop/”, line 28, in
    filename = sys.argv[1]

    IndexError: list index out of range

    I have the script and my video both on the desktop. Script is named and Video is named Test.mp4

    Any ideas?

    • Saulius Lukšė
      2016-07-09 07:28

      Hi Daniel, I think you have not provided threshold value. Your command line should look like “ Test.mp4 10000”.

      • Daniel
        2016-07-13 19:00

        Okay, I got that to run, but it is not really processing the mp4 file.
        [i] Frame size: 0 0
        [i] Total frames: 0
        [i] Fps: 0

        [i] Strikes: 0
        [i] elapsed time 0.00999999046326

        and the csv file it generated is blank. Should the video file be avi instead or should that even matter?

        • Saulius Lukšė
          2016-07-13 19:19

          Hi Daniel, yes I guess should be converted or search for instructions on Python forums how to make PIL read MP4 files.