hikvision - extract thousands of images from camera sd card?

My challenge is that the camera is difficult to access physically (sitting high on a pole), so I want to this via the LAN cable.
I can access the camera via LAN, no problem, but I am still limited by only 100 pictues per download.

Any solutions to this?
Saxtoft

Not - quite - read on - so if you are doing this for a timelapse camera - the idea of this script is to save you the nightmare of downloading the images in limited numbers with ivms or browser.
If you want to get them off the camera - I'd advise ftp / or sftp to a remote webpage - or use webcam.io - trying to do it over the lan means you are stuck with the manufacturers hardware handling the card - hence a limitation.
You could always add a nas drive - buffalo or similar - even a hootoo adapter and save images via ftp each time ( aswell as sd card capture) - locally on the network
I have nothing to do with writing the script - but we have used it - and it's very handy, and the work is much appreciated - if you have any questions let me know.
This is the sort of headache we deal with - happy to give advice.

Dave
 
pls HELP
i dont have index00p.bin. I have only the event_db_index01 and event_db_index00.
i cant run this script, please help to convert to a normal JPG from the SD-Card.

Recently i have also a NVR, is there any possibilities to export to NVR HDD as a JPG?!
 
I've made a major update to the script that will work with both older and newer generation Hikvision cameras. I will post it some time next week, so stay tuned.

You can't export JPGs to the NVR HDD, the NVR manages the HDD and creates a specific file structure.
 
  • Like
Reactions: mollaomer50
Sorry for the delay.

This is the new updated script that works for both older and newer generation Hikvision cameras.
Hikvision-PIC-to-JPG/extract_capture.pl at main · RANSoftware/Hikvision-PIC-to-JPG

I've made some changes and now the input dir should be the folder that contains the "datadir" subfolders.




Unfortunately it doesn't work for me, I have the new version where the script should work :(

I followed your video, I get the following error:

Processing folder: C:\Users\User\Desktop\HIK/datadir0
DBD::SQLite::db prepare failed: no such table: event_id at C:\Users\User\Desktop\HIK\extract_capture.pl line 68, <STDIN> line 4.
DBD::SQLite::db prepare failed: no such table: event_id at C:\Users\User\Desktop\HIK\extract_capture.pl line 68, <STDIN> line 4.
 

Attachments

  • Knipsel.PNG
    Knipsel.PNG
    79.9 KB · Views: 6
I've made some changes to the script, but this is something specific for the structure of your file, so others should not use it.
Try with this code.
Perl:
#!/usr/bin/perl -

use Date::Format;
use File::Find::Rule;
use DBI;

my ($mainInputDir, $outputDir) = @ARGV;

if (not defined $mainInputDir) {
  die "\nERROR: Need input directory.\nThe input directory should be the directory that contains the datadir folders.\nUsage: extract_capture.pl inputdir outputdir\n";
}

if (not defined $outputDir) {
  die "\nERROR: Need output directory.\nUsage: extract_capture.pl inputdir outputdir\n";
}

unless ((-e $mainInputDir."/datadir0/event_db_index01") or (-e $mainInputDir."/datadir0/index00p.bin") or (-e $mainInputDir."/datadir0/index01p.bin")) {
        die "\nERROR: Can't find event_db_index01 or index00p.bin or index01p.bin in $mainInputDir\\datadir0\n";
    }

print "\n************* EXTRACTION DATE AND TIME LIMITS *************\n";
print "\nDate and time format example YYYYMMDDHHMMSS: 20181225221508\n";
print "\nEnter START date and time (or enter 1 to start from the\nfirst available image or leave empty if you don't want to use\na date and time limit): ";
my $inputStartDate = <STDIN>;
chomp $inputStartDate;

$nowEndDate = time2str("%Y%m%d%H%M%S", time());

if ($inputStartDate != "") {
    print "\nEnter END date and time (or enter 1 to use current date and\ntime (this will extract until the last image), or leave empty\nif you don't want to use a date and time limit): ";
    our $inputEndDate = <STDIN>;
    chomp $inputEndDate;
    if ($inputEndDate == 1) {
        $inputEndDate = $nowEndDate;
    }
}

print "\n************* OUTPUT JPG FILES NAMING OPTIONS (DEFAULT 1) *************\n";
print "\nEnter 1 for Image_YYYY_MM_DD-HH_MM_SS-DayName.jpg\nEx: Image_2018_12_25-22_15_08-Tue.jpg\n";
print "\nEnter 2 for Image_YYYYMMDDHHMMSS-DayNumber.jpg\nEx: Image_20181225221508-2.jpg\n";
my $outputDateFormat = <STDIN>;
chomp $outputDateFormat;

print "\n************* WHEN MORE THAN ONE PICTURE IS TAKEN PER SECOND (DEFAULT 1) *************\n";
print "\nEnter 1 to auto rename and keep all pictures\n";
print "\nEnter 2 to only save the first picture\n";
my $timeDuplicates = <STDIN>;
chomp $timeDuplicates;

my @datadirArray = File::Find::Rule->directory->in($mainInputDir);
my $datadirNumber = @datadirArray;   

