Created
August 28, 2016 14:58
-
-
Save bcyrill/e59fda6c7ffe23c7c4b08a990804b269 to your computer and use it in GitHub Desktop.
Python script to inject the EOI marker into panoramic photos created by the S7 camera. In addition the script allows to dump the Samsung specific data that is added to the image
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/python | |
# The Samsung trailer format is based on the implementation from ExifTool | |
# http://www.sno.phy.queensu.ca/~phil/exiftool/ | |
import mmap | |
import struct | |
import os | |
import sys | |
if (len(sys.argv) < 2) or (len(sys.argv) > 3): | |
print("Usage: fix_eoi.py <filename> [dump]") | |
exit() | |
file = sys.argv[1] | |
if len(sys.argv) < 3: | |
dump = 0 | |
elif sys.argv[2] == "dump": | |
dump = 1 | |
else: | |
print("Invalid argument") | |
print("Usage: fix_eoi.py <filename> [dump]") | |
exit() | |
(file_name, file_ext) = os.path.splitext(file) | |
with open(file, 'rb') as fh: | |
m = mmap.mmap(fh.fileno(), 0, access=mmap.ACCESS_READ) | |
b = bytearray(m) | |
trailer_tail = b[-4:] | |
if trailer_tail != 'SEFT': | |
print("No valid Samsung trailer found") | |
exit() | |
else: | |
print("Found SEFT") | |
length = struct.unpack_from("<I",b[-8:-4])[0] | |
trailer = b[-(8+length):] | |
endPos = len(b) | |
dirPos = endPos-(8+length) | |
if trailer[0:4] != 'SEFH': | |
print("No valid Samsung trailer found") | |
exit() | |
else: | |
print("Found SEFH") | |
version = struct.unpack_from("<I",trailer[4:8])[0] | |
if version != 101: | |
print("Unknown Samsung trailer version") | |
exit() | |
count = struct.unpack_from("<I",trailer[8:12])[0] | |
firstBlock = 0 | |
is_pano = 0 | |
for index in range(0, count): | |
entry = 12 + 12 * index; | |
type = struct.unpack_from("<H",trailer[entry+2:entry+4])[0] | |
noff = struct.unpack_from("<I",trailer[entry+4:entry+8])[0] | |
size = struct.unpack_from("<I",trailer[entry+8:entry+12])[0] | |
if firstBlock < noff: | |
firstBlock = noff | |
entryPos = dirPos - noff | |
entryLen = size | |
data = b[entryPos:entryPos+entryLen] | |
# Validate as the type has to match the SEFH/SEFT entry type | |
entry_type = struct.unpack_from("<H",data[2:4])[0] | |
if type != entry_type: | |
print("Missmatch detected. Aborting!") | |
exit() | |
entry_offset = struct.unpack_from("<I",data[4:8])[0] | |
entry_name = data[8:8+entry_offset].decode("utf-8") | |
if entry_name == "Panorama_Shot_Info": | |
is_pano = 1 | |
entry_data = data[8+entry_offset:] | |
if dump: | |
print("Dumping: %s" % entry_name) | |
with open(file_name + '_' + entry_name, 'wb') as f: | |
f.write(entry_data) | |
if not is_pano: | |
print "No Panorama info found" | |
exit() | |
dataPos = dirPos - firstBlock | |
dirLen = endPos - dataPos | |
eoi = struct.unpack_from(">H",b[dataPos-2:dataPos])[0] | |
if eoi == 0xffd9: | |
print("Found EOI") | |
exit() | |
else: | |
print("Inserting EOI") | |
with open(file_name + '_pano' + file_ext, 'wb') as f: | |
f.write(b[0:dataPos]) | |
f.write(bytearray(b'\xff\xd9')) | |
f.write(b[dataPos:]) | |
I made a rust version which can strip the files of the videos and reduce the size in about half. This version merely makes the file a valid JPEG file but the video remains in there. I also made some binaries if somebody is interested: https://github.com/frapa/sampan
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Added a fork that works w. S8+, plus some decorations for batch friendliness.