This commit is contained in:
2019-05-07 01:53:20 -07:00
parent 0fae2a9042
commit e35c2515f2

369
leaf.php
View File

@@ -2,9 +2,10 @@
<?php <?php
// Leaf - Tools for Book Scans // Leaf - Tools for Book Scans
$version = "0.6.8"; $version = "0.6.9";
$time_start = microtime(true); $time_start = microtime(true);
date_default_timezone_set("America/Los_Angeles"); date_default_timezone_set("America/Los_Angeles");
$xt = 3; // threads
// Functions // Functions
@@ -19,7 +20,10 @@ function multiexec($thread, $x) {
} }
$echo = implode("\n", $msg); $echo = implode("\n", $msg);
$exec = implode(" > /dev/null & ",$cmd)." & wait"; $exec = implode(" > /dev/null & ",$cmd)." & wait";
echo $echo."\n"; echo $echo;
if (strlen($echo) > 1) {
echo "\n";
}
exec($exec); exec($exec);
} }
} }
@@ -141,9 +145,15 @@ $help = "Leaf $version
USAGE: leaf [mode] [-options] directory USAGE: leaf [mode] [-options] directory
Modes: Modes:
clean remove scratch files
crop define EXIF crop values using template files crop define EXIF crop values using template files
-crops=<num> specify how many files to use -crops=<num> specify how many files to use
desort remove image sequence prefix desort remove image sequence prefix
deskew detect rotation angles
-max=<num> angles greater than this value are ignored, default .4
-pad=<num> pixels to pad around crop area, default 80
-contrast=<num> contrast boost, default 20
-size=<num> size in pixels of dmap, default 2200
divide wrapper for imagemagick Divide_Src divide wrapper for imagemagick Divide_Src
-map=<file> specify brightness file -map=<file> specify brightness file
-adjust=<params> levels adjustment (ex. \"0%,98%,.9\") -adjust=<params> levels adjustment (ex. \"0%,98%,.9\")
@@ -158,6 +168,9 @@ resort reorder image sequence by adding a new image
-file=<file> file to insert -file=<file> file to insert
-x=<num> position of inserted file -x=<num> position of inserted file
review print a table of image dimension statistics review print a table of image dimension statistics
rotate batch transform rotate (lossy)
-x=<angle> rotation angle, default=90
-q=<0-100> jpeg quality, default read from source or 95
setdpi set image dpi with exiftool setdpi set image dpi with exiftool
-x=<dpi> specify dpi -x=<dpi> specify dpi
-height=<inches> calculate dpi from specified height -height=<inches> calculate dpi from specified height
@@ -169,6 +182,68 @@ strip strip exif crop values from images with exiftool
echo $help; echo $help;
fin(); fin();
////////////////////////////////////////////////////////////////////////////////////////////////
// Note: Clean
//////////////////////
} elseif (args("app") == "clean") {
echo Welcome("Remove scratch dir");
/////////////////////////////////////////////////////////////////////////
msg("Remove scratch files?",2);
if (!is_dir("scratch")) {
msg("Problem with scratch dir",2);
} else {
foreach (glob("scratch/*.*") as $file) {
unlink($file);
}
}
fin();
////////////////////////////////////////////////////////////////////////////////////////////////
// Note: Rotate
//////////////////////
} elseif (args("app") == "rotate") {
echo Welcome("Batch rotate (lossy)");
/////////////////////////////////////////////////////////////////////////
$default_quality = 95;
$files = glob(args("dir")."*.{jpg,JPG,tif,TIF}", GLOB_BRACE);
if (args("x")) {
$angle = args("x");
} else {
$angle = 90;
}
$dest = rtrim(args("dir"), '/')."_rotated";
if (count(glob($dest."/*.*"))) {
msg("Files already exist in destination ".$dest,1);
} elseif (!is_dir($dest)) {
mkdir($dest);
}
foreach ($files as $file) {
$ext = pathinfo($file,PATHINFO_EXTENSION);
$output = $dest."/".basename($file,$ext)."jpg";
if (args("q")) {
$quality = args("q");
} elseif (strtolower($ext) == "jpg") {
$quality = @exec("identify -format '%Q' ".$file);
} else {
$quality = $default_quality;
}
$msg = "Rotating ".$file." ".$angle." to ".$output." with Q=".$quality;
$cmd = "convert -rotate ".$angle." -quality ".$quality." ".$file." ".$output;
$thread[] = array($msg, $cmd);
}
msg("Beginning multithreaded convert with ".$xt." threads");
multiexec($thread,$xt);
fin();
//////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////
// Note: Profile // Note: Profile
////////////////////// //////////////////////
@@ -666,7 +741,6 @@ fin();
echo Welcome("Composite image from brightness map"); echo Welcome("Composite image from brightness map");
///////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////
$xt = 8;
$files = glob(args("dir")."*.{jpg,JPG,tif}", GLOB_BRACE); $files = glob(args("dir")."*.{jpg,JPG,tif}", GLOB_BRACE);
if (!args("map")) { if (!args("map")) {
@@ -800,16 +874,96 @@ fin();
echo Welcome("Detect skew angle and apply to EXIF tags"); echo Welcome("Detect skew angle and apply to EXIF tags");
///////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////
$deskew_max_angle = ".4"; if (args("max")) {
$deskew_padding = 80; $deskew_max_angle = args("max");
$deskew_contrast = 20; } else {
$deskew_size = 2200; $deskew_max_angle = .4;
}
$files = glob(args("dir")."*.jpg"); if (null !== args("pad")) {
$deskew_padding = args("pad");
echo "pad = ".args("pad");
} else {
$deskew_padding = 80;
}
if (null !== args("contrast")) {
$deskew_contrast = args("contrast");
} else {
$deskew_contrast = 20;
}
if (args("size")) {
$deskew_size = args("size");
} else {
$deskew_size = 2200;
}
die;
$files = glob(args("dir")."*.{jpg,JPG,dng,DNG}", GLOB_BRACE);
echo "Scanning for crop values: ";
if (!args("nomap")) { if (!args("nomap")) {
msg("Creating dmaps..."); foreach ($files as $file) {
$ext = pathinfo($file,PATHINFO_EXTENSION);
$dmfile = "scratch/".basename($file, ".".$ext)."_dmap.jpg";
if (!file_exists($dmfile)) {
$parts = chop(shell_exec("exiftool -s -s -s -XMP-crs:CropTop -XMP-crs:CropRight -XMP-crs:CropLeft -XMP-crs:CropBottom -ImageWidth -ImageHeight ".$file." 2>&1"));
@list($top, $right, $left, $bottom, $check_width, $check_height) = explode("\n", $parts);
if (!$top | !$right | !$left | !$bottom) { msg($file." missing crop information!",1); }
$jpeg_width = $check_height;
$jpeg_height = $check_width;
$top_pixels = floor( $jpeg_width * $top );
$right_pixels = $jpeg_height - floor( $right * $jpeg_height );
$left_pixels = floor( $jpeg_height * $left );
$bottom_pixels = $jpeg_width - floor( $bottom * $jpeg_width );
$width = $jpeg_width - ( $top_pixels + $bottom_pixels );
$height = $jpeg_height - ( $left_pixels + $right_pixels );
$area_left = ( $width + $deskew_padding*2 )."x".( $height + $deskew_padding*2 )."+".( $bottom_pixels - $deskew_padding )."+".( $left_pixels - $deskew_padding );
$area_right = ( $width + $deskew_padding*2 )."x".( $height + $deskew_padding*2 )."+".( $top_pixels - $deskew_padding )."+".( $right_pixels - $deskew_padding );
//echo "\n\n(Area left = ".$area_left.", Area right = ".$area_right.")";
$size = $deskew_size."x".$deskew_size;
if (substr(basename($file), 0, 3) % 2 == 0) {
$cmd = "convert ".$file." -auto-orient -crop ".$area_right." -level ".$deskew_contrast."%,".(100-$deskew_contrast)."% -colorspace Gray -resize ".$size." ".$dmfile;
} else {
$cmd = "convert ".$file." -auto-orient -crop ".$area_left." -level ".$deskew_contrast."%,".(100-$deskew_contrast)."% -colorspace Gray -resize ".$size." ".$dmfile;
}
$msg = "Creating dmap for ".$file;
$thread[] = array($msg, $cmd);
echo ".";
} else {
$thread[] = array("Dmap for ".$file." already exists", "true");
echo "o";
}
}
echo "\n\n";
if (is_array($thread)) {
msg("Creating dmaps with ".$xt." threads");
multiexec($thread,$xt);
}
} else { } else {
@@ -817,45 +971,73 @@ if (!args("nomap")) {
} }
if (shell_exec("exiftool -s -s -s -CropAngle ".$files[0]." 2>&1")) { $thread = null;
msg("Crop angle detected on ".$files[0].", continue?",2);
} echo "\n";
msg("Detecting skew...");
foreach ($files as $file) { foreach ($files as $file) {
if (args("nomap")) { if (args("nomap")) {
$dmfile = $file; $dmfile = $file;
} else { } else {
$dmfile = "scratch/".basename($file, ".jpg")."_dmap.jpg"; $ext = pathinfo($file,PATHINFO_EXTENSION);
$dmfile = "scratch/".basename($file, ".".$ext)."_dmap.jpg";
} }
$result = shell_exec("deskew -o /tmp/null -l 99 \"".$dmfile."\" 2>&1"); $result = shell_exec("deskew -o /tmp/null -l 99 \"".$dmfile."\" 2>&1");
$arr = explode("Skew angle found: ", $result); $arr = explode("Skew angle found: ", $result);
$angle = substr($arr[1], 0, 4); $angle = substr($arr[1], 0, 4);
echo $file.": ".$angle; $msg = $file.": ".$angle;
if ($angle > $deskew_max_angle | $angle < ($deskew_max_angle*-1)) { if ($angle > $deskew_max_angle | $angle < ($deskew_max_angle*-1)) {
echo " (too big)"; $msg .= " (too big)";
} else { } else {
$angles[$file] = $angle; $angles[$file] = $angle;
} }
echo "\n"; msg($msg);
} }
// echo "\nWriting angles to EXIF: "; echo "\n\n";
//
// 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");
// }
// }
// } echo "Checking for existing rotation: ";
foreach ($files as $file) {
if(shell_exec("exiftool -s -s -s -CropAngle ".$file." 2>&1")) {
$msg = $file." already has a CropAngle";
$cmd = "true";
echo "o";
} else {
if (isset($angles[$file])) {
$msg = "Writing CropAngle ".$angles[$file]." to ".$file;
$cmd = "exiftool -overwrite_original -XMP-crs:CropAngle=".$angles[$file]." ".$file;
echo ".";
} else {
$msg = "No CropAngle for ".$file.", writing Rating=1";
$cmd = "exiftool -overwrite_original -Rating=1 ".$file;
echo ",";
}
}
$thread[] = array($msg, $cmd);
}
echo "\n\n";
if (is_array($thread)) {
msg("Writing EXIF tags with ".$xt." threads");
//multiexec($thread,$xt);
}
//////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////
// Elif // Elif
@@ -867,135 +1049,6 @@ echo Welcome("Program name \"".$argv[1]."\" not found");
die; 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;
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");
//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");
}
}
}
// check bridge labels // check bridge labels
// for i in *; do exiftool -s -s -s -Label $i; done // for i in *; do exiftool -s -s -s -Label $i; done