for ($i=1; $i<$datadirNumber; $i++) {
    my $inputDir = $datadirArray[$i];
    print "Processing folder: $inputDir\n";

    if (-e $inputDir."/event_db_index01") {
        
        sleep(2);

        my $driver = "SQLite";
        my $database = $inputDir . "/event_db_index01";
        my $dsn = "DBI:$driver:dbname=$database";
        my $userid = "";
        my $password = "";
        my $dbh = DBI->connect($dsn, $userid, $password, { RaiseError => 1 }) or die $DBI::errstr;
        #my $stmt = qq(SELECT id, event_table_name from event_id;);
        #my $sth = $dbh->prepare($stmt);
        #my $rv = $sth->execute() or die $DBI::errstr;
        #if($rv < 0) {
          # print $DBI::errstr;
        #}
        #$ary1 = $sth->fetchall_arrayref({});

        #my $TBNumber = $#$ary1 + 1;
        #print "Number of tables in database: $TBNumber\n";
        #sleep(1);
        #for ($j=0; $j<$TBNumber; $j++) {
            #my $tableName = $ary1->[$j]{'event_table_name'};
            #print "Processing table: $tableName: ";
            my $stmt = "SELECT id, trigger_time, file_no, pic_offset_0, pic_len_0 from "."TimingCaptureTB";
            my $sth = $dbh->prepare($stmt) or die $DBI::errstr;
            my $rv = $sth->execute();
            
            if($rv < 0) {
               print $DBI::errstr;
            }
            $ary = $sth->fetchall_arrayref({});

            my $TBLength = $#$ary + 1;
            print "$TBLength\n";
            sleep(1);
            
            for ($k=0; $k<$TBLength; $k++) {
                $fileNumber = $ary->[$k]{'file_no'};
                $picFileName = "hiv" . sprintf("%05d", $fileNumber) . ".pic";
                open (PF, $inputDir . "/" . $picFileName) or die "ERROR: Can't find " .$picFileName. " in the input folder.\n";
                binmode(PF);
                $capDate = $ary->[$k]{'trigger_time'};
                $startOffset = $ary->[$k]{'pic_offset_0'};
                $endOffset = $startOffset + ($ary->[$k]{'pic_len_0'});
                $formatted_start_time = time2str("%C", $capDate, -0005);
                if ($outputDateFormat == 1) {
                    $fileDate = time2str("%Y_%m_%d-%H_%M_%S", $capDate, -0005);
                    $fileDayofWeek = time2str("%a", $capDate, -0005);
                }
                elsif ($outputDateFormat == 2) {
                    $fileDate = time2str("%Y%m%d%H%M%S", $capDate, -0005);
                    $fileDayofWeek = time2str("%w", $capDate, -0005);
                }
                else {
                    $fileDate = time2str("%Y_%m_%d-%H_%M_%S", $capDate, -0005);
                    $fileDayofWeek = time2str("%a", $capDate, -0005);
                }
                $limitFileDate = time2str("%Y%m%d%H%M%S", $capDate, -0005);
                                
                if ($inputStartDate != "" and $inputEndDate != "") {
                    if ($capDate > 0 and $limitFileDate >= $inputStartDate and $limitFileDate <= $inputEndDate) {
                        $jpegLength = ($endOffset - $startOffset);
                        $fileSize = $jpegLength / 1024;
                        $fileName = "Image_${fileDate}-${fileDayofWeek}.jpg";
                        if ($timeDuplicates != 2) {
                            my $picNumber = 1;
                            START:
                                if (-e $outputDir."/".$fileName) {
                                print "File Exists! Renaming...\n";
                                $fileName = "Image_${fileDate}-${fileDayofWeek}-". sprintf("%03d", $picNumber) .".jpg";
                                $picNumber++;
                                goto START;
                                }
                        }
                        unless (-e $outputDir."/".$fileName) {
                            if ($jpegLength > 0) {
                                seek (PF, $startOffset, 0);
                                read (PF, $singlejpeg, $jpegLength);
                                print "PicFile: $picFileName\n";
                                if ($singlejpeg =~ /[^\0]/) {
                                    print "POSITION (".($k+1)."): $formatted_start_time - OFFSET:($startOffset - $endOffset)\nFILE NAME: $fileName FILE SIZE: ". int($fileSize)." KB\n\n";
                                    open (OUTFILE, ">". $outputDir."/".$fileName);
                                    binmode(OUTFILE);
                                    print OUTFILE ${singlejpeg};
                                    close OUTFILE;
                                }
                            }
                        }
                    }
                }
                else {
                    if ($capDate > 0) {
                        $jpegLength = ($endOffset - $startOffset);
                        $fileSize = $jpegLength / 1024;
                        $fileName = "Image_${fileDate}-${fileDayofWeek}.jpg";
                        if ($timeDuplicates != 2) {
                            my $picNumber = 1;
                            START:
                                if (-e $outputDir."/".$fileName) {
                                print "File Exists! Renaming...\n";
                                $fileName = "Image_${fileDate}-${fileDayofWeek}-". sprintf("%03d", $picNumber) .".jpg";
                                $picNumber++;
                                goto START;
                                }
                        }
                        unless (-e $outputDir."/".$fileName) {
                            if ($jpegLength > 0) {
                                seek (PF, $startOffset, 0);
                                read (PF, $singlejpeg, $jpegLength);
                                print "PicFile: $picFileName\n";
                                if ($singlejpeg =~ /[^\0]/) {
                                    print "POSITION (".($k+1)."): $formatted_start_time - OFFSET:($startOffset - $endOffset)\nFILE NAME: $fileName FILE SIZE: ". int($fileSize)." KB\n\n";
                                    open (OUTFILE, ">". $outputDir."/".$fileName);
                                    binmode(OUTFILE);
                                    print OUTFILE ${singlejpeg};
                                    close OUTFILE;
                                }
                            }
                        }
                    }
                }
                close (PF);
            }
        #}
        $dbh->disconnect();
        print "Operation done for folder: $inputDir\n";
    }
    elsif ((-e $inputDir."/index00p.bin") or (-e $inputDir."/index01p.bin")) {   
                
        $maxRecords = 4096;
        $recordSize = 80;

        open (FH,$inputDir . "/index00p.bin") or (print "\n\nCan't find index00p.bin, trying to use index01p.bin...\n" and open (FH,$inputDir . "/index01p.bin")) or die "ERROR: Can't find index00p.bin or index01p.bin in the input folder.\n";
        read (FH,$buffer,1280);
        #read (FH,$buffer,-s "index00.bin");

        ($modifyTimes, $version, $picFiles, $nextFileRecNo, $lastFileRecNo, $curFileRec, $unknown, $checksum) = unpack("Q1I1I1I1I1C1176C76I1",$buffer);
        #print "$modifyTimes, $version, $picFiles, $nextFileRecNo, $lastFileRecNo, $curFileRec, $unknown, $checksum\n";

        $currentpos = tell (FH);
        $offset = $maxRecords * $recordSize;
        $fullSize = $offset * $picFiles;

        for ($l=0; $l<$fullSize; $l++) {
                seek (FH, $l, 0); #Use seek to make sure we are at the right location, 'read' was occasionally jumping a byte
                $Headcurrentpos = tell (FH);
                read (FH,$Headbuffer,80); #Read 80 bytes for the record
                #print "************$Headcurrentpos***************\n";
                        
                ($Headfield1, $Headfield2, $Headfield3, $Headfield4, $Headfield5, $Headfield6, $HeadcapDate, $Headfield8, $Headfield9, $Headfield10, $Headfield11, $Headfield12, $Headfield13, $Headfield14, $HeadstartOffset, $HeadendOffset) = unpack("I*",$Headbuffer);
                        
                #print "$Headercurrentpos: $Headfield1, $Headfield2, $Headfield3, $Headfield4, $Headfield5, $Headfield6, $HeadcapDate, $Headfield8, $Headfield9, $Headfield10, $Headfield11, $Headfield12, $Headfield13, $Headfield14, $HeadstartOffset, $HeadendOffset\n";
                if ($HeadcapDate > 0 and $Headfield8 == 0 and $Headfield9 > 0 and $Headfield10 == 0 and $Headfield11 == 0 and $Headfield12 == 0 and $Headfield13 == 0 and $Headfield14 == 0 and $HeadstartOffset > 0 and $HeadendOffset > 0)
                {
                    $fullSize = 1;
                    $headerSize = $Headcurrentpos - $recordSize;
                    print "\nHeaderSize: $headerSize\nStarting...\n\n";
                    sleep (3);
                }
        }
        
        for ($m=0; $m<$picFiles; $m++) {
            my $LastCapDate = 0;
            $newOffset = $headerSize + ($offset * $m);
            seek (FH, $newOffset, 0);
            $picFileName = "hiv" . sprintf("%05d", $m) . ".pic";
            print "PicFile: $picFileName at $newOffset\n";
            open (PF, $inputDir . "/" . $picFileName) or die "ERROR: Can't find " . $picFileName . " in the input folder.\n";
            binmode(PF);
                
            for ($n=0; $n<$maxRecords; $n++) {
                $recordOffset = $newOffset + ($n * $recordSize); #get the next record location
                seek (FH, $recordOffset, 0); #Use seek to make sure we are at the right location, 'read' was occasionally jumping a byte
                $currentpos = tell (FH);
                read (FH,$buffer,80); #Read 80 bytes for the record
                #print "************$currentpos***************\n";
                        
                ($field1, $field2, $field3, $field4, $field5, $field6, $capDate, $field8, $field9, $field10, $field11, $field12, $field13, $field14, $startOffset, $endOffset) = unpack("I*",$buffer);
                
                $formatted_start_time = time2str("%C", $capDate, -0005);
                if ($outputDateFormat == 1) {
                    $fileDate = time2str("%Y_%m_%d-%H_%M_%S", $capDate, -0005);
                    $fileDayofWeek = time2str("%a", $capDate, -0005);
                }
                elsif ($outputDateFormat == 2) {
                    $fileDate = time2str("%Y%m%d%H%M%S", $capDate, -0005);
                    $fileDayofWeek = time2str("%w", $capDate, -0005);
                }
                else {
                    $fileDate = time2str("%Y_%m_%d-%H_%M_%S", $capDate, -0005);
                    $fileDayofWeek = time2str("%a", $capDate, -0005);
                }
                $limitFileDate = time2str("%Y%m%d%H%M%S", $capDate, -0005);
                
                #print "$currentpos: $field1, $field2, $field3, $field4, $field5, $field6, $capDate, $field8, $field9, $field10, $field11, $field12, $field13, $field14, $startOffset, $endOffset\n";
                
                if ($inputStartDate != "" and $inputEndDate != "") {
                    if ($capDate > 0 and $capDate > $LastCapDate and $limitFileDate >= $inputStartDate and $limitFileDate <= $inputEndDate) {
                        $jpegLength = ($endOffset - $startOffset);
                        $fileSize = $jpegLength / 1024;
                        $fileName = "Image_${fileDate}-${fileDayofWeek}.jpg";
                        if ($timeDuplicates != 2) {
                            my $picNumber = 1;
                            START:
                                if (-e $outputDir."/".$fileName) {
                                print "File Exists! Renaming...\n";
                                $fileName = "Image_${fileDate}-${fileDayofWeek}-". sprintf("%03d", $picNumber) .".jpg";
                                $picNumber++;
                                goto START;
                                }
                        }
                        unless (-e $outputDir."/".$fileName) {
                            if ($jpegLength > 0) {
                                seek (PF, $startOffset, 0);
                                read (PF, $singlejpeg, $jpegLength) or last;
                                if ($singlejpeg =~ /[^\0]/) {
                                    print "POSITION ($currentpos): $formatted_start_time - OFFSET:($startOffset - $endOffset)\nFILE NAME: $fileName FILE SIZE: ". int($fileSize)." KB\n\n";
                                    open (OUTFILE, ">". $outputDir."/".$fileName);
                                    binmode(OUTFILE);
                                    print OUTFILE ${singlejpeg};
                                    close OUTFILE;
                                }
                            }
                        }
                        if ($LastCapDate < $capDate) {
                        $LastCapDate = $capDate;
                        }
                    }
                }
                else {
                    if ($capDate > 0 and $capDate > $LastCapDate) {
                        $jpegLength = ($endOffset - $startOffset);
                        $fileSize = $jpegLength / 1024;
                        $fileName = "Image_${fileDate}-${fileDayofWeek}.jpg";
                        if ($timeDuplicates != 2) {
                            my $picNumber = 1;
                            START:
                                if (-e $outputDir."/".$fileName) {
                                print "File Exists! Renaming...\n";
                                $fileName = "Image_${fileDate}-${fileDayofWeek}-". sprintf("%03d", $picNumber) .".jpg";
                                $picNumber++;
                                goto START;
                                }
                        }
                        unless (-e $outputDir."/".$fileName) {
                            if ($jpegLength > 0) {
                                seek (PF, $startOffset, 0);
                                read (PF, $singlejpeg, $jpegLength) or last;
                                if ($singlejpeg =~ /[^\0]/) {
                                    print "POSITION ($currentpos): $formatted_start_time - OFFSET:($startOffset - $endOffset)\nFILE NAME: $fileName FILE SIZE: ". int($fileSize)." KB\n\n";
                                    open (OUTFILE, ">". $outputDir."/".$fileName);
                                    binmode(OUTFILE);
                                    print OUTFILE ${singlejpeg};
                                    close OUTFILE;
                                }
                            }
                        }
                        if ($LastCapDate < $capDate) {
                        $LastCapDate = $capDate;
                        }
                    }
                }
            }
            close (PF);
        }
        close FH;
    }
    else {   
        die "\nERROR: Can't find event_db_index01 or index00p.bin or index01p.bin in $inputDir\n";
    }
}
print "\nAll operations completed.\n";
 
  • Like
