Guest User

Untitled

a guest
Jan 24th, 2019
599
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. import csv
  2. import datetime
  3. import os
  4. import struct
  5. import sys
  6.  
  7.  
  8. def gopro_binary_to_csv(filename):
  9.     """Essentially a reimplementation of
  10.    https://github.com/JuanIrache/gopro-utils in python.
  11.  
  12.    Takes an output file location to write to. This will parse a GoPro
  13.    binary data file, and turn it into a CSV we can use to load data.
  14.    That binary data file can be created with:
  15.        ffmpeg -y -i GOPR0001.MP4 -codec copy \
  16.                -map 0:3 -f rawvideo GOPR0001.bin
  17.    """
  18.     if os.path.isfile('{}.csv'.format(filename)):
  19.         # We've already created the file.
  20.         return
  21.     label_length = 4
  22.     desc_length = 4
  23.     # Set default scale values so we can always use it.
  24.     scales = [1, 1, 1, 1]
  25.     # Decide if we have a GPS fix and should start recording data.
  26.     gps_fix = 0
  27.     gps_accuracy = 9999
  28.     okay_to_record = False
  29.     data = []
  30.     current_data = {}
  31.     with open(filename, 'rb') as gopro_binary:
  32.         while True:
  33.             try:
  34.                 label_string = str(gopro_binary.read(label_length))
  35.                 desc = struct.unpack('>cBBB', gopro_binary.read(desc_length))
  36.             except struct.error:
  37.                 break
  38.             # If the first byte of the description string is zero, there
  39.             # is no length.
  40.             data_type = desc[0]
  41.             if data_type == b'\x00':
  42.                 continue
  43.             # If the label is empty, skip a packet.
  44.             if label_string == "EMPT":
  45.                 gopro_binary.read(4)
  46.                 continue
  47.             # Get the size and length of data.
  48.             val_size = desc[1]
  49.             num_values = desc[2] << 8 | desc[3]
  50.             data_length = val_size * num_values
  51.  
  52.             if label_string == "SCAL":
  53.                 # Get the scale to apply to subsequent values.
  54.                 scales = []
  55.                 for i in range(num_values):
  56.                     if val_size == 2:
  57.                         scales.append(int(struct.unpack('>H', gopro_binary.read(2))[0]))
  58.                     elif val_size == 4:
  59.                         scales.append(int(struct.unpack('>I', gopro_binary.read(4))[0]))
  60.                     else:
  61.                         raise Exception("Unknown val_size for scales. Expected 2 or 4, got {}".format(val_size))
  62.             else:
  63.                 for value in range(num_values):
  64.                     if label_string == "GPS5":
  65.                         current_gps_data = {}
  66.                         if val_size != 20:
  67.                             raise Exception("Invalid data length for GPS5 data type. Expected 20 got {}.".format(
  68.                                 val_size))
  69.                         latitude, longitude, altitude, speed, speed3d = struct.unpack(
  70.                             '>iiiii', gopro_binary.read(val_size))
  71.                         if okay_to_record:
  72.                             current_gps_data["latitude"] = float(latitude) / scales[0]
  73.                             current_gps_data["longitude"] = float(longitude) / scales[1]
  74.                             current_gps_data["speedmps"] = float(speed) / scales[3]
  75.                             current_data["gps_data"].append(current_gps_data)
  76.                     elif label_string == "GPSU":
  77.                         # Only append to data if we have some GPS data.
  78.                         if current_data and current_data['gps_data']:
  79.                             data.append(current_data)
  80.                         timestamp = datetime.datetime.strptime(
  81.                             gopro_binary.read(data_length).strip().decode(), '%y%m%d%H%M%S.%f')
  82.                         current_data = {'timestamp': timestamp, 'gps_data': []}
  83.                     elif label_string == 'GPSF':
  84.                         # GPS Fix. Per https://github.com/gopro/gpmf-parser:
  85.                         # Within the GPS stream: 0 - no lock, 2 or 3 - 2D or 3D Lock.
  86.                         gps_fix = int(struct.unpack('>I', gopro_binary.read(val_size))[0])
  87.                     elif label_string == 'GPSP':
  88.                         # GPS Accuracy. Per https://github.com/gopro/gpmf-parser:
  89.                         # Within the GPS stream, under 500 is good.
  90.                         gps_accuracy = int(struct.unpack('>H', gopro_binary.read(val_size))[0])
  91.                     else:
  92.                         # Just skip on by the data_length, this is a data
  93.                         # type we don't care about.
  94.                         gopro_binary.read(val_size)
  95.                     # Decide whether we want to record data.
  96.                     okay_to_record = gps_fix in [2, 3] and gps_accuracy < 500
  97.             # Data is always packed to four bytes, so skip to the next
  98.             # four byte chunk if we're not currently there.
  99.             mod = data_length % 4
  100.             if mod:
  101.                 gopro_binary.read(4 - mod)
  102.  
  103.         # Now we've got all the data, we need to populate timestamps.
  104.         csv_data = []
  105.         for index, row in enumerate(data):
  106.             try:
  107.                 start_time = row['timestamp']
  108.             except KeyError:
  109.                 if index == 0:
  110.                     # Let's just assume it's one second before the next
  111.                     # available timestamp.
  112.                     start_time = data[index + 1]['timestamp'] - datetime.timedelta(seconds=1)
  113.                 else:
  114.                     raise
  115.             if index == len(data) - 1:
  116.                 end_time = start_time + datetime.timedelta(seconds=1)
  117.             else:
  118.                 end_time = data[index + 1]['timestamp']
  119.             step = (end_time - start_time) / len(row['gps_data'])
  120.             for gps_index, gps_datum in enumerate(row['gps_data']):
  121.                 gps_datum['timestamp'] = (start_time + (gps_index * step)).strftime('%Y-%m-%d %H:%M:%S.%f')
  122.                 csv_data.append(gps_datum)
  123.  
  124.         # Write out our data to a CSV file.
  125.         with open('{}.csv'.format(filename), 'w') as csvfile:
  126.             fieldnames = ['timestamp', 'latitude', 'longitude', 'speedmps']
  127.             writer = csv.DictWriter(csvfile, fieldnames=fieldnames)
  128.             writer.writeheader()
  129.             for row in csv_data:
  130.                 writer.writerow(row)
  131.  
  132.  
  133. if __name__ == "__main__":
  134.     gopro_binary_to_csv(sys.argv[1])
RAW Paste Data