Camera Hacking
Posted by at 04:10 on 28 Oct 2008. There are ? Comments

I've been wondering how many pictures I've taken with my Canon EOS 350D lately. After a lot of research online it seems there are two trains of thought on this subject on the Interwebs. Higher end models store this value internally but accessing it is proprietary - I know that at least the 40D supports using the Canon SDK to find the shutter actuation count, but you have to sign for a license with all kinds of terms and the library is proprietary so this is a non-starter for me.

<p>My Canon EOS 350D
</p>

My Canon EOS 350D

(~)
CameraCanon Eos Digital Rebel Xt
Date20 October 2008 at 12:11
Exposure ModeAperture Priority
Exposure Time1/60 seconds
Aperturef/4
ISO Speed1600
Focal Length105 mm
MeteringPattern
FlashOff

Another approach I found uses a header stored in all RAW files produced by the camera. Here is an article on finding the shutter actuation count on a 20D, but several flaws have been found. This number can be forced to reset with a factory reset (apparently). This number is only 16-bit (max value of 65,536). After my own testing this is not the shutter actuation count, but the number of files that have been saved to a memory card (taking out the card and taking photos doesn't increase the number). The location of this value changes from camera to camera due to strings in the TIFF tags.

The limitations above suck, but I've been the only owner of my camera, I know I haven't taken over 65,000 photos, and I've rarely shot without a card. The last issue can be solved programmatically, so that is what I did.

Assume that there exists a single byte value in every RAW image shot with the camera that increases by one for every picture taken. For any given two pictures the probability of finding this value is low - in fact I bet there are hundreds of values that increase by one, but add another photo or two and the probability rapidly approaches one. Taking this assumption I hacked together a bit of python to search for this value by doing the equivalent of a byte-by-byte diff of any number of RAW files, throwing out any diffs that do not increase by one each time. Using just four photos from my 350D I was able to find the correct value:

./framecount.py IMG_417*
Using 4 images, scanning first 512 KiB
Potential candidate found at 0x944: 79 => 80 => 81 => 82
Found 1 candidates
Unsigned 16-bit integer at 0x943 is 21077 for /Users/dan/IMG_4178.CR2

So I've taken around 21,077 photos since I bought the camera. Not bad :-) The source for the script is provided below for anyone that is interested in trying this on their own camera:

#!/usr/bin/env python
 
import struct
import sys
 
BUFFER_SIZE = 1024
READ_COUNT = 512
 
imgs = []
candidates = []
 
# Open all passed files
for value in sys.argv[1:]:
imgs.append(open(value))
 
print "Using %d images, scanning first %d KiB" % \
(len(imgs), BUFFER_SIZE * READ_COUNT / 1024)
 
for x in range(READ_COUNT):
# Read in chunks of bytes to test
values = []
for image in imgs:
values.append(list(struct.unpack(str(BUFFER_SIZE) + "B",
image.read(BUFFER_SIZE))))

base_pos = x * BUFFER_SIZE

for y in range(BUFFER_SIZE):
# Increment values that come after the max 8-bit uint
overflow = False
for pos in range(len(values)):
if overflow:
values[pos][y] += 255
if values[pos][y] == 255:
overflow = True

# Make sure values are increasing by one each time
failed = False
last = values[0][y]
for pos in range(len(values) - 1):
cur = values[pos + 1][y]
if last != cur - 1:
failed = True
break
last = cur

if not failed:
# Print info about this candidate
value_str = "%s => " * len(values) % \
tuple([value[y] for value in values])
print "Potential candidate found at %s: %s" % \
(str(hex(base_pos + y)), value_str.strip("=> "))
candidates.append(base_pos + y)
 
print "Found %d candidates" % len(candidates)
 
if len(candidates) == 1:
offset = candidates[0] - 1
imgs[-1].seek(offset)
value = struct.unpack("<H", imgs[-1].read(2))[0]
print "Unsigned 16-bit integer at %s is %d for %s" % (str(hex(offset)),
value, sys.argv[-1])
elif len(candidates) > 1:
print "Try passing more photo data to scan to narrow down the number "
"of candidates!"
else:
print "Hmmm, try passing several raw images that were taken one after "
"the other so that the image count increases by one each time!"

Comments

blog comments powered by Disqus