Reactions: mollaomer50
I've made some changes to the script, but this is something specific for the structure of your file, so others should not use it.
Try with this code.
Perl:
#!/usr/bin/perl -

use Date::Format;
use File::Find::Rule;
use DBI;

my ($mainInputDir, $outputDir) = @ARGV;

if (not defined $mainInputDir) {
  die "\nERROR: Need input directory.\nThe input directory should be the directory that contains the datadir folders.\nUsage: extract_capture.pl inputdir outputdir\n";
}

if (not defined $outputDir) {
  die "\nERROR: Need output directory.\nUsage: extract_capture.pl inputdir outputdir\n";
}

unless ((-e $mainInputDir."/datadir0/event_db_index01") or (-e $mainInputDir."/datadir0/index00p.bin") or (-e $mainInputDir."/datadir0/index01p.bin")) {
        die "\nERROR: Can't find event_db_index01 or index00p.bin or index01p.bin in $mainInputDir\\datadir0\n";
    }

print "\n************* EXTRACTION DATE AND TIME LIMITS *************\n";
print "\nDate and time format example YYYYMMDDHHMMSS: 20181225221508\n";
print "\nEnter START date and time (or enter 1 to start from the\nfirst available image or leave empty if you don't want to use\na date and time limit): ";
my $inputStartDate = <STDIN>;
chomp $inputStartDate;

$nowEndDate = time2str("%Y%m%d%H%M%S", time());

if ($inputStartDate != "") {
    print "\nEnter END date and time (or enter 1 to use current date and\ntime (this will extract until the last image), or leave empty\nif you don't want to use a date and time limit): ";
    our $inputEndDate = <STDIN>;
    chomp $inputEndDate;
    if ($inputEndDate == 1) {
        $inputEndDate = $nowEndDate;
    }
}

print "\n************* OUTPUT JPG FILES NAMING OPTIONS (DEFAULT 1) *************\n";
print "\nEnter 1 for Image_YYYY_MM_DD-HH_MM_SS-DayName.jpg\nEx: Image_2018_12_25-22_15_08-Tue.jpg\n";
print "\nEnter 2 for Image_YYYYMMDDHHMMSS-DayNumber.jpg\nEx: Image_20181225221508-2.jpg\n";
my $outputDateFormat = <STDIN>;
chomp $outputDateFormat;

print "\n************* WHEN MORE THAN ONE PICTURE IS TAKEN PER SECOND (DEFAULT 1) *************\n";
print "\nEnter 1 to auto rename and keep all pictures\n";
print "\nEnter 2 to only save the first picture\n";
my $timeDuplicates = <STDIN>;
chomp $timeDuplicates;

my @datadirArray = File::Find::Rule->directory->in($mainInputDir);
my $datadirNumber = @datadirArray;  

