7

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?

  • 2
    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? – null Sep 1 '16 at 15:50
  • 1
    @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. – mattdm Sep 1 '16 at 15:58
  • 1
    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. – mattdm Sep 1 '16 at 16:56
  • 1
    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. – Itai Sep 1 '16 at 17:47
  • 1
    @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. – Nayuki Sep 1 '16 at 23:06
4

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_.

  • Ah — yeah, my created image had no thumbnail, and I didn't name it according to the DCIM specs. I'll try both things. – mattdm Sep 2 '16 at 13:01
  • 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.) – mattdm Sep 5 '16 at 17:14
  • @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! :-) – Toby Speight Sep 6 '16 at 12:27
  • 1
    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. – mattdm Sep 6 '16 at 13:32
  • Sadly, this approach totally fails with my X-T2. Not sure why. – mattdm Mar 15 '17 at 23:15
16

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.

  • 1
    LOL! Very elegant! :-) – TFuto Sep 1 '16 at 16:13
  • 1
    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. – Johnny Sep 1 '16 at 21:04
  • 1
    The whole point is to script the operation so that it can be done without thinking – Nayuki Sep 1 '16 at 23:04
  • 1
    WayneF and osullic mean making his import script write that "proper" image to the card when the upload of all images is complete. – Jerome Jaglale Sep 1 '16 at 23:05
  • 3
    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. – Florent Bayle Sep 2 '16 at 7:02

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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