7
\$\begingroup\$

I have a paranoia about accidentally formatting SD cards which contain photos I haven't uploaded yet. I try to be careful about it, but since I'm sometimes juggling two or three cards, I still get nervous.

I have an import script which sucks in photos and verifies correct transfer, but I don't have a good way to then mark the SD card as "safe to delete". Obviously, there are a few things I could do in the physical world (lock and unlock the card, or put it in a special location, etc.), but I'm sometimes absent-minded or distracted and I'd rather the computer take care of it.

Of course, I could also format the card itself as part of the script, but... it seems like that's actually primarily introducing another possible disaster, if my error handling has bugs, for example. Additionally, I like to wait until later to format the card, because my upload process just saves locally, while a day later the overnight backup will have copied them offsite.

One idea I had was to write a JPEG (or other image) to the card with a nice green background and the text "safe to format!" — then, when I see that with the card in the camera, I'd know to trigger the in-camera format options.

But, I can't seem to get the camera to actually display any image I construct. Is there particular metadata, location, or other trick that will get the camera to recognize this file?

\$\endgroup\$
10
  • 2
    \$\begingroup\$ What's the difference between having your script delete the files vs. letting it put a picture on the card so that you format the card on camera? How does the latter prevent the possible disasters that stop you from doing the former? \$\endgroup\$
    – null
    Commented Sep 1, 2016 at 15:50
  • 1
    \$\begingroup\$ @null With the latter, there's no inherently-destructive automated behavior. Let's say I make an error in changing the earlier part of the script and the files are instantly /dev/nulled instead of saved, and the script continues on merrily to the format before I have a chance to intervene. Oops. \$\endgroup\$
    – mattdm
    Commented Sep 1, 2016 at 15:58
  • 1
    \$\begingroup\$ Of course I will, but... by definition, bugs that make it through are usually ones you didn't test for. Whereas "don't format the card unless it has the 'safe' marker" seems like a pretty easy habit to establish. \$\endgroup\$
    – mattdm
    Commented Sep 1, 2016 at 16:56
  • 1
    \$\begingroup\$ Which camera is it? Most Fuji show images taken from another camera with a small 'gift' icon. Try downloading a non-Fuji image onto the card to see if it works. \$\endgroup\$
    – Itai
    Commented Sep 1, 2016 at 17:47
  • 1
    \$\begingroup\$ @mattdm About ~5 years ago, I was able to inject computer-generated images onto an SD card and make the camera show it. My trick was to create baseline 4:2:2 Huffman JPEG images where the width and height are multiples of 8. (Avoid progressive, 4:4:4, and arithmetic coding.) Let me know if this works for you or you need additional info. I hope this can be turned into a full answer. \$\endgroup\$
    – Nayuki
    Commented Sep 1, 2016 at 23:06

2 Answers 2

4
\$\begingroup\$