for ($i=1; $i<$datadirNumber; $i++) {
    my $inputDir = $datadirArray[$i];
    print "Processing folder: $inputDir\n";

    if (-e $inputDir."/event_db_index01") {
       
        sleep(2);

        my $driver = "SQLite";
        my $database = $inputDir . "/event_db_index01";
        my $dsn = "DBI:$driver:dbname=$database";
        my $userid = "";
        my $password = "";
        my $dbh = DBI->connect($dsn, $userid, $password, { RaiseError => 1 }) or die $DBI::errstr;
        #my $stmt = qq(SELECT id, event_table_name from event_id;);
        #my $sth = $dbh->prepare($stmt);
        #my $rv = $sth->execute() or die $DBI::errstr;
        #if($rv < 0) {
          # print $DBI::errstr;
        #}
        #$ary1 = $sth->fetchall_arrayref({});

        #my $TBNumber = $#$ary1 + 1;
        #print "Number of tables in database: $TBNumber\n";
        #sleep(1);
        #for ($j=0; $j<$TBNumber; $j++) {
            #my $tableName = $ary1->[$j]{'event_table_name'};
            #print "Processing table: $tableName: ";
            my $stmt = "SELECT id, trigger_time, file_no, pic_offset_0, pic_len_0 from "."TimingCaptureTB";
            my $sth = $dbh->prepare($stmt) or die $DBI::errstr;
            my $rv = $sth->execute();
           
            if($rv < 0) {
               print $DBI::errstr;
            }
            $ary = $sth->fetchall_arrayref({});

            my $TBLength = $#$ary + 1;
            print "$TBLength\n";
            sleep(1);
           
            for ($k=0; $k<$TBLength; $k++) {
                $fileNumber = $ary->[$k]{'file_no'};
                $picFileName = "hiv" . sprintf("%05d", $fileNumber) . ".pic";
                open (PF, $inputDir . "/" . $picFileName) or die "ERROR: Can't find " .$picFileName. " in the input folder.\n";
                binmode(PF);
                $capDate = $ary->[$k]{'trigger_time'};
                $startOffset = $ary->[$k]{'pic_offset_0'};
                $endOffset = $startOffset + ($ary->[$k]{'pic_len_0'});
                $formatted_start_time = time2str("%C", $capDate, -0005);
                if ($outputDateFormat == 1) {
                    $fileDate = time2str("%Y_%m_%d-%H_%M_%S", $capDate, -0005);
                    $fileDayofWeek = time2str("%a", $capDate, -0005);
                }
                elsif ($outputDateFormat == 2) {
                    $fileDate = time2str("%Y%m%d%H%M%S", $capDate, -0005);
                    $fileDayofWeek = time2str("%w", $capDate, -0005);
                }
                else {
                    $fileDate = time2str("%Y_%m_%d-%H_%M_%S", $capDate, -0005);
                    $fileDayofWeek = time2str("%a", $capDate, -0005);
                }
                $limitFileDate = time2str("%Y%m%d%H%M%S", $capDate, -0005);
                               
                if ($inputStartDate != "" and $inputEndDate != "") {
                    if ($capDate > 0 and $limitFileDate >= $inputStartDate and $limitFileDate <= $inputEndDate) {
                        $jpegLength = ($endOffset - $startOffset);
                        $fileSize = $jpegLength / 1024;
                        $fileName = "Image_${fileDate}-${fileDayofWeek}.jpg";
                        if ($timeDuplicates != 2) {
                            my $picNumber = 1;
                            START:
                                if (-e $outputDir."/".$fileName) {
                                print "File Exists! Renaming...\n";
                                $fileName = "Image_${fileDate}-${fileDayofWeek}-". sprintf("%03d", $picNumber) .".jpg";
                                $picNumber++;
                                goto START;
                                }
                        }
                        unless (-e $outputDir."/".$fileName) {
                            if ($jpegLength > 0) {
                                seek (PF, $startOffset, 0);
                                read (PF, $singlejpeg, $jpegLength);
                                print "PicFile: $picFileName\n";
                                if ($singlejpeg =~ /[^\0]/) {
                                    print "POSITION (".($k+1)."): $formatted_start_time - OFFSET:($startOffset - $endOffset)\nFILE NAME: $fileName FILE SIZE: ". int($fileSize)." KB\n\n";
                                    open (OUTFILE, ">". $outputDir."/".$fileName);
                                    binmode(OUTFILE);
                                    print OUTFILE ${singlejpeg};
                                    close OUTFILE;
                                }
                            }
                        }
                    }
                }
                else {
                    if ($capDate > 0) {
                        $jpegLength = ($endOffset - $startOffset);
                        $fileSize = $jpegLength / 1024;
                        $fileName = "Image_${fileDate}-${fileDayofWeek}.jpg";
                        if ($timeDuplicates != 2) {
                            my $picNumber = 1;
                            START:
                                if (-e $outputDir."/".$fileName) {
                                print "File Exists! Renaming...\n";
                                $fileName = "Image_${fileDate}-${fileDayofWeek}-". sprintf("%03d", $picNumber) .".jpg";
                                $picNumber++;
                                goto START;
                                }
                        }
                        unless (-e $outputDir."/".$fileName) {
                            if ($jpegLength > 0) {
                                seek (PF, $startOffset, 0);
                                read (PF, $singlejpeg, $jpegLength);
                                print "PicFile: $picFileName\n";
                                if ($singlejpeg =~ /[^\0]/) {
                                    print "POSITION (".($k+1)."): $formatted_start_time - OFFSET:($startOffset - $endOffset)\nFILE NAME: $fileName FILE SIZE: ". int($fileSize)." KB\n\n";
                                    open (OUTFILE, ">". $outputDir."/".$fileName);
                                    binmode(OUTFILE);
                                    print OUTFILE ${singlejpeg};
                                    close OUTFILE;
                                }
                            }
                        }
                    }
                }
                close (PF);
            }
        #}
        $dbh->disconnect();
        print "Operation done for folder: $inputDir\n";
    }
    elsif ((-e $inputDir."/index00p.bin") or (-e $inputDir."/index01p.bin")) {  
               
        $maxRecords = 4096;
        $recordSize = 80;

        open (FH,$inputDir . "/index00p.bin") or (print "\n\nCan't find index00p.bin, trying to use index01p.bin...\n" and open (FH,$inputDir . "/index01p.bin")) or die "ERROR: Can't find index00p.bin or index01p.bin in the input folder.\n";
        read (FH,$buffer,1280);
        #read (FH,$buffer,-s "index00.bin");

        ($modifyTimes, $version, $picFiles, $nextFileRecNo, $lastFileRecNo, $curFileRec, $unknown, $checksum) = unpack("Q1I1I1I1I1C1176C76I1",$buffer);
        #print "$modifyTimes, $version, $picFiles, $nextFileRecNo, $lastFileRecNo, $curFileRec, $unknown, $checksum\n";

        $currentpos = tell (FH);
        $offset = $maxRecords * $recordSize;
        $fullSize = $offset * $picFiles;

        for ($l=0; $l<$fullSize; $l++) {
                seek (FH, $l, 0); #Use seek to make sure we are at the right location, 'read' was occasionally jumping a byte
                $Headcurrentpos = tell (FH);
                read (FH,$Headbuffer,80); #Read 80 bytes for the record
                #print "************$Headcurrentpos***************\n";
                       
                ($Headfield1, $Headfield2, $Headfield3, $Headfield4, $Headfield5, $Headfield6, $HeadcapDate, $Headfield8, $Headfield9, $Headfield10, $Headfield11, $Headfield12, $Headfield13, $Headfield14, $HeadstartOffset, $HeadendOffset) = unpack("I*",$Headbuffer);
                       
                #print "$Headercurrentpos: $Headfield1, $Headfield2, $Headfield3, $Headfield4, $Headfield5, $Headfield6, $HeadcapDate, $Headfield8, $Headfield9, $Headfield10, $Headfield11, $Headfield12, $Headfield13, $Headfield14, $HeadstartOffset, $HeadendOffset\n";
                if ($HeadcapDate > 0 and $Headfield8 == 0 and $Headfield9 > 0 and $Headfield10 == 0 and $Headfield11 == 0 and $Headfield12 == 0 and $Headfield13 == 0 and $Headfield14 == 0 and $HeadstartOffset > 0 and $HeadendOffset > 0)
                {
                    $fullSize = 1;
                    $headerSize = $Headcurrentpos - $recordSize;
                    print "\nHeaderSize: $headerSize\nStarting...\n\n";
                    sleep (3);
                }
        }
       
        for ($m=0; $m<$picFiles; $m++) {
            my $LastCapDate = 0;
            $newOffset = $headerSize + ($offset * $m);
            seek (FH, $newOffset, 0);
            $picFileName = "hiv" . sprintf("%05d", $m) . ".pic";
            print "PicFile: $picFileName at $newOffset\n";
            open (PF, $inputDir . "/" . $picFileName) or die "ERROR: Can't find " . $picFileName . " in the input folder.\n";
            binmode(PF);
               
            for ($n=0; $n<$maxRecords; $n++) {
                $recordOffset = $newOffset + ($n * $recordSize); #get the next record location
                seek (FH, $recordOffset, 0); #Use seek to make sure we are at the right location, 'read' was occasionally jumping a byte
                $currentpos = tell (FH);
                read (FH,$buffer,80); #Read 80 bytes for the record
                #print "************$currentpos***************\n";
                       
                ($field1, $field2, $field3, $field4, $field5, $field6, $capDate, $field8, $field9, $field10, $field11, $field12, $field13, $field14, $startOffset, $endOffset) = unpack("I*",$buffer);
               
                $formatted_start_time = time2str("%C", $capDate, -0005);
                if ($outputDateFormat == 1) {
                    $fileDate = time2str("%Y_%m_%d-%H_%M_%S", $capDate, -0005);
                    $fileDayofWeek = time2str("%a", $capDate, -0005);
                }
                elsif ($outputDateFormat == 2) {
                    $fileDate = time2str("%Y%m%d%H%M%S", $capDate, -0005);
                    $fileDayofWeek = time2str("%w", $capDate, -0005);
                }
                else {
                    $fileDate = time2str("%Y_%m_%d-%H_%M_%S", $capDate, -0005);
                    $fileDayofWeek = time2str("%a", $capDate, -0005);
                }
                $limitFileDate = time2str("%Y%m%d%H%M%S", $capDate, -0005);
               
                #print "$currentpos: $field1, $field2, $field3, $field4, $field5, $field6, $capDate, $field8, $field9, $field10, $field11, $field12, $field13, $field14, $startOffset, $endOffset\n";
               
                if ($inputStartDate != "" and $inputEndDate != "") {
                    if ($capDate > 0 and $capDate > $LastCapDate and $limitFileDate >= $inputStartDate and $limitFileDate <= $inputEndDate) {
                        $jpegLength = ($endOffset - $startOffset);
                        $fileSize = $jpegLength / 1024;
                        $fileName = "Image_${fileDate}-${fileDayofWeek}.jpg";
                        if ($timeDuplicates != 2) {
                            my $picNumber = 1;
                            START:
                                if (-e $outputDir."/".$fileName) {
                                print "File Exists! Renaming...\n";
                                $fileName = "Image_${fileDate}-${fileDayofWeek}-". sprintf("%03d", $picNumber) .".jpg";
                                $picNumber++;
                                goto START;
                                }
                        }
                        unless (-e $outputDir."/".$fileName) {
                            if ($jpegLength > 0) {
                                seek (PF, $startOffset, 0);
                                read (PF, $singlejpeg, $jpegLength) or last;
                                if ($singlejpeg =~ /[^\0]/) {
                                    print "POSITION ($currentpos): $formatted_start_time - OFFSET:($startOffset - $endOffset)\nFILE NAME: $fileName FILE SIZE: ". int($fileSize)." KB\n\n";
                                    open (OUTFILE, ">". $outputDir."/".$fileName);
                                    binmode(OUTFILE);
                                    print OUTFILE ${singlejpeg};
                                    close OUTFILE;
                                }
                            }
                        }
                        if ($LastCapDate < $capDate) {
                        $LastCapDate = $capDate;
                        }
                    }
                }
                else {
                    if ($capDate > 0 and $capDate > $LastCapDate) {
                        $jpegLength = ($endOffset - $startOffset);
                        $fileSize = $jpegLength / 1024;
                        $fileName = "Image_${fileDate}-${fileDayofWeek}.jpg";
                        if ($timeDuplicates != 2) {
                            my $picNumber = 1;
                            START:
                                if (-e $outputDir."/".$fileName) {
                                print "File Exists! Renaming...\n";
                                $fileName = "Image_${fileDate}-${fileDayofWeek}-". sprintf("%03d", $picNumber) .".jpg";
                                $picNumber++;
                                goto START;
                                }
                        }
                        unless (-e $outputDir."/".$fileName) {
                            if ($jpegLength > 0) {
                                seek (PF, $startOffset, 0);
                                read (PF, $singlejpeg, $jpegLength) or last;
                                if ($singlejpeg =~ /[^\0]/) {
                                    print "POSITION ($currentpos): $formatted_start_time - OFFSET:($startOffset - $endOffset)\nFILE NAME: $fileName FILE SIZE: ". int($fileSize)." KB\n\n";
                                    open (OUTFILE, ">". $outputDir."/".$fileName);
                                    binmode(OUTFILE);
                                    print OUTFILE ${singlejpeg};
                                    close OUTFILE;
                                }
                            }
                        }
                        if ($LastCapDate < $capDate) {
                        $LastCapDate = $capDate;
                        }
                    }
                }
            }
            close (PF);
        }
        close FH;
    }
    else {  
        die "\nERROR: Can't find event_db_index01 or index00p.bin or index01p.bin in $inputDir\n";
    }
}
print "\nAll operations completed.\n";

