Files
Leaf/leaf.php
2019-05-07 01:52:02 -07:00

1045 lines
31 KiB
PHP

#!/usr/bin/php
<?php
// Leaf - Tools for Book Scans
$version = "0.6.6";
$time_start = microtime(true);
date_default_timezone_set("America/Los_Angeles");
// Functions
function fin($print = null) {
if (isset($print)) {
echo "\n".$print;
}
echo "\nFinished in ".floor($time = microtime(true)-$_SERVER["REQUEST_TIME_FLOAT"])." seconds\n";
die;
}
function ask($text) {
echo $text."\n";
return trim(fgets(fopen("php://stdin","r")));
}
function msg($text, $action = null) {
if ($action == 1) {
echo bashcolor($text."\n","red"); die;
} elseif ($action == 2) {
echo bashcolor($text." (Y/n)\n","blue");
$line = trim(fgets(fopen("php://stdin","r")));
$line = $line ?: "y";
if($line != "y"){
echo bashcolor("Exiting!\n","red"); die;
}
} else {
echo $text."\n";
}
}
function args($query) {
global $argv;
foreach ($argv as $value) {
$parts = explode("=", $value);
$opt[trim($parts[0],"-")] = isset($parts[1]) ? $parts[1] : 1;
}
if ($query == "app" && isset($argv[1])) {
return $argv[1];
} elseif ($query == "dir" && count($argv) > 2) {
return chop($argv[count($argv)-1], "/")."/";
} elseif (isset($opt[$query])) {
return $opt[$query];
} else {
return null;
}
}
function Welcome($msg) {
global $version;
$out = "\033[1;1H\033[2J"; // clear screen
$out .= bashcolor("Leaf Version: ".$version, "white", "magenta")." ".shell_exec("date");
$out .= $msg."\n\n";
return $out;
}
function getClosest($search, $arr) {
$closest = null;
foreach ($arr as $item) {
if ($closest === null || abs($search - $closest) > abs($item - $search)) {
$closest = $item;
}
}
return $closest;
}
$colors = array_merge(array("blue","green","cyan","red","purple","brown","yellow","light blue","light green", "light cyan", "light red", "light purple"),array_fill(0, 500, "light gray"));
function bashcolor($str,$fgcolor="white",$bgcolor=null) {
static $fgcolors = array('black' => '0;30', 'dark gray' => '1;30', 'blue' => '0;34', 'light blue' => '1;34', 'green' => '0;32', 'light green' => '1;32', 'cyan' => '0;36', 'light cyan' => '1;36', 'red' => '0;31', 'light red' => '1;31', 'purple' => '0;35', 'light purple' => '1;35', 'brown' => '0;33', 'yellow' => '1;33', 'light gray' => '0;37', 'white' => '1;37', 'underline' => '4');
static $bgcolors = array('black' => '40', 'red' => '41', 'green' => '42', 'yellow' => '43', 'blue' => '44', 'magenta' => '45', 'cyan' => '46', 'light gray' => '47');
$out="";
if (!isset($fgcolors[$fgcolor])) { $fgcolor='white'; }
if (!isset($bgcolors[$bgcolor])) { $bgcolor=null; }
if ($fgcolor) { $out .= "\033[{$fgcolors[$fgcolor]}m"; }
if ($bgcolor) { $out .= "\033[{$bgcolors[$bgcolor]}m"; }
$out .= $str."\033[0m";
return $out;
}
// Checks
if (args("dir") && !is_dir(args("dir"))) {
msg("Problem with working dir: ".args("dir"), 1);
}
if (!is_dir("scratch")) {
mkdir("scratch");
}
////////////////////////////////////////////////////////////////////////////////////////////////
// Help
//////////////////////
if (!args("app") | args("app") == "help" | args("app") == "-help" | args("app") == "--help") {
/////////////////////////////////////////////////////////////////////////
$help = "Leaf $version
USAGE: leaf [mode] [-options] directory
Modes:
desort remove image sequence prefix
divide wrapper for imagemagick Divide_Src
-map=<file> specify brightness file
-adjust=<params> levels adjustment (ex. \"0%,98%,.9\")
-q=<quality> quality out of 100
dupes Find duplicate images using computed PHASH on thumbnails
-threshold=<num> match threshold
-walk=<num> comparison scope (compare n image to n, n+1, n+2, etc)
makepdf combine images into a pdf with img2pdf
profile apply xmp profile to images (requires exiv2 > 0.25)
-file=<file> xmp profile
resort reorder image sequence by adding a new image
-file=<file> file to insert
-x=<num> position of inserted file
review print a table of image dimension statistics
setdpi set image dpi with exiftool
-x=<dpi> specify dpi
-height=<inches> calculate dpi from specified height
sort sort files as AAABBB -> ABABAB
-m=<num> specify midpoint (cover image)
strip strip exif crop values from images with exiftool
";
echo $help;
fin();
////////////////////////////////////////////////////////////////////////////////////////////////
// Profile
//////////////////////
} elseif (args("app") == "profile") {
echo Welcome("Apply XMP profile to images");
/////////////////////////////////////////////////////////////////////////
$files = glob(args("dir")."*.*");
if (!file_exists(args("file"))) {
msg("Error reading xmp profile",1);
} else {
$profile = args("file");
}
echo $profile.": ".date("F d, Y", filemtime($profile))." (".count(file($profile))." lines)\n\n";
$lines = file_get_contents($profile);
$check[] = "crs:RawFileName";
$check[] = "crs:WhiteBalance";
$check[] = "crs:Sharpness";
$check[] = "crs:LensProfileName";
$check[] = "crs:IncrementalTemperature";
$check[] = "crs:IncrementalTint";
foreach ($check as $query) {
preg_match("/^.*".$query.".*\$/m",$lines,$matches);
if ($matches) {
echo trim($matches[0])."\n";
}
}
echo "\n";
msg("This operation will overwrite all existing metadata for target files. Continue?",2);
foreach ($files as $file) {
msg("Applying ".$profile." to ".$file);
@exec("cat ".$profile." | exiv2 -iXX- ".$file);
}
fin();
////////////////////////////////////////////////////////////////////////////////////////////////
// Sort
//////////////////////
} elseif (args("app") == "sort") {
echo Welcome("Sort files AAABBB -> ABABAB");
/////////////////////////////////////////////////////////////////////////
$files = glob(args("dir")."*.*");
if (args("m")) {
$check = glob(args("dir")."*".args("m")."*");
if (!$check) {
msg("Cannot find midpoint file to match ".args("m"),1);
}
$midpoint_key = args("m");
} else {
$mid = $files[ceil(count($files)/2)];
echo "Guessing midpoint key from ".$mid."\n";
$midpoint_key = filter_var($mid, FILTER_SANITIZE_NUMBER_INT);
}
////////////////////////////////////////////////////////////////////////////////////////////////
// Desort
//////////////////////
} elseif (args("app") == "desort") {
echo Welcome("Remove image sequence");
/////////////////////////////////////////////////////////////////////////
$files = glob(args("dir")."[0-9][0-9][0-9]-*");
foreach ($files as $file) {
$ops[] = array($file, args("dir").substr(basename($file),4));
}
foreach ($ops as $parts) {
if (file_exists($parts[1])) {
msg("Meltdown! Renamed file would overwrite ".$parts[1],1);
}
}
foreach ($ops as $parts) {
echo "Renaming ".$parts[0]." to ".$parts[1]."\n";
rename($parts[0], $parts[1]);
}
fin();
////////////////////////////////////////////////////////////////////////////////////////////////
// Dupes
//////////////////////
function dupes() {}
} elseif (args("app") == "dupes") {
echo Welcome("Find duplicate images");
/////////////////////////////////////////////////////////////////////////
if (args("threshold")) {
$threshold = args("threshold");
} else {
$threshold = 10;
}
if (args("walk") > 1) {
$walk = args("walk");
} elseif (args("walk")) {
msg("Walk must be 2 or greater",1);
} else {
$walk = 5;
}
$files = glob(args("dir")."*.*");
echo "Checking thumbnails: ";
foreach ($files as $file) {
$tnfile = "scratch/".basename($file,".".pathinfo($file,PATHINFO_EXTENSION))."_thumb.".pathinfo($file, PATHINFO_EXTENSION);
if (!file_exists($tnfile)) {
echo ".";
exec("vipsthumbnail ".$file." --size 300x300 -o ../".$tnfile." 2>&1");
} else {
echo "o";
}
$tnfiles[] = $tnfile;
}
echo "\n\n";
echo "Comparing phash values: ";
foreach ($tnfiles as $file) {
foreach ($tnfiles as $nfile) {
$anum = filter_var($file, FILTER_SANITIZE_NUMBER_INT);
$bnum = filter_var($nfile, FILTER_SANITIZE_NUMBER_INT);
$diff = abs($anum-$bnum);
$done[$file][$nfile] = 1;
if ($file != $nfile && $diff < $walk && isset($done[$nfile][$file])) {
echo ".";
$distance = shell_exec("compare -metric phash ".$file." ".$nfile." diffimage 2>&1")."\n";
if ($distance < $threshold) {
$match[] = array($file, $nfile, $distance);
}
}
}
}
echo "\n\n";
foreach ($match as $pair) {
echo $pair[0]." <> ".$pair[1]." = ".$pair[2];
}
echo "\n";
msg("Review duplicates?",2);
foreach ($match as $pair) {
$afile = args("dir").basename(str_replace("_thumb", "", $pair[1]));
$bfile = args("dir").basename(str_replace("_thumb", "", $pair[0]));
ask("Press return to compare ".$afile." to ".$bfile);
//@exec("montage -label '%f' -font Helvetica -pointsize 20 -background '#000000' -fill 'white' -define jpeg:size=600x600 -geometry 600x600+2+2 -auto-orient ".$afile." ".$bfile." /tmp/contact_sheet.jpg");
//@exec("open /tmp/contact_sheet.jpg -b com.apple.Preview");
@exec("open ".$afile." ".$bfile." -b com.apple.Preview");
}
fin();
////////////////////////////////////////////////////////////////////////////////////////////////
// Resort
//////////////////////
} elseif (args("app") == "resort") {
echo Welcome("Insert image into numbered sequence");
/////////////////////////////////////////////////////////////////////////
$files = glob(args("dir")."[0-9][0-9][0-9]-*.jpg");
$count = count($files);
if (!$count) {
msg("Could not find any numbered files in source directory",1);
}
if (!args("x")) {
msg("Please specify a position for inserted file",1);
} else {
$x = sprintf('%03d',args("x"));
}
if (!file_exists(args("file"))) {
msg("Error reading target file",1);
} else {
$target = args("file");
}
foreach ($files as $file) {
$seq[substr(basename($file),0,3)] = $file;
}
if (!isset($seq[$x])) {
msg("Specified position does not exist: ".$x,1);
}
foreach ($seq as $num => $file) {
if ($num == $x) {
$ops[] = array($target, args("dir").$x."-".basename($target));
}
if ($num >= $x) {
$ops[] = array($file, args("dir").sprintf('%03d',$num+1).substr(basename($file),3));
}
}
foreach ($ops as $parts) {
if (file_exists($parts[1])) {
msg("Meltdown! Renamed file would overwrite ".$parts[1],1);
}
}
foreach ($ops as $parts) {
echo "Renaming ".$parts[0]." to ".$parts[1]."\n";
rename($parts[0], $parts[1]);
}
fin("Starting file count: ".$count."; Ending file count: ".count(glob(args("dir")."[0-9][0-9][0-9]-*.jpg")));
////////////////////////////////////////////////////////////////////////////////////////////////
// Review
//////////////////////
} elseif (args("app") == "review") {
echo Welcome("List image dimension statistics");
/////////////////////////////////////////////////////////////////////////
$files = glob(args("dir")."*.*");
foreach ($files as $file) {
if (isset($attr)) {
$prev_attr = $attr;
$attr = null;
}
$ext = pathinfo($file, PATHINFO_EXTENSION);
if ($ext == "png") {
$tags = @shell_exec("exiftool -FileSize -ProfileDescription -BitsPerSample -PhotoshopQuality -ImageWidth -ImageHeight -PixelsPerUnitX -PixelsPerUnitY ".$file);
} else {
$tags = @shell_exec("exiftool -FileSize -ProfileDescription -BitsPerSample -PhotoshopQuality -ImageWidth -ImageHeight -Xresolution -Yresolution ".$file);
}
foreach (explode("\n", trim($tags)) as $tag) {
$parts = explode(":", $tag);
$attr[trim($parts[0])] = trim($parts[1]);
}
if ($ext == "png" && isset($attr['Pixels Per Unit X'])) {
$attr['X Resolution'] = round($attr['Pixels Per Unit X']/39.37007874016);
$attr['Y Resolution'] = round($attr['Pixels Per Unit Y']/39.37007874016);
}
$hilite["jpg"] = "blue";
$hilite["png"] = "green";
$hilite["JPG"] = "light blue";
$hilite["tif"] = "dark grey";
$hilite["DNG"] = "purple";
if (!isset($hilite[pathinfo($file, PATHINFO_EXTENSION)])) {
echo "Can't read ".$file."\n";
continue;
}
$print[] = bashcolor($file, $hilite[pathinfo($file, PATHINFO_EXTENSION)]);
$size = $attr['File Size'];
if ($ext != "DNG") { $quality = @exec("identify -format '%Q' ".$file); }
if (isset($quality)) {
$size .= " [Q=".$quality."]";
}
if (isset($attr['Photoshop Quality'])) {
$size .= " [PQ=".$attr['Photoshop Quality']."]";
}
$print[] = $size;
if (isset($attr['Profile Description'])) {
$print[] = bashcolor($attr['Profile Description']." (".$attr['Bits Per Sample']." bit)", "cyan");
} else {
$print[] = bashcolor("unknown","black");
}
$dims = $attr['Image Width']."x".$attr['Image Height'];
$color = "purple";
if (isset($prev_attr['Image Width']) && isset($prev_attr['Image Height'])) {
$prev_dims = $prev_attr['Image Width']."x".$prev_attr['Image Height'];
if ($dims != $prev_dims) {
$color = "light purple";
}
}
$print[] = bashcolor($attr['Image Width']."x".$attr['Image Height'], $color);
if (isset($attr['X Resolution'])) {
$print[] = bashcolor(floor($attr['X Resolution']).":".floor($attr['Y Resolution']), "brown");
$width = number_format($attr['Image Width']/$attr['X Resolution'],2);
$height = number_format($attr['Image Height']/$attr['Y Resolution'],2);
$print[] = bashcolor($width."\" x ".$height."\"", "white", "black");
} else {
$print[] = bashcolor("unknown","black");
$print[] = bashcolor("can't determine","black","white");
}
foreach ($print as $piece) {
if ($piece == $print[0]) {
echo str_pad($piece, 60);
} else {
echo str_pad($piece, 40);
}
}
echo "\n";
$print = null;
}
fin();
////////////////////////////////////////////////////////////////////////////////////////////////
// Makepdf
//////////////////////
} elseif (args("app") == "makepdf") {
echo Welcome("Combine finished images into a pdf with python img2pdf");
/////////////////////////////////////////////////////////////////////////
$input = args("dir")."*.*";
$dest = basename(getcwd()).".pdf";
if (file_exists($dest)) {
$dest = basename(getcwd())."_".rand(1000,9999).".pdf";
if (file_exists($dest)) { msg("Freak accident",1); }
}
echo "Creating pdf...\n\n";
exec("img2pdf --verbose --viewer-page-layout twocolumnright --output ".$dest." ".$input);
echo "\n";
msg("Press return to open in Acrobat",2);
exec("open ".$dest." -b com.adobe.Acrobat.Pro");
fin();
////////////////////////////////////////////////////////////////////////////////////////////////
// Divide
// Note: we assume a .JPG file is an unmodified DCIM image and convert to a greyscale tif for overlay.
// A .jpg file is treated like a prepared brightness map file and is unmodified.
//////////////////////
} elseif (args("app") == "divide") {
echo Welcome("Composite image from brightness map");
/////////////////////////////////////////////////////////////////////////
$files = glob(args("dir")."*.{jpg,JPG,tif}", GLOB_BRACE);
if (!args("map")) {
msg("No brightness map specified",1);
} elseif (!file_exists(args("map"))) {
msg("Can't open brightness map",1);
} elseif (substr(args("map"), -3, 3) == "JPG") {
//$method = "auto-level";
$method = "normalize";
$map = "scratch/".basename(args("map"),".JPG")."-divide_map.tif";
if (!file_exists($map)) {
@exec("convert -".$method." -colorspace gray ".args("map")." ".$map);
}
} elseif (substr(args("map"), -3, 3) == "jpg") {
$map = args("map");
}
$dest = rtrim(args("dir"), '/')."_divided";
if (count(glob($dest."/*.*"))) {
msg("Files already exist in destination ".$dest,1);
} elseif (!is_dir($dest)) {
mkdir($dest);
}
if (args("q")) {
$quality = args("q");
} else {
$quality = 95;
}
foreach ($files as $file) {
echo "Dividing ".$file." with ".$map.", Q=".$quality;
list ($width, $height) = getimagesize($map);
list ($twidth, $theight) = getimagesize($file);
if ($width != $twidth | $height != $theight) {
$tmap = $map."'[".$twidth."x".$theight."!]'";
echo " (resize map) ";
} else {
$tmap = $map;
}
if (args("adjust")) {
echo " (".args("adjust").")";
$cmd = "convert ".$file." ".$tmap." -compose Divide_Src -composite -level ".args("adjust")." -quality ".$quality." ".$dest."/".basename($file);
@exec($cmd);
} else {
$cmd = "convert ".$file." ".$tmap." -compose Divide_Src -composite -quality ".$quality." ".$dest."/".basename($file);
@exec($cmd);
}
echo "\n";
}
fin();
////////////////////////////////////////////////////////////////////////////////////////////////
// SetDPI
//////////////////////
} elseif (args("app") == "setdpi") {
echo Welcome("Batch set EXIF resolution tags");
/////////////////////////////////////////////////////////////////////////
$files = glob(args("dir")."*.*");
if (args("height")) {
$height = args("height");
} elseif (args("x")) {
$x = args("x");
} else {
msg("No resolution value",1);
}
foreach ($files as $file) {
echo "Processing ".$file.": ";
$ext = pathinfo($file, PATHINFO_EXTENSION);
if (isset($height)) {
list ($imwidth, $imheight) = getimagesize($file);
$x = floor($imheight/$height);
}
if ($ext == "jpg" | $ext == "JPG") {
exec("exiftool -overwrite_original -Xresolution=".$x." -Yresolution=".$x." -jfif:Xresolution=".$x." -jfif:Yresolution=".$x." ".$file);
echo "set DPI to ".$x;
} elseif ($ext == "DNG") {
exec("exiftool -overwrite_original -Xresolution=".$x." -Yresolution=".$x." -SubIFD:Xresolution=".$x." -SubIFD:Yresolution=".$x." -SubIFD1:Xresolution=".$x." -SubIFD1:Yresolution=".$x." ".$file);
echo "set DPI to ".$x;
} elseif ($ext == "png") {
$res = $x*39.37007874016;
exec("exiftool -overwrite_original -PixelsPerUnitX=".$res." -PixelsPerUnitY=".$res." ".$file);
echo "set DPI to ".$res;
} else {
echo " -> skip";
}
echo "\n";
}
fin();
////////////////////////////////////////////////////////////////////////////////////////////////
// Strip
//////////////////////
} elseif (args("app") == "strip") {
echo Welcome("Strip crop values from images");
/////////////////////////////////////////////////////////////////////////
$files = glob(args("dir")."*.*");
foreach ($files as $file) {
echo "Processing ".$file.": ";
$ext = pathinfo($file, PATHINFO_EXTENSION);
if ($ext == "jpg" | $ext == "JPG" | $ext == "DNG") {
$parts = chop(shell_exec("exiftool -s -s -s -XMP-crs:CropTop -XMP-crs:CropRight -XMP-crs:CropLeft -XMP-crs:CropBottom ".$file." 2>&1"));
if (strlen($parts) > 1) { list($top, $right, $left, $bottom) = explode("\n", $parts); }
if (isset($top) | isset($right) | isset($left) | isset($bottom)) {
shell_exec("exiftool -overwrite_original -XMP-crs:CropTop= -XMP-crs:CropRight= -XMP-crs:CropLeft= -XMP-crs:CropBottom= ".$file." 2>&1");
echo "removed";
} else {
echo "no crop found";
}
} else {
echo "cant handle ".$ext;
}
echo "\n";
}
fin();
////////////////////////////////////////////////////////////////////////////////////////////////
// Deskew
//////////////////////
} elseif (args("app") == "deskew") {
echo Welcome("Detect skew angle and apply to EXIF tags");
/////////////////////////////////////////////////////////////////////////
$deskew_max_angle = ".4";
$deskew_padding = 80;
$deskew_contrast = 20;
$deskew_size = 2200;
$files = glob(args("dir")."*.jpg");
if (!args("nomap")) {
msg("Creating dmaps...");
} else {
msg("Skipping dmap creation");
}
if (shell_exec("exiftool -s -s -s -CropAngle ".$files[0]." 2>&1")) {
msg("Crop angle detected on ".$files[0].", continue?",2);
}
foreach ($files as $file) {
if (args("nomap")) {
$dmfile = $file;
} else {
$dmfile = "scratch/".basename($file, ".jpg")."_dmap.jpg";
}
$result = shell_exec("deskew -o /tmp/null -l 99 \"".$dmfile."\" 2>&1");
$arr = explode("Skew angle found: ", $result);
$angle = substr($arr[1], 0, 4);
echo $file.": ".$angle;
if ($angle > $deskew_max_angle | $angle < ($deskew_max_angle*-1)) {
echo " (too big)";
} else {
$angles[$file] = $angle;
}
echo "\n";
}
// echo "\nWriting angles to EXIF: ";
//
// foreach ($files as $file) {
//
// if (isset($angles[$file])) {
// exec("exiftool -overwrite_original -XMP-crs:CropAngle=".$angles[$file]." ".$file);
// echo ".";
// } else {
// // erase top edge crop to indicate rotation was not applied
// exec("exiftool -overwrite_original -XMP-crs:CropTop=0 ".$file." 2>&1");
// }
// }
// }
////////////////////////////////////////////////////////////////////////////////////////////////
// Elif
//////////////////////
} else {
echo Welcome("Program name \"".$argv[1]."\" not found");
}
/////////////////////////////////////////////////////////////////////////
die;
// Settings
$tif_dir = "null";
$input_dir = "avision_jpg";
$xmp_profile = "avision_null.xmp";
$compare_threshold = "1";
$deskew_max_angle = ".4";
$deskew_padding = 80;
$deskew_contrast = 20;
$deskew_size = 2200;
$multicrops = 8;
// Header
echo "\033[1;1H\033[2J"; // clear screen
echo "Welcome to avision_jpg version ".$version."\n";
echo "Processing ".basename($_SERVER["PWD"])."\n";
echo "---------------------------------------------------------------------------\n\n";
// Dirs
if (!file_exists($input_dir)) {
if (!file_exists($tif_dir)) {
echo "No input dirs, exiting\n";
die;
} else {
mkdir ($input_dir);
}
}
if (!file_exists($xmp_profile)) {
echo "No xmp profile, exiting\n";
die;
}
if (!file_exists("scratch")) {
mkdir("scratch");
}
if (file_exists($tif_dir)) {
echo "Checking jpegs: ";
foreach (glob($tif_dir."/*") as $file) {
$jpegfile = str_replace("tif", "jpg", $file);
if (!file_exists($jpegfile)) {
exec("convert -quality 95 ".$file." ".$jpegfile);
echo ".";
} else {
echo "o";
}
}
echo "\n\n";
}
$files = glob($input_dir."/*.jpg");
// Renumber files
if (glob($input_dir."/001-*.jpg")) {
echo "It appears files have been numbered, skipping.\n";
} else {
echo "Checking thumbnails: ";
foreach ($files as $file) {
$tnfile = "scratch/".basename($file, ".jpg")."_thumb.jpg";
if (!file_exists($tnfile)) {
echo ".";
exec("vipsthumbnail ".$file." --size 160x160 -o ../".$tnfile." 2>&1");
} else {
echo "o";
}
}
echo "\n\nPlease specify the midpoint (cover image) or enter \"none\"\n";
$midpoint_key = trim(fgets(fopen("php://stdin","r")));
echo "\n";
if ($midpoint_key != "none") {
if (!glob($input_dir."/*".$midpoint_key."*.jpg")) {
echo "Can't find midpoint matching '".$midpoint_key."', exiting\n";
die;
}
}
$i = 0;
foreach ($files as $file) {
$tnfile = "scratch/".basename($file, ".jpg")."_thumb.jpg";
if ($i > 1) {
$prev = $files[array_search($file,$files)-1];
$prevtnfile = "scratch/".basename($prev, ".jpg")."_thumb.jpg";
$thingy = shell_exec("compare -metric ae -fuzz '10%' ".$tnfile." ".$prevtnfile." /tmp/null 2>&1");
echo $tnfile." <> ".$prevtnfile." = ".$thingy."\n";
if ($thingy < $compare_threshold) {
echo "Possible duplicates: ".basename($file)." <> ".basename($prev).". Press return to compare.";
fgets(fopen("php://stdin","r"));
exec("open ".$prev." ".$file." -b com.apple.Preview");
echo "0 (default) = Keep ".basename($file)."; 1 = Keep ".basename($prev)."; 2 = Keep both files\nResponse: ";
$line = trim(fgets(fopen("php://stdin","r")));
if(!$line){
echo "Keeping ".basename($file)."\n";
unset($files[array_search($prev,$files)]);
} elseif ($line == 1) {
echo "Keeping file ".basename($prev)."\n";
unset($files[array_search($file,$files)]);
} elseif ($line == 2) {
echo "Keeping both files\n";
} else {
echo "Invalid response, skipping\n";
}
echo "Resuming...\n";
}
}
$i++;
}
echo "\n";
$e = 2; $o = 1;
if ($midpoint_key == "none") {
foreach ($files as $file) {
$new = $input_dir."/".sprintf('%03d',$o)."-".basename($file);
$ops[$o] = array($file,$new);
$o++;
}
} else {
foreach ($files as $file) {
$num = substr($file, -7, 3);
if ($num < $midpoint_key) {
$new = $input_dir."/".sprintf('%03d',$e)."-".basename($file);
$ops[$e] = array($file,$new);
$e = $e+2;
} else {
$new = $input_dir."/".sprintf('%03d',$o)."-".basename($file);
$ops[$o] = array($file,$new);
$o = $o+2;
}
}
}
ksort($ops);
foreach ($ops as $op) {
echo $op[0]." will be renamed ".$op[1]."\n";
}
echo "\nType \"ok\" to skip comparison\n\n";
while (trim(fgets(fopen("php://stdin","r"))) != "ok") {
echo "Press return to compare another spread or type \"ok\" when finished\n\n";
$start = floor(rand(count($ops)-(count($ops)*.2),count($ops))/2)*2;
$left = $ops[$start][0];
$right = $ops[$start+1][0];
exec("open ".$left." ".$right." -b com.apple.Preview");
}
echo "\nMoving files!\n";
foreach ($ops as $key => $op) {
rename($op[0],$op[1]);
}
}
$files = glob($input_dir."/[0-9][0-9][0-9]-*.jpg");
// Apply profile and orientation
if (shell_exec("exiftool -s -s -s -Orientation ".$files[0]." 2>&1")) {
echo "\nOrientation already applied, skipping.\n\n";
} else {
echo "\nApplying XMP profile & orientation: ";
foreach ($files as $file) {
exec("cat ".$xmp_profile." | exiv2 -iXX- ".$file);
if (substr(basename($file), 0, 3) % 2 != 0) {
exec("exiftool -overwrite_original -n -Orientation=3 ".$file);
}
echo ".";
}
echo "\n\n";
}
// Define crop area
while (isset($proceed) != true) {
echo "Set crop areas: [x] to skip, [d] to define, [s] to scan.\n";
$input = trim(fgets(fopen("php://stdin","r")));
switch ($input) {
case "x":
$proceed = 1;
break;
case "s":
echo "Checking Crops: ";
foreach ($files as $file) {
$parts = chop(shell_exec("exiftool -s -s -s -XMP-crs:CropTop -XMP-crs:CropRight -XMP-crs:CropLeft -XMP-crs:CropBottom ".$file." 2>&1"));
@list($top, $right, $left, $bottom) = explode("\n", $parts);
if ($top && $right && $left && $bottom) {
$key = preg_replace("/[^0-9]/","",substr($file, -8, 4));
$crops[$key]['top'] = $top;
$crops[$key]['right'] = $right;
$crops[$key]['left'] = $left;
$crops[$key]['bottom'] = $bottom;
echo "*";
} else {
echo "-";
}
}
$proceed = 1;
break;
case "d":
foreach ($files as $file) {
$seq[preg_replace("/[^0-9]/","",substr($file, -8, 4))] = $file;
}
ksort($seq);
$seg = floor(count($seq)/$multicrops)-1;
$picks = array();
foreach (range(1, $multicrops, 1) as $val) {
$picks[] = array_values(array_slice($seq, $val*$seg, 1))[0];
}
foreach ($picks as $file) {
echo $file."\n";
}
trim(fgets(fopen("php://stdin","r")));
exec("open ".implode(" ",$picks)." -b com.adobe.Photoshop");
break;
}
echo "\n";
}
if (isset($crops) && count($crops) > count($files)/2) {
echo "\nIt looks like multicrop has already been applied, skipping.\n\n";
} elseif (isset($crops) && count($crops)) {
echo "Crop areas have been detected as follows. Press return to write to files.\n";
foreach ($crops as $key => $parts) {
echo "\n";
echo $key."\t\tTop: ".bashcolor($parts['top'], $colors[array_search($key,array_keys($crops))]);
echo "\tRight: ".bashcolor($parts['right'], $colors[array_search($key,array_keys($crops))]);
echo "\tLeft: ".bashcolor($parts['left'], $colors[array_search($key,array_keys($crops))]);
echo "\tBottom: ".bashcolor($parts['bottom'], $colors[array_search($key,array_keys($crops))]);
}
echo "\n";
trim(fgets(fopen("php://stdin","r")));
echo "Crop Mapping:\n";
foreach ($files as $file) {
$key = preg_replace("/[^0-9]/","",substr($file, -8, 4));
$closekey = getClosest($key, array_keys($crops));
$parts = $crops[$closekey];
echo "\n";
echo $file."\t\tTop: ".bashcolor($parts['top'], $colors[array_search($closekey,array_keys($crops))]);
echo "\tRight: ".bashcolor($parts['right'], $colors[array_search($closekey,array_keys($crops))]);
echo "\tLeft: ".bashcolor($parts['left'], $colors[array_search($closekey,array_keys($crops))]);
echo "\tBottom: ".bashcolor($parts['bottom'], $colors[array_search($closekey,array_keys($crops))]);
exec("exiftool -overwrite_original -XMP-crs:HasCrop=1 -XMP-crs:AlreadyApplied=0 -XMP-crs:CropTop=".$parts['top']." -XMP-crs:CropRight=".$parts['right']." -XMP-crs:CropLeft=".$parts['left']." -XMP-crs:CropBottom=".$parts['bottom']." ".$file);
}
echo "\n\n";
}
// Deskew
echo "Creating dmaps: ";
foreach ($files as $file) {
$dmfile = "scratch/".basename($file, ".jpg")."_dmap.jpg";
if (file_exists($dmfile)) {
echo "o";
} else {
//$jpeg_width = "7228";
//$jpeg_height = "9000";
$parts = chop(shell_exec("exiftool -s -s -s -XMP-crs:CropTop -XMP-crs:CropRight -XMP-crs:CropLeft -XMP-crs:CropBottom ".$file." 2>&1"));
list($top, $right, $left, $bottom) = explode("\n", $parts);
if (!$top | !$right | !$left | !$bottom) { echo $file." missing crop information!\n"; die; }
$jpeg_width = chop(shell_exec("exiftool -s -s -s -ImageWidth ".$files[0]." 2>&1"));
$jpeg_height = chop(shell_exec("exiftool -s -s -s -ImageHeight ".$files[0]." 2>&1"));
$top_pixels = floor( $jpeg_height * $top );
$bottom_pixels = $jpeg_height - floor( $bottom * $jpeg_height );
$left_pixels = floor( $jpeg_width * $left );
$width = $jpeg_width - $left_pixels;
$height = $jpeg_height - ( $top_pixels + $bottom_pixels );
$area_left = ( $width + $deskew_padding )."x".( $height + $deskew_padding*2 )."+0+".( $bottom_pixels - $deskew_padding );
$area_right = ( $width + $deskew_padding )."x".( $height + $deskew_padding*2 )."+".( $left_pixels - $deskew_padding )."+".( $top_pixels - $deskew_padding );
//echo "(Area left = ".$area_left.", ";
//echo "Area right = ".$area_right.")";
$size = $deskew_size."x".$deskew_size;
if (substr(basename($file), 0, 3) % 2 == 0) {
exec("convert ".$file." -auto-orient -crop ".$area_right." -level ".$deskew_contrast."%,".(100-$deskew_contrast)."% -colorspace Gray -resize ".$size." ".$dmfile);
} else {
exec("convert ".$file." -auto-orient -crop ".$area_left." -level ".$deskew_contrast."%,".(100-$deskew_contrast)."% -colorspace Gray -resize ".$size." ".$dmfile);
}
echo ".";
}
}
echo "\n\n";
if (shell_exec("exiftool -s -s -s -CropAngle ".$files[0]." 2>&1")) {
echo "CropAngle detected, skipping deskew.\n";
} else {
foreach ($files as $file) {
$dmfile = "scratch/".basename($file, ".jpg")."_dmap.jpg";
$result = shell_exec("deskew -o /tmp/null -l 99 \"".$dmfile."\" 2>&1");
$arr = explode("Skew angle found: ", $result);
$angle = substr($arr[1], 0, 4);
echo $file.": ".$angle;
if ($angle > $deskew_max_angle | $angle < ($deskew_max_angle*-1)) {
echo " (too big)";
} else {
$angles[$file] = $angle;
}
echo "\n";
}
echo "\nWriting angles to EXIF: ";
foreach ($files as $file) {
if (isset($angles[$file])) {
exec("exiftool -overwrite_original -XMP-crs:CropAngle=".$angles[$file]." ".$file);
echo ".";
} else {
// erase top edge crop to indicate rotation was not applied
exec("exiftool -overwrite_original -XMP-crs:CropTop=0 ".$file." 2>&1");
}
}
}
// Open files
echo "\nFinished! Press return to open all files in Photoshop\n";
fgets(fopen("php://stdin","r"));
exec("open ".implode(" ",$files)." -b com.adobe.Photoshop");
// check bridge labels
// for i in *; do exiftool -s -s -s -Label $i; done
// convert to sRGB lossy
// jpegicc -q100 001-Plustek0001.jpg 001-Plustek0001_out.jpg
?>