sorting images to avoid suboptimal searching?

Andreas Schleth schleth_es at web.de
Thu Nov 23 16:56:14 GMT 2023


Am 23.11.23 um 16:56 schrieb Per Funke:
> Hi guys!!
>
> KPhotoAlbum
> Version 5.8.1
>
> Xubuntu 22.04.3 LTS
>
>
> I imported 50000 images and I can truly say, I will be dead before all
> these images
> will get a date (I'm 73yrs now), stopping the complaint window from
> appearing (it never appeared before!?)
> That makes me curious: How "suboptimal" is my search? Just speed? Or,
> is function affected also?
> *The above is my REAL question, the rest is just whining.*

Hi Per,

what complaint window do you mean? "some images have invalid dates" or so?

I scanned some 10000 slides and imported them to KPA a few years ago.
The problem was that all these images had no idea when they were taken
(exif data).

I decided that an approximate date and a correct sorting order are more
important than having the exact date for images from 1982.

So I made a perl script to set the date on a group of images to
equidistant values in a certain date range. The image files need to have
some kind of consecutive numbering in the filename. This script changes
the exif info in the images and tries to set the file creation date to
the same value (if after 1970). For efficient use, I started this with
small shell scripts inside my image folders.

You might need some additional Perl packages but otherwise, this is
plain old Fortran written in Perl.

Try it out with a small subset (copy!) of your images.

See attachments (the "set" shell script is for a folder with k003_01.jpg
... k003_50.jpg)

Best regards, Andreas

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.kde.org/pipermail/kphotoalbum/attachments/20231123/fd13b45e/attachment.htm>
-------------- next part --------------
exif_date_range k003_01 22.5.81	
exif_date_range k003_02 30.5.81 k003_14	31.5.81
exif_date_range k003_15 6.6.81	k003_37 9.6.81
exif_date_range k003_38 17.6.81	k003_44 
exif_date_range k003_45 28.6.81 k003_50
mv new/* .
rmdir new
-------------- next part --------------
#!/usr/bin/perl -w
#  Copyright (C) 2011-2019 Andreas Schleth
#
#  This program is free software; you can redistribute it and/or
#  modify it under the terms of the GNU General Public
#  License as published by the Free Software Foundation; either
#  version 2 of the License, or (at your option) any later version.
#
#  This program is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
#  General Public License for more details.
#
use Image::ExifTool;
use Time::Local;        # - efficiently compute time from local and GMT time
#    $time = timelocal($sec,$min,$hour,$mday,$mon,$year);
use strict;
my $debug=3;
# date: jjjj:mm:dd hh:mm:ss
my $usage = "\nusage: exif_date_range [img1] [date1] [img2] [date2]\n".
    	    "\tdate: dd.mm.[yy]yy[-hh:mm] \n".
	    "Set all dates in all *.jpg-files in the given range equally spaced to dates in date range.\n".
	    "Also updates the file creation date.\nResultfiles are in subdirectory 'new'\n\n";
#
#  get parameters
#
my $img1 = shift || die $usage;
my $dat1 = shift || die $usage;
my $img2 = shift;
my $dat2 = shift;
my $nimg = 0;
my ($base,$field);
#  same day 
$dat2 = $dat1 unless($dat2);
#  1 pic
unless($img2) {
	$img1 =~ s/.jpg$//i;
	$img2 = $img1;
	$dat2 = $dat1;
	$nimg = 1;
} else {
	#
	#  find image range
	#
	my $len = length($img2) > length($img1) ? length($img2) : length($img1);
	if($debug) {
		print "Image 1, Date 1: $img1, $dat1\n";
		print "Image 2, Date 2: $img2, $dat2\n";
		print "Stringlength   : $len\n";
		die("Stop $debug\n") if($debug == 1);
	}
	my $i=0;
	while($i < $len && substr($img1,$i,1) eq substr($img2,$i,1)) {
		$i++;
	}
	#
	#  find number part
	# 
	$base = substr($img1,0,$i);
	print "Image base name: $base\n";
	$img1 =~ s/.jpg$//i;
	$img1 =~ s/^$base//i;
	$img2 =~ s/.jpg$//i;
	$img2 =~ s/^$base//i;
	$field = length($img1);
	print "numberfield $field\n";
	$img1 *= 1.;
	$img2 *= 1.;
	print "Number range: $img1 ... $img2\n";
	$nimg = $img2 - $img1 + 1;
}
#
#  prepare for exiftool attack
#
mkdir "new" unless(-d "new");
my $exifTool = new Image::ExifTool;
$exifTool->Options(Unknown => 1);
my @TAGS = qw(CreateDate DateTimeOriginal ModifyDate);
if($debug) {
	print "No of images: $nimg\n";
	die("Stop $debug\n") if($debug == 2);
}
#
#  Find date/time-range and doit, babe!
#
my ($date,$time1,$time2);
if($nimg > 1) {
	$time1 = &parse_timestring($dat1,1);
	$time2 = &parse_timestring($dat2,2);
	my $deltatime = ($time2 - $time1)/($nimg-1);
	my $dd = int($deltatime/(3600*24));
	my $dh = int(($deltatime - $dd*3600*24)/3600);
	my $dm = int(($deltatime - $dd*3600*24 - $dh*3600)/6)/10;
	print "Delta days: $dd  delta hours: $dh  delta minutes: $dm\n";
	my $dtime = -$deltatime;
	for(my $i=$img1; $i<=$img2; $i++) {
		$dtime += $deltatime;
		$date = &construct_time_date($time1 + $dtime);
		print "$base ... $i:\t$date\n";
		my $fn="$i";
		while(length($fn) < $field) {
			$fn = "0$fn";
		}
		#  simply try both variants
		my $fnc = $base.$fn.'.jpg';
		&change_date($fnc,$date) if(-f $fnc);	
		$fnc 	= $base.$fn.'.JPG';
		&change_date($fnc,$date) if(-f $fnc);	
	}
} else {
	$date = &construct_time_date( &parse_timestring($dat1,0) );
	#  simply try both variants
	my $fn 	= $img1.'.jpg';
	&change_date($fn,$date) if(-f $fn);	
	$fn 	= $img1.'.JPG';
	&change_date($fn,$date) if(-f $fn);	
}

sub construct_time_date {
#
#  return a meaningful time-date-string from time value (1 parameter)
#
	my $time = shift || return "-0-";
	#  0    1    2     3     4    5     6     7     8
        my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($time);
	##print "# $i :: ",join("-",($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst)),"\n";
	$sec = "00$sec";
	$sec =~ s/.*(..)$/$1/;
	$min = "00$min";
	$min =~ s/.*(..)$/$1/;
	$hour= "00$hour";
	$hour=~ s/.*(..)$/$1/;
	$mday= "00$mday";
	$mday=~ s/.*(..)$/$1/;
	$mon++;
	$mon = "00$mon";
	$mon =~ s/.*(..)$/$1/;
	$year += 1900;
	return "$year:$mon:$mday $hour:$min:$sec";
}

sub parse_timestring {
#
#  split date-input into meaningful bits and produce time value
#
#  parameters:
#	1.	date-string
#	2.	0 || 1 || 2 = only || begin || end of period
#
	my $dat = shift || return 0;
	my $period = shift;
	my ($ddat,$tdat) = split(/-/,$dat);
	my ($h,$i)=(12,0);
	if($period) {
		if($period == 1) {
			($h,$i)=(0,1);
		} else {
			($h,$i)=(23,59);
		}
	}
	($h,$i) = split(/:/,$tdat) if($tdat);
	my ($d,$m,$y) = split(/\./,$ddat);
	return timelocal(0,$i,$h,$d,$m-1,$y);
}

sub change_date {
#
#  change date settings to a certain date
#
#  parameters:
#	1.	$src  = filename of image
#	2.	$date = new date
#
	my $src = shift || return 0;
		print "... $src ..."; 
  	my $date = shift || return 0;
  	$src =~ s/\ /\\ /g;
  	my $no = 0;
	foreach my $t (@TAGS) {
 		unless($exifTool->SetNewValue($t, $date)) {
    			print "   setting for $t failed!\n";
	            	$no = 1;
        	}
  	}
  	unless ($no) {
     		my $f = "new/$src";
		if(-f $f) {
			print "  overwrite!!";
			unlink($f);
		}
     		if($exifTool->WriteInfo($src,$f)) {
		        print "   successfully rewritten!\n";
			my $s = `exiftool -DateTimeOriginal -d %Y%m%d%H%M.%S $f`;
			chomp $s;
			my $t = `exiftool -DateTimeOriginal -d %Y $f`;
			chomp $t;
			$t =~ s/^.*:\s//;
			if($t < 1970) {
				print STDERR "date prior to epoch ($f)\n";
				$s = "197001011200.00";
			} 
			$s =~ s/^.*:\s*//;
			`touch -t $s $f`;
			print "$f:\t$s\n\t",`ls -l $f`;
     		} else {
			print "   rewrite failed\n";
		}
  	}
}


More information about the KPhotoAlbum mailing list