Ann it works! Thank you so much, it's exporting crazy now!
 
Your event_db_index is missing a table that has a list of all the detection types, probably Hikvision introduced that in newer versions of the firmware. I've set the script to read the TimingCaptureTB table directly. That is where the info about the pictures is stored if you have your camera set to take one picture every second (the timelapse function). Unfortunately if you would have intrusion detection or line crossing detection or other type of detection set, the code above would not work to extract the pictures stored by those detections. You would have to change the target table. I'll try to think of a solution in the next days.

Later edit:
I've updated the code on github. Now it should work without problems, so you can use that instead of the custom one.
 
Last edited:
Later edit:

I've updated the code on github. Now it should work without problems, so you can use that instead of the custom one.

Hi there, and happy 2023 to all.

Background: I have a cam that is set to capture images once every five minutes during daytime hours, however it will no longer access my WD NAS due to an upgrade which now blocks SMB V1 so I am unable to download the images using the normal camera web interface.

I have been able to copy the data folder containing the .pic files etc. for the few days I need and can successfully extract the images using your script, but I end up with nearly 40000 images at ~ 18GB.

Is there a part of your script I can edit so that it extracts just one image at original capture time five-minute intervals please? I have some limited experience with scripting but haven't used perl, and while I can see where the array variable is built (I think?) I am not sure how to do what I need.

If you are able to help that is awesome, but if not no worries; I am already in full appreciation of what you have done - thanks again.
 
Hi,

Do you have other detections (like line crossing or intrusion...) enabled on the camera or just the one that captures one image every five minutes?
Does your camera use event_db_index01 or the older index00p.bin?
 
Thanks for the quick reply Alex.

I only had the one time interval set, although their was a motion detection setting that emailed images to me - and that is still working.

I have both the index00p.bin file:
1673749372631.png

Appreciate your help.

Cheers, Jeff.
 
If I got this right your extracted files contain all the picture files that are saved every 5 minutes, but there are also some additional pictures saved in between the 5 minute interval and they are all mixed.
Can you please upload your index00p.bin file?
 
Yes - I was a bit confused by this too. When I run your script it extracts one photo for each second which is why I end up with ~40000 of them. Each of the PIC files are ~ 244MB and there are 85 of them.

I've zipped the index00p.bin and attached.

Thanks again - I don't want to underestimate how much I appreciate your help with this. I owe you a few beers if you ever find yourself in NZ!
 

Attachments