At the risk of just dumping irrelevant code, here's a Perl program that I used for a similar purpose - transferring images to a printer with a card reader (before I was able to locate the printer near enough to use its USB connection). It has some built-in assumptions (that card filesystems are user-mountable under /media/card/<card-type>) but you should be able to discard the mount/umount part if it's not relevant to you. (If I were writing it again from scratch, I'd probably use pmount instead.)

There's also an attempt to give non-JPEG images a suitable thumbnail file as prescribed by DCIM; I can confirm that this works on my Canon DSLR, but I can't remember whether I tested it on a Fuji. If I did, it would be on a F30 or F60 compact.

FWIW, my card-reading script (not shown) deletes each image from the card filesystem when it has been correctly written to my archive directory (and marked readonly). I have a --keep option to override that, which is useful when testing changes to the script. I never re-format the cards.

The code

#!/usr/bin/perl
use strict;
use Getopt::Long;

# Usage: write-image.pl file... dest
# dest is a dcf root directory - i.e. it contains a dcim subdir.

my $verbose = '';
my $rotate = '';
my $clear = '';

GetOptions ("clear!" => \$clear,
            "rotate!" => \$rotate,
            "verbose!" => \$verbose);

my $basedir = "/media/card";
if (@ARGV < 2) {
    die "not enough arguments\n";
}

my $dest = pop;
$dest = "$basedir/$dest" unless $dest =~ /^\//;

my $mounted = -d "$dest/dcim";
unless ($mounted) {
    print "mount $dest\n" if $verbose;
    system("mount", "$dest") == 0 or die;
}

if ($clear && -d "$dest/dcim") {
    clear(<$dest/dcim/*>);
}

# scan image directories
my $dirno = 99;
my $destdir;
foreach (<$dest/dcim/*>) {
    $dirno = $1 if /\/([1-9][0-9][0-9])/;;
    $destdir = $_ if /\/([1-9][0-9][0-9])linux$/;
}

unless ($destdir) {
    die if ++$dirno > 999;
    $destdir = "$dest/dcim/${dirno}linux";
    print "Adding new directory $destdir\n" if $verbose;
    -d "$dest/dcim" or mkdir"$dest/dcim" or die;
    mkdir $destdir or die;
}

# scan files
my $fileno = 0;
foreach (<$destdir/????[0-9][0-9][0-9][0-9].*>) {
    /\/....([0-9][0-9][0-9][0-9])\..*/;
    $fileno = $1 if $1 > $fileno;
}

sub clear(@) {
    my $f;
    foreach $f (@_) {
         if (-d $f) {
             &clear(<$f/*>);
             #print STDERR "rmdir  $f\n";
             rmdir $f;
         } else {
             #print STDERR "unlink $f\n";
             unlink $f;
         }
    }
}

#do it
foreach (@ARGV) {
    if (++$fileno > 9999) {
        die if ++$dirno > 999;
        $fileno = 1;
        $destdir = "$dest/dcim/${dirno}linux";
        mkdir $destdir or die;
    }
    my $destfile = sprintf "$destdir/img_%04d", $fileno;
    my $suffix = "";
    if (/\.[^.]+\Z/) {
        $suffix = $&;
    }
    #print "$_ -> $destfile$suffix\n" if $verbose;
    print "$_: " if $verbose;   # no newline!
    if ("$suffix" eq ".jpg") {
        if ($rotate) {
            system("( jpegtran -rot 90 -perfect $_ || djpeg $_ | pnmflip -cw | cjpeg -quality 95 -dct float -sample 1x1 ) > $destfile.jpg && exiftran -g -i $destfile.jpg") == 0 || die;
        } else {
            system("exiftran", "-g", "-o", "$destfile.jpg", "$_") == 0 || die;
            print sprintf(" -> img_%04d$suffix\n", $fileno) if $verbose;
        }
    } else {
        system("/bin/cp $_ $destfile$suffix") == 0 || die "$!\n";
        system("anytopnm $_ 2>&- | pnmscale -xysize 160 120 | pnmpad -width 160 -height 120 | pnmtojpeg >$destfile.thm") ==0 || die "$!\n";
        print "\n" if $verbose;
    }
}

unless ($mounted) {
    print "umount $dest\n" if $verbose;
    system("umount", "$dest") == 0 or die;
}

Again, I apologise for the length. But it should now be clear where to write the image in the card's filesystem: in particular, according to the DCIM standard, it must be in DCIM/nnnAAAAA/AAAAnnnn.ext, where n is numeric, A is any character¹, and ext is JPG or THM depending on whether it's the object itself or a separate thumbnail. If the name doesn't fit this pattern, the camera will likely ignore it.

Note also the use of exiftran -g to add an EXIF thumbnail (of the correct size) to an image that doesn't have one. That is likely important for your use, as cameras tend to rely on the thumbnail for image preview. The one created by GIMP is probably the wrong size, and may cause your camera to ignore the image, display a placeholder or even report a read error.


¹ That is, any character within the quite restricted set allowed by DCIM: 123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_.

\$\endgroup\$
5
  • \$\begingroup\$ Ah — yeah, my created image had no thumbnail, and I didn't name it according to the DCIM specs. I'll try both things. \$\endgroup\$
    – mattdm
    Commented Sep 2, 2016 at 13:01
  • \$\begingroup\$ This worked. I had to create the thumbnail with exiftran -g, because for some reason the one created by GIMP causes a read error (wrong size, perhaps). Have a file with no thumbnail also causes a read error. (If the file is named according to the DCIM spec, that is; otherwise, it's just not processed.) \$\endgroup\$
    – mattdm
    Commented Sep 5, 2016 at 17:14
  • \$\begingroup\$ @matt, thanks for the extra information; I've updated. I think you have identified why the exiftran line is in there (I'm a GIMPer too). It's nice to know that my old, no-longer-used script was worth hanging on to! :-) \$\endgroup\$ Commented Sep 6, 2016 at 12:27
  • 1
    \$\begingroup\$ Another fun fact: using DCIM/999_AAAA/AAAA9999.JPG causes the camera to display an error (I forget the exact phrasing, but something about hitting a numeric limit) and not respond to anything — you can't even format the card. \$\endgroup\$
    – mattdm
    Commented Sep 6, 2016 at 13:32
  • \$\begingroup\$ Sadly, this approach totally fails with my X-T2. Not sure why. \$\endgroup\$
    – mattdm
    Commented Mar 15, 2017 at 23:15
16
\$\begingroup\$

I don't know what is required to recognize the "foreign" image, probably something in the Exif... but for your goal, just create the image with your message, with appropriate background, and take a picture of it when showing on the home computer screen. Then it is already in the camera.

\$\endgroup\$
8
  • 1
    \$\begingroup\$ LOL! Very elegant! :-) \$\endgroup\$
    – TFuto
    Commented Sep 1, 2016 at 16:13
  • 1
    \$\begingroup\$ If he is going to put the card in the camera and then take a picture of it to show that it's safe to delete the images, why not just delete the images at that time instead of taking the picture? Taking the picture doesn't seem to add any additional measure of safety. \$\endgroup\$
    – Johnny
    Commented Sep 1, 2016 at 21:04
  • 1
    \$\begingroup\$ The whole point is to script the operation so that it can be done without thinking \$\endgroup\$
    – Nayuki
    Commented Sep 1, 2016 at 23:04
  • 1
    \$\begingroup\$ WayneF and osullic mean making his import script write that "proper" image to the card when the upload of all images is complete. \$\endgroup\$ Commented Sep 1, 2016 at 23:05
  • 3
    \$\begingroup\$ Instead of taking a new "safe to delete" picture every time, just take one "safe to delete" picture, transfer it to/save it in the computer, and you now have an image that you can copy to the camera at the end of the script. \$\endgroup\$ Commented Sep 2, 2016 at 7:02

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.