Try with the code below. You need to use the date and time limit and just enter 1 for both start and end date and time limits, so all the images will be processed.

date time limit.png

The change I've made is on line 405: and $capDate >= ($LastCapDate + 300)
300 is the value in seconds between the extracted pictures, so roughly 5 minutes between them. It can't be exactly 5 minutes because there are some time gaps between some of the images, for example the time jumps by almost 6 minutes between 2022_10_14-14_25_51-Fri and 2022_10_14-14_32_13-Fri

As I said to another user, this code is specific for the structure of your file, so others should not use it.

Perl:
#!/usr/bin/perl -

use Date::Format;
use File::Find::Rule;
use DBI;
#use Data::Dumper qw(Dumper);

my ($mainInputDir, $outputDir) = @ARGV;

if (not defined $mainInputDir) {
  die "\nERROR: Need input directory.\nThe input directory should be the directory that contains the datadir folders.\nUsage: extract_capture.pl inputdir outputdir\n";
}

if (not defined $outputDir) {
  die "\nERROR: Need output directory.\nUsage: extract_capture.pl inputdir outputdir\n";
}

unless ((-e $mainInputDir."/datadir0/event_db_index01") or (-e $mainInputDir."/datadir0/index00p.bin") or (-e $mainInputDir."/datadir0/index01p.bin")) {
        die "\nERROR: Can't find event_db_index01 or index00p.bin or index01p.bin in $mainInputDir\\datadir0\n";
    }

print "\n************* EXTRACTION DATE AND TIME LIMITS *************\n";
print "\nDate and time format example YYYYMMDDHHMMSS: 20181225221508\n";
print "\nEnter START date and time (or enter 1 to start from the\nfirst available image or leave empty if you don't want to use\na date and time limit): ";
my $inputStartDate = <STDIN>;
chomp $inputStartDate;

$nowEndDate = time2str("%Y%m%d%H%M%S", time());

if ($inputStartDate != "") {
    print "\nEnter END date and time (or enter 1 to use current date and\ntime (this will extract until the last image), or leave empty\nif you don't want to use a date and time limit): ";
    our $inputEndDate = <STDIN>;
    chomp $inputEndDate;
    if ($inputEndDate == 1) {
        $inputEndDate = $nowEndDate;
    }
}

print "\n************* OUTPUT JPG FILES NAMING OPTIONS (DEFAULT 1) *************\n";
print "\nEnter 1 for Image_YYYY_MM_DD-HH_MM_SS-DayName.jpg\nEx: Image_2018_12_25-22_15_08-Tue.jpg\n";
print "\nEnter 2 for Image_YYYYMMDDHHMMSS-DayNumber.jpg\nEx: Image_20181225221508-2.jpg\n";
my $outputDateFormat = <STDIN>;
chomp $outputDateFormat;

print "\n************* WHEN MORE THAN ONE PICTURE IS TAKEN PER SECOND (DEFAULT 1) *************\n";
print "\nEnter 1 to auto rename and keep all pictures\n";
print "\nEnter 2 to only save the first picture\n";
my $timeDuplicates = <STDIN>;
chomp $timeDuplicates;

my @datadirArray = File::Find::Rule->directory->in($mainInputDir);
my $datadirNumber = @datadirArray; 

for ($i=1; $i<$datadirNumber; $i++) {
    my $inputDir = $datadirArray[$i];
    print "Processing folder: $inputDir\n";

    if (-e $inputDir."/event_db_index01") {
        my $ary1;
        my $TBNumber;
        sleep(2);

        my $driver = "SQLite";
        my $database = $inputDir . "/event_db_index01";
        my $dsn = "DBI:$driver:dbname=$database";
        my $userid = "";
        my $password = "";
        my $dbh = DBI->connect($dsn, $userid, $password, { RaiseError => 1 }) or die $DBI::errstr;
      
        my $sth = $dbh->table_info('','main','%', 'TABLE');
        my $tables = [map{$_->[2]} @{$sth->fetchall_arrayref()}];
        #print join("\n", @$tables);
      
        if ( grep( /^event_id$/, @$tables ) ) {
            print "\n\n***** event_id found *****\n";
            my $stmt = qq(SELECT id, event_table_name from event_id;);
            my $sth = $dbh->prepare($stmt);
            my $rv = $sth->execute() or die $DBI::errstr;
                if($rv < 0) {
                    print $DBI::errstr;
                }
            $ary1 = $sth->fetchall_arrayref({});
            #print Dumper(\$ary1);     
        }
      
        else {
            $ary1 = [
                {
                  'id' => 10,
                  'event_table_name' => 'MotionDetectTB'
                },
                {
                  'id' => 12,
                  'event_table_name' => 'AlarmInputTB'
                },
                {
                  'id' => 13,
                  'event_table_name' => 'SceneChangeTB'
                },
                {
                  'id' => 24,
                  'event_table_name' => 'NormalPirTB'
                },
                {
                  'id' => 194,
                  'event_table_name' => 'faceSnapDB'
                },
                {
                  'id' => 204,
                  'event_table_name' => 'intrusionDB'
                },
                {
                  'id' => 205,
                  'event_table_name' => 'lineCrossDB'
                },
                {
                  'id' => 206,
                  'event_table_name' => 'regionEnterDB'
                },
                {
                  'id' => 207,
                  'event_table_name' => 'regionExitDB'
                },
                {
                  'id' => 208,
                  'event_table_name' => 'loiteringDB'
                },
                {
                  'id' => 209,
                  'event_table_name' => 'peopleGatherDB'
                },
                {
                  'id' => 210,
                  'event_table_name' => 'fastMoveDB'
                },
                {
                  'id' => 211,
                  'event_table_name' => 'ParkingDB'
                },
                {
                  'id' => 212,
                  'event_table_name' => 'objectLeaveDB'
                },
                {
                  'id' => 213,
                  'event_table_name' => 'objectRemoveDB'
                },
                {
                  'id' => 234,
                  'event_table_name' => 'MdWithTargetDB'
                },
                {
                  #---------- Extract first picture only ----------
                  'id' => 241,
                  'event_table_name' => 'FaceDetectTB'
                },
                {
                  'id' => 449,
                  'event_table_name' => 'IntrusionCaptureTable'
                },
                {
                  'id' => 450,
                  'event_table_name' => 'LineCrossCaptureTable'
                },
                {
                  'id' => 451,
                  'event_table_name' => 'RegionEnterCaptureTable'
                },
                {
                  'id' => 452,
                  'event_table_name' => 'RegionExitCaptureTable'
                },
                {
                  'id' => 453,
                  'event_table_name' => 'LoiteringCaptureTable'
                },
                {
                  'id' => 454,
                  'event_table_name' => 'PeopleGatherCaptureTable'
                },
                {
                  'id' => 455,
                  'event_table_name' => 'FastMoveCaptureTable'
                },
                {
                  'id' => 456,
                  'event_table_name' => 'ParkingCaptureTable'
                },
                {
                  'id' => 457,
                  'event_table_name' => 'ObjectLeaveCaptureTable'
                },
                {
                  'id' => 458,
                  'event_table_name' => 'ObjectRemoveCaptureTable'
                },
                {
                  'id' => 460,
                  'event_table_name' => 'TimingCaptureTB'
                },
                #------------------- Unknown id -------------------
                {
                  'id' => 1000,
                  'event_table_name' => 'ManualCaptureTB'
                },
                {
                  'id' => 1001,
                  'event_table_name' => 'MdWithTargetCaptureTable'
                },
                {
                  #---------- Extract first picture only ----------
                  'id' => 1002,
                  'event_table_name' => 'mixedTrafficDetectTB'
                }
            ];
        }
      
        $TBNumber = $#$ary1 + 1;
        print "\nNumber of tables in database: $TBNumber\n";
        sleep(1);
        my $tableName;
        for ($j=0; $j<$TBNumber; $j++) {
            $tableName = $ary1->[$j]{'event_table_name'};
            if ( grep( /^$tableName$/, @$tables ) ) {
                print "\n***** $tableName found *****\n";
                print "Processing table: $tableName: ";
                my $stmt = "SELECT id, trigger_time, file_no, pic_offset_0, pic_len_0 from ".$tableName;
                my $sth = $dbh->prepare($stmt) or die $DBI::errstr;
                my $rv = $sth->execute();
              
                if($rv < 0) {
                   print $DBI::errstr;
                }
                $ary = $sth->fetchall_arrayref({});

                my $TBLength = $#$ary + 1;
                print "$TBLength\n";
                sleep(1);
              
                for ($k=0; $k<$TBLength; $k++) {
                    $fileNumber = $ary->[$k]{'file_no'};
                    $picFileName = "hiv" . sprintf("%05d", $fileNumber) . ".pic";
                    open (PF, $inputDir . "/" . $picFileName) or die "ERROR: Can't find " .$picFileName. " in the input folder.\n";
                    binmode(PF);
                    $capDate = $ary->[$k]{'trigger_time'};
                    $startOffset = $ary->[$k]{'pic_offset_0'};
                    $endOffset = $startOffset + ($ary->[$k]{'pic_len_0'});
                    $formatted_start_time = time2str("%C", $capDate, -0005);
                    if ($outputDateFormat == 1) {
                        $fileDate = time2str("%Y_%m_%d-%H_%M_%S", $capDate, -0005);
                        $fileDayofWeek = time2str("%a", $capDate, -0005);
                    }
                    elsif ($outputDateFormat == 2) {
                        $fileDate = time2str("%Y%m%d%H%M%S", $capDate, -0005);
                        $fileDayofWeek = time2str("%w", $capDate, -0005);
                    }
                    else {
                        $fileDate = time2str("%Y_%m_%d-%H_%M_%S", $capDate, -0005);
                        $fileDayofWeek = time2str("%a", $capDate, -0005);
                    }
                    $limitFileDate = time2str("%Y%m%d%H%M%S", $capDate, -0005);
                                  
                    if ($inputStartDate != "" and $inputEndDate != "") {
                        if ($capDate > 0 and $limitFileDate >= $inputStartDate and $limitFileDate <= $inputEndDate) {
                            $jpegLength = ($endOffset - $startOffset);
                            $fileSize = $jpegLength / 1024;
                            $fileName = "Image_${fileDate}-${fileDayofWeek}.jpg";
                            if ($timeDuplicates != 2) {
                                my $picNumber = 1;
                                START:
                                    if (-e $outputDir."/".$fileName) {
                                    print "File Exists! Renaming...\n";
                                    $fileName = "Image_${fileDate}-${fileDayofWeek}-". sprintf("%03d", $picNumber) .".jpg";
                                    $picNumber++;
                                    goto START;
                                    }
                            }
                            unless (-e $outputDir."/".$fileName) {
                                if ($jpegLength > 0) {
                                    seek (PF, $startOffset, 0);
                                    read (PF, $singlejpeg, $jpegLength);
                                    print "PicFile: $picFileName\n";
                                    if ($singlejpeg =~ /[^\0]/) {
                                        print "POSITION (".($k+1)."): $formatted_start_time - OFFSET:($startOffset - $endOffset)\nFILE NAME: $fileName FILE SIZE: ". int($fileSize)." KB\n\n";
                                        open (OUTFILE, ">". $outputDir."/".$fileName);
                                        binmode(OUTFILE);
                                        print OUTFILE ${singlejpeg};
                                        close OUTFILE;
                                    }
                                }
                            }
                        }
                    }
                    else {
                        if ($capDate > 0) {
                            $jpegLength = ($endOffset - $startOffset);
                            $fileSize = $jpegLength / 1024;
                            $fileName = "Image_${fileDate}-${fileDayofWeek}.jpg";
                            if ($timeDuplicates != 2) {
                                my $picNumber = 1;
                                START:
                                    if (-e $outputDir."/".$fileName) {
                                    print "File Exists! Renaming...\n";
                                    $fileName = "Image_${fileDate}-${fileDayofWeek}-". sprintf("%03d", $picNumber) .".jpg";
                                    $picNumber++;
                                    goto START;
                                    }
                            }
                            unless (-e $outputDir."/".$fileName) {
                                if ($jpegLength > 0) {
                                    seek (PF, $startOffset, 0);
                                    read (PF, $singlejpeg, $jpegLength);
                                    print "PicFile: $picFileName\n";
                                    if ($singlejpeg =~ /[^\0]/) {
                                        print "POSITION (".($k+1)."): $formatted_start_time - OFFSET:($startOffset - $endOffset)\nFILE NAME: $fileName FILE SIZE: ". int($fileSize)." KB\n\n";
                                        open (OUTFILE, ">". $outputDir."/".$fileName);
                                        binmode(OUTFILE);
                                        print OUTFILE ${singlejpeg};
                                        close OUTFILE;
                                    }
                                }
                            }
                        }
                    }
                    close (PF);
                }
            }
            else {
                print "\nCan't find table: $tableName\n";
            }
        }
        $dbh->disconnect();
        print "Operation done for folder: $inputDir\n";
    }
    elsif ((-e $inputDir."/index00p.bin") or (-e $inputDir."/index01p.bin")) { 
              
        $maxRecords = 4096;
        $recordSize = 80;

        open (FH,$inputDir . "/index00p.bin") or (print "\n\nCan't find index00p.bin, trying to use index01p.bin...\n" and open (FH,$inputDir . "/index01p.bin")) or die "ERROR: Can't find index00p.bin or index01p.bin in the input folder.\n";
        read (FH,$buffer,1280);
        #read (FH,$buffer,-s "index00.bin");

        ($modifyTimes, $version, $picFiles, $nextFileRecNo, $lastFileRecNo, $curFileRec, $unknown, $checksum) = unpack("Q1I1I1I1I1C1176C76I1",$buffer);
        #print "$modifyTimes, $version, $picFiles, $nextFileRecNo, $lastFileRecNo, $curFileRec, $unknown, $checksum\n";

        $currentpos = tell (FH);
        $offset = $maxRecords * $recordSize;
        $fullSize = $offset * $picFiles;

        for ($l=0; $l<$fullSize; $l++) {
                seek (FH, $l, 0); #Use seek to make sure we are at the right location, 'read' was occasionally jumping a byte
                $Headcurrentpos = tell (FH);
                read (FH,$Headbuffer,80); #Read 80 bytes for the record
                #print "************$Headcurrentpos***************\n";
                      
                ($Headfield1, $Headfield2, $Headfield3, $Headfield4, $Headfield5, $Headfield6, $HeadcapDate, $Headfield8, $Headfield9, $Headfield10, $Headfield11, $Headfield12, $Headfield13, $Headfield14, $HeadstartOffset, $HeadendOffset) = unpack("I*",$Headbuffer);
                      
                #print "\n$Headcurrentpos: $Headfield1, $Headfield2, $Headfield3, $Headfield4, $Headfield5, $Headfield6, $HeadcapDate, $Headfield8, $Headfield9, $Headfield10, $Headfield11, $Headfield12, $Headfield13, $Headfield14, $HeadstartOffset, $HeadendOffset\n";
                if ($HeadcapDate > 0 and $Headfield8 == 0 and $Headfield9 > 0 and $Headfield10 == 0 and $Headfield11 == 0 and $Headfield12 == 0 and $Headfield13 == 0 and $Headfield14 == 0 and $HeadstartOffset > 0 and $HeadendOffset > 0)
                {
                    $fullSize = 1;
                    $headerSize = $Headcurrentpos - $recordSize;
                    print "\nHeaderSize: $headerSize\nStarting...\n\n";
                    sleep (3);
                }
        }
        my $LastCapDate = 0;
        for ($m=0; $m<$picFiles; $m++) {
          
            $newOffset = $headerSize + ($offset * $m);
            seek (FH, $newOffset, 0);
            $picFileName = "hiv" . sprintf("%05d", $m) . ".pic";
            print "PicFile: $picFileName at $newOffset\n";
            open (PF, $inputDir . "/" . $picFileName) or die "ERROR: Can't find " . $picFileName . " in the input folder.\n";
            binmode(PF);
              
            for ($n=0; $n<$maxRecords; $n++) {
                $recordOffset = $newOffset + ($n * $recordSize); #get the next record location
                seek (FH, $recordOffset, 0); #Use seek to make sure we are at the right location, 'read' was occasionally jumping a byte
                $currentpos = tell (FH);
                read (FH,$buffer,80); #Read 80 bytes for the record
                #print "************$currentpos***************\n";
                      
                ($field1, $field2, $field3, $field4, $field5, $field6, $capDate, $field8, $field9, $field10, $field11, $field12, $field13, $field14, $startOffset, $endOffset) = unpack("I*",$buffer);
                #print "\n$field1, $field2, $field3, $field4, $field5, $field6, $capDate, $field8, $field9, $field10, $field11, $field12, $field13, $field14, $startOffset, $endOffset\n";
                $formatted_start_time = time2str("%C", $capDate, -0005);
                if ($outputDateFormat == 1) {
                    $fileDate = time2str("%Y_%m_%d-%H_%M_%S", $capDate, -0005);
                    $fileDayofWeek = time2str("%a", $capDate, -0005);
                }
                elsif ($outputDateFormat == 2) {
                    $fileDate = time2str("%Y%m%d%H%M%S", $capDate, -0005);
                    $fileDayofWeek = time2str("%w", $capDate, -0005);
                }
                else {
                    $fileDate = time2str("%Y_%m_%d-%H_%M_%S", $capDate, -0005);
                    $fileDayofWeek = time2str("%a", $capDate, -0005);
                }
                $limitFileDate = time2str("%Y%m%d%H%M%S", $capDate, -0005);
              
                #print "$currentpos: $field1, $field2, $field3, $field4, $field5, $field6, $capDate, $field8, $field9, $field10, $field11, $field12, $field13, $field14, $startOffset, $endOffset\n";
              
                if ($inputStartDate != "" and $inputEndDate != "") {
                    if ($capDate > 0 and $capDate > $LastCapDate and $limitFileDate >= $inputStartDate and $limitFileDate <= $inputEndDate and $capDate >= ($LastCapDate + 300)) {
                        $jpegLength = ($endOffset - $startOffset);
                        $fileSize = $jpegLength / 1024;
                        $fileName = "Image_${fileDate}-${fileDayofWeek}.jpg";
                        if ($timeDuplicates != 2) {
                            my $picNumber = 1;
                            START:
                                if (-e $outputDir."/".$fileName) {
                                print "File Exists! Renaming...\n";
                                $fileName = "Image_${fileDate}-${fileDayofWeek}-". sprintf("%03d", $picNumber) .".jpg";
                                $picNumber++;
                                goto START;
                                }
                        }
                        unless (-e $outputDir."/".$fileName) {
                            if ($jpegLength > 0) {
                                seek (PF, $startOffset, 0);
                                read (PF, $singlejpeg, $jpegLength) or last;
                                if ($singlejpeg =~ /[^\0]/) {
                                    print "POSITION ($currentpos): $formatted_start_time - OFFSET:($startOffset - $endOffset)\nFILE NAME: $fileName FILE SIZE: ". int($fileSize)." KB\n\n";
                                    open (OUTFILE, ">". $outputDir."/".$fileName);
                                    binmode(OUTFILE);
                                    print OUTFILE ${singlejpeg};
                                    close OUTFILE;
                                }
                            }
                        }
                        if ($LastCapDate < $capDate) {
                        $LastCapDate = $capDate;
                        }
                    }
                }
                else {
                    if ($capDate > 0 and $capDate > $LastCapDate) {
                        $jpegLength = ($endOffset - $startOffset);
                        $fileSize = $jpegLength / 1024;
                        $fileName = "Image_${fileDate}-${fileDayofWeek}.jpg";
                        if ($timeDuplicates != 2) {
                            my $picNumber = 1;
                            START:
                                if (-e $outputDir."/".$fileName) {
                                print "File Exists! Renaming...\n";
                                $fileName = "Image_${fileDate}-${fileDayofWeek}-". sprintf("%03d", $picNumber) .".jpg";
                                $picNumber++;
                                goto START;
                                }
                        }
                        unless (-e $outputDir."/".$fileName) {
                            if ($jpegLength > 0) {
                                seek (PF, $startOffset, 0);
                                read (PF, $singlejpeg, $jpegLength) or last;
                                if ($singlejpeg =~ /[^\0]/) {
                                    print "POSITION ($currentpos): $formatted_start_time - OFFSET:($startOffset - $endOffset)\nFILE NAME: $fileName FILE SIZE: ". int($fileSize)." KB\n\n";
                                    open (OUTFILE, ">". $outputDir."/".$fileName);
                                    binmode(OUTFILE);
                                    print OUTFILE ${singlejpeg};
                                    close OUTFILE;
                                }
                            }
                        }
                        if ($LastCapDate < $capDate) {
                        $LastCapDate = $capDate;
                        }
                    }
                }
            }
            close (PF);
        }
        close FH;
    }
    else { 
        die "\nERROR: Can't find event_db_index01 or index00p.bin or index01p.bin in $inputDir\n";
    }
}
print "\nAll operations completed.\n";
 
Last edited:
  • Like
Reactions: AnotherKiwi
That worked fantastically - thank you again. This is an example of what the internet was envisaged and developed to do: helping out others - even from opposite sides of the earth - if you are able.

Don't forget those beers on offer when you're down-under any time!

Cheers, Jeff.
 
Hello,
Thanks much for your effort in creating a process for mass extraction of JPG's. I am setting up a timelaps (1 image per 5 minutes for approx 1 year) so this might be a great help!!

I've been trying the software on a test-set of data. Successfully loaded the data from the memory card to my computer but it appeared that the file/folder structure from the camera was different than had been shown in the explanatory video (no datadir0 folder). In an attempt to resolve this I moved all the datafiles from the root folder into a newly created datadir0 folder and restarted the script. It then takes 10 minutes before the first reply appears on the screen and from there regular replies follow:
Processing folder: D:\Hikv/datadir0
PicFile: hiv00000.pic at 0​
PicFile: hiv00001.pic at 327680​
--etc etc etc--​
PicFile: hiv00104.pic at 34078720​
PicFile: hiv00105.pic at 34406400​
All operations completed.​

But although all processing seems to have been successful, no JPGs are found in the target directory.
So in summary:
  • Directory structure of the Hikvision Camera is different from the two examples in the video
  • Scripts’ message about processing folder contains a mix of fwd and backwd slashes (“D:\Hikv/datadir0”)
  • Processing messages from the script are not similar to those in the video and do not mention the output file name
  • After starting the script and displaying “processing folder xxx” it takes 10 minutes before the first line with “Picfile: hiv000xxxx…” is being displayed.
  • After successful completion of the script there are no output files