0.8.3
This commit is contained in:
246
leaf.php
246
leaf.php
@@ -1,7 +1,8 @@
|
||||
<?php
|
||||
|
||||
// Leaf - Tools for Book Scans
|
||||
$version = "0.8.1";
|
||||
$version = "0.8.3";
|
||||
$debug = 0;
|
||||
$time_start = microtime(true);
|
||||
date_default_timezone_set("America/Los_Angeles");
|
||||
|
||||
@@ -17,7 +18,20 @@ if (in_array($cores,$reasonable_cores)) {
|
||||
|
||||
// Functions
|
||||
|
||||
function levels_ps_to_imgk($string) {
|
||||
$parts = explode(",", $string);
|
||||
if (count($parts) != 3) {
|
||||
msg("Invalid levels string",1);
|
||||
} else {
|
||||
$black = ($parts[0]/253)."%";
|
||||
$white = (number_format(($parts[1]/255),5)*100)."%";
|
||||
$gamma = $parts[2];
|
||||
return $black.",".$white.",".$gamma;
|
||||
}
|
||||
}
|
||||
|
||||
function multiexec($thread, $x, $null = true) {
|
||||
global $debug;
|
||||
$batch = array_chunk($thread,$x);
|
||||
foreach ($batch as $group) {
|
||||
$msg = array();
|
||||
@@ -37,6 +51,9 @@ function multiexec($thread, $x, $null = true) {
|
||||
$exec = implode(" & ",$cmd)." & wait";
|
||||
}
|
||||
echo $echo;
|
||||
if ($debug) {
|
||||
echo "\n\n".$exec."\n\n";
|
||||
}
|
||||
exec($exec);
|
||||
}
|
||||
}
|
||||
@@ -99,6 +116,8 @@ function args($query) {
|
||||
return $argv[1];
|
||||
} elseif ($query == "dir" && count($argv) > 2) {
|
||||
return chop($argv[count($argv)-1], "/")."/";
|
||||
} elseif ($query == "levels" && isset($opt['levels'])) {
|
||||
return levels_ps_to_imgk($opt['levels']);
|
||||
} elseif (isset($opt[$query])) {
|
||||
return $opt[$query];
|
||||
} else {
|
||||
@@ -158,6 +177,7 @@ $help = "Leaf $version
|
||||
USAGE: leaf [mode] [-options] directory
|
||||
|
||||
Modes:
|
||||
build combine images into a pdf with img2pdf
|
||||
clean remove scratch files
|
||||
crop define EXIF crop values using template files
|
||||
-crops=<num> (6) specify how many files to use
|
||||
@@ -168,11 +188,17 @@ deskew detect rotation angles
|
||||
-contrast=<num> (20) levels attenuation
|
||||
-size=<num> (2200) dmap size in pixels
|
||||
-nomap don't use dmaps
|
||||
despine remove shadow from spine
|
||||
-target=<file> only process pages with spine similar to the spine of this target file
|
||||
-levels=<params> levels for overlay (ex. \"0-253,0-255,0-2\")
|
||||
-width=<pixels> (300) spine width in pixels
|
||||
-q=<0-100> (auto) jpeg quality, default read from source or 95
|
||||
divide wrapper for imagemagick Divide_Src
|
||||
-map=<file> specify brightness file
|
||||
-adjust=<params> levels adjustment (ex. \"0%,98%,.9\")
|
||||
-levels=<params> photoshop levels adjustment (ex. \"0-253,0-255,0-2\")
|
||||
-q=<quality> (95) quality out of 100
|
||||
-pages map file is for LR rotated pages
|
||||
-mix=<0-100> percentage mix with original (90% = mostly og)
|
||||
dupes Find duplicate images using computed PHASH on thumbnails
|
||||
-threshold=<num> (10) match threshold
|
||||
-walk=<num> (5) comparison scope (compare n image to n, n+1, n+2, etc)
|
||||
@@ -182,10 +208,9 @@ generate create final jpg images for pdf creation
|
||||
-pixels=<num> scale to x pixels on longest side
|
||||
-inches=<num> set dpi to x inches on longest side
|
||||
-q=<0-100> (90) jpeg quality
|
||||
-adjust=<params> levels adjustment (ex. \"0%,98%,.9\")
|
||||
-levels=<params> photoshop levels adjustment (ex. \"0-253,0-255,0-2\")
|
||||
-px=<num> png size multiplier
|
||||
-padjust=<params> png levels adjustment (otherwise calculated from adjust)
|
||||
makepdf combine images into a pdf with img2pdf
|
||||
-plevels=<params> png levels adjustment (otherwise calculated from adjust)
|
||||
match move files and set crop values to match source dir
|
||||
-source=<dir> read FROM dir
|
||||
profile apply xmp profile to images (requires exiv2 > 0.25)
|
||||
@@ -208,6 +233,143 @@ strip strip exif crop values from images with exiftool
|
||||
echo $help;
|
||||
fin();
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Note: Brightness_table
|
||||
//////////////////////
|
||||
} elseif (args("app") == "brightness_table") {
|
||||
echo Welcome("Display brightness values for a directory of images");
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
|
||||
$files = glob(args("dir")."*.*");
|
||||
foreach ($files as $file) {
|
||||
echo $file.": ".filter_var(shell_exec("convert ".$file." -colorspace Gray -format \"%[fx:quantumrange*image.mean]\" info:"),FILTER_SANITIZE_NUMBER_INT)."\n";
|
||||
}
|
||||
|
||||
fin();
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Note: Despine
|
||||
//////////////////////
|
||||
} elseif (args("app") == "despine") {
|
||||
echo Welcome("Remove spine on white pages");
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
|
||||
$threshold = 190;
|
||||
$default_quality = 95;
|
||||
$dest = rtrim(args("dir"), '/')."_despined";
|
||||
|
||||
if (count(glob($dest."/*.*"))) {
|
||||
msg("Files already exist in ".$dest.". Move to trash?",2);
|
||||
exec("/opt/local/bin/trash -a ".$dest);
|
||||
mkdir($dest);
|
||||
} elseif (!is_dir($dest)) {
|
||||
mkdir($dest);
|
||||
}
|
||||
|
||||
if (args("width")) {
|
||||
$swidth = args("width");
|
||||
} else {
|
||||
$swidth = 240;
|
||||
}
|
||||
|
||||
if (args("levels")) {
|
||||
$adjust = args("levels");
|
||||
} else {
|
||||
$adjust = levels_ps_to_imgk("0,250,1");
|
||||
}
|
||||
|
||||
//if (!args("target") || !is_file(args("target"))) {
|
||||
// msg("Specify a target file to match spine",1);
|
||||
// } else {
|
||||
// $target = args("target");
|
||||
// }
|
||||
|
||||
//$ext = pathinfo($target,PATHINFO_EXTENSION);
|
||||
//$tspine_left = "scratch/".basename($target, ".".$ext)."_spineleft.".$ext;
|
||||
//$tspine_right = "scratch/".basename($target, ".".$ext)."_spineright.".$ext;
|
||||
//list ($width, $height) = getimagesize($target);
|
||||
//$even = 0;
|
||||
//if (substr(basename($target), 0, 3) % 2 == 0) {
|
||||
// $geo = "+".($width-$swidth)."+0";
|
||||
// $cropsize = $swidth."x".$height;
|
||||
// $crop = $cropsize.$geo;
|
||||
// $even = 1;
|
||||
// } else {
|
||||
// $geo = "+0+0";
|
||||
// $cropsize = $swidth."x".$height;
|
||||
// $crop = $cropsize.$geo;
|
||||
// }
|
||||
//
|
||||
//if ($even) {
|
||||
// exec("convert ".$target." -crop ".$crop." -flop ".$tspine_left);
|
||||
// exec("convert ".$target." -crop ".$crop." ".$tspine_right);
|
||||
// } else {
|
||||
// exec("convert ".$target." -crop ".$crop." ".$tspine_left);
|
||||
// exec("convert ".$target." -crop ".$crop." -flop ".$tspine_right);
|
||||
// }
|
||||
|
||||
$files = glob(args("dir")."[0-9][0-9][0-9]-*");
|
||||
|
||||
msg("Comparing spines");
|
||||
|
||||
foreach ($files as $file) {
|
||||
$ext = pathinfo($file,PATHINFO_EXTENSION);
|
||||
list ($width, $height) = getimagesize($file);
|
||||
if (substr(basename($file), 0, 3) % 2 == 0) {
|
||||
$geo = "+".($width-$swidth)."+0";
|
||||
$cropsize = $swidth."x".$height;
|
||||
$crop = $cropsize.$geo;
|
||||
// $mytspine = $tspine_right;
|
||||
} else {
|
||||
$geo = "+0+0";
|
||||
$cropsize = $swidth."x".$height;
|
||||
$crop = $cropsize.$geo;
|
||||
// $mytspine = $tspine_left;
|
||||
}
|
||||
// $filespine = "/tmp/".basename($file,$ext)."-spine.".$ext;
|
||||
// if (!file_exists($filespine)) {
|
||||
// exec("convert ".$file." -auto-orient -crop ".$crop." ".$filespine);
|
||||
// }
|
||||
|
||||
// $distance = chop(shell_exec("compare -metric phash ".$mytspine." ".$filespine." /tmp/diffimage 2>&1"));
|
||||
|
||||
// if ($distance < $threshold) {
|
||||
// $print_distance = bashcolor($distance,"green");
|
||||
// } else {
|
||||
// $print_distance = $distance;
|
||||
// }
|
||||
// msg("Comparing ".$mytspine." with ".$filespine.": ".$print_distance);
|
||||
|
||||
if (args("q")) {
|
||||
$quality = args("q");
|
||||
} elseif (strtolower($ext) == "jpg") {
|
||||
$quality = chop(shell_exec("identify -format '%Q' ".$file));
|
||||
} else {
|
||||
$quality = $default_quality;
|
||||
}
|
||||
|
||||
//if ($distance < $threshold) {
|
||||
$msg = $file." matched, despining...";
|
||||
$cmd = "convert ".$file." \( ".$file." -auto-orient -crop ".$crop." -colorspace gray -level ".$adjust." \) -auto-orient -geometry ".$geo." -compose Divide_Src -composite -quality ".$quality." ".$dest."/".basename($file);
|
||||
if (args("mix")) {
|
||||
$pass1 = str_replace("convert", "convert \(", $cmd);
|
||||
$pass2 = str_replace("-composite", "-composite \) ".$file." -auto-orient -compose dissolve -define compose:args=".args("mix")." -composite", $pass1);
|
||||
$cmd = $pass2;
|
||||
}
|
||||
//} else {
|
||||
//$msg = $file." <skip>";
|
||||
//$cmd = "true";
|
||||
//}
|
||||
|
||||
$thread[] = array($msg, $cmd);
|
||||
}
|
||||
|
||||
echo "\n\n";
|
||||
msg("Beginning multithreaded convert with ".$xt." threads");
|
||||
multiexec($thread,$xt);
|
||||
|
||||
fin();
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Note: Match
|
||||
//////////////////////
|
||||
@@ -221,6 +383,9 @@ if (!$files) {
|
||||
msg("No valid files in source dir!",1);
|
||||
}
|
||||
|
||||
$i = 0;
|
||||
$ix = 0;
|
||||
|
||||
foreach ($files as $file) {
|
||||
$ext = pathinfo($file,PATHINFO_EXTENSION);
|
||||
$key = substr(basename($file), 0, 3);
|
||||
@@ -231,7 +396,10 @@ foreach ($files as $file) {
|
||||
if (basename($file,$ext) != basename($dest,$dparts['extension'])) {
|
||||
$newdest = $dparts['dirname']."/".$key."-".$dparts['basename'];
|
||||
msg("Moving ".$dest." to ".$newdest);
|
||||
//rename($dest,$newdest);
|
||||
rename($dest,$newdest);
|
||||
$i++;
|
||||
} else {
|
||||
$newdest = $dest;
|
||||
}
|
||||
$parts = chop(shell_exec("exiftool -s -s -s -XMP-crs:CropTop -XMP-crs:CropRight -XMP-crs:CropLeft -XMP-crs:CropBottom -XMP-crs:CropAngle ".$file." 2>&1"));
|
||||
@list($top, $right, $left, $bottom, $angle) = explode("\n", $parts);
|
||||
@@ -243,7 +411,8 @@ foreach ($files as $file) {
|
||||
$info .= " Bottom: ".bashcolor($bottom,"green");
|
||||
$info .= " Angle: ".bashcolor($angle,"green");
|
||||
msg($info);
|
||||
//exec("exiftool -overwrite_original -XMP-crs:HasCrop=1 -XMP-crs:AlreadyApplied=0 -XMP-crs:CropTop=".$top." -XMP-crs:CropRight=".$right." -XMP-crs:CropLeft=".$left." -XMP-crs:CropBottom=".$bottom." -XMP-crs:CropAngle=".$angle." ".$file);
|
||||
exec("exiftool -overwrite_original -XMP-crs:HasCrop=1 -XMP-crs:AlreadyApplied=0 -XMP-crs:CropTop=".$top." -XMP-crs:CropRight=".$right." -XMP-crs:CropLeft=".$left." -XMP-crs:CropBottom=".$bottom." -XMP-crs:CropAngle=".$angle." ".$newdest);
|
||||
$ix++;
|
||||
} else {
|
||||
msg("Source file ".$file." does not contain crop information");
|
||||
}
|
||||
@@ -253,6 +422,11 @@ foreach ($files as $file) {
|
||||
|
||||
}
|
||||
|
||||
echo "\n";
|
||||
|
||||
msg("Moved ".$i." files");
|
||||
msg("Tagged ".$ix." files");
|
||||
|
||||
fin();
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
@@ -410,9 +584,9 @@ foreach ($jpg as $parts) {
|
||||
|
||||
}
|
||||
|
||||
if (args("adjust")) {
|
||||
$msg .= " (".args("adjust").")";
|
||||
$cmd .= "-level ".args("adjust")." ";
|
||||
if (args("levels")) {
|
||||
$msg .= " (".args("levels").")";
|
||||
$cmd .= "-level ".args("levels")." ";
|
||||
}
|
||||
|
||||
$msg .= " [Q=".$quality."]";
|
||||
@@ -441,8 +615,8 @@ if (isset($bitmap)) {
|
||||
$msg = "Processing bitmap page ".$file." @ ".$pngdims."!";
|
||||
$cmd = "convert -resize ".$pngdims."\! ";
|
||||
|
||||
if (args("adjust")) {
|
||||
$adjust = args("adjust");
|
||||
if (args("levels")) {
|
||||
$adjust = args("levels");
|
||||
} else {
|
||||
$adjust = "0%,100%,1";
|
||||
}
|
||||
@@ -452,8 +626,8 @@ if (isset($bitmap)) {
|
||||
$white = rtrim($parts[1],"%");
|
||||
$gamma = $parts[2];
|
||||
|
||||
if (args("padjust")) {
|
||||
$radjust = args("padjust");
|
||||
if (args("plevels")) {
|
||||
$radjust = args("plevels");
|
||||
} else {
|
||||
$radjust = ($black+60)."%".",".($white-15)."%".",".($gamma-.7);
|
||||
}
|
||||
@@ -564,7 +738,7 @@ foreach ($files as $file) {
|
||||
if (args("q")) {
|
||||
$quality = args("q");
|
||||
} elseif (strtolower($ext) == "jpg") {
|
||||
$quality = @exec("identify -format '%Q' ".$file);
|
||||
$quality = chop(shell_exec("identify -format '%Q' ".$file));
|
||||
} else {
|
||||
$quality = $default_quality;
|
||||
}
|
||||
@@ -1046,9 +1220,9 @@ foreach ($files as $file) {
|
||||
fin();
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Note: Makepdf
|
||||
// Note: Build
|
||||
//////////////////////
|
||||
} elseif (args("app") == "makepdf") {
|
||||
} elseif (args("app") == "build") {
|
||||
echo Welcome("Combine finished images into a pdf with python img2pdf");
|
||||
/////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@@ -1144,6 +1318,11 @@ if (!args("map")) {
|
||||
$map = args("map");
|
||||
}
|
||||
|
||||
if (args("mpush")) {
|
||||
$map = basename(args("map"),".JPG")."-pushed.tif";
|
||||
@exec("convert -level ".levels_ps_to_imgk(args("mpush"))." ".args("map")." ".$map);
|
||||
}
|
||||
|
||||
$dest = rtrim(args("dir"), '/')."_divided";
|
||||
|
||||
if (count(glob($dest."/*.*"))) {
|
||||
@@ -1164,8 +1343,9 @@ if ($pages) {
|
||||
$ext = pathinfo($map, PATHINFO_EXTENSION);
|
||||
$mapleft = "scratch/".str_replace(".".$ext, "_left.".$ext, $map);
|
||||
$mapright = "scratch/".str_replace(".".$ext, "_right.".$ext, $map);
|
||||
exec("convert -rotate 90 -quality 95 ".$map." ".$mapleft);
|
||||
exec("convert -rotate 270 -quality 95 ".$map." ".$mapright);
|
||||
//@exec("convert -rotate 90 -quality 95 ".$map." ".$mapleft." 2>&1");
|
||||
@exec("convert -rotate 90 -flip -quality 95 ".$map." ".$mapleft." 2>&1");
|
||||
@exec("convert -rotate 270 -quality 95 ".$map." ".$mapright." 2>&1");
|
||||
}
|
||||
|
||||
echo "Building threads: ";
|
||||
@@ -1174,7 +1354,7 @@ $thread = array();
|
||||
foreach ($files as $file) {
|
||||
|
||||
$msg = "Dividing ".$file." with ".$map.", Q=".$quality;
|
||||
$cmd = "convert ".$file." ";
|
||||
$cmd = "convert ".$file;
|
||||
$output = $dest."/".basename($file);
|
||||
|
||||
if ($pages) {
|
||||
@@ -1198,7 +1378,7 @@ foreach ($files as $file) {
|
||||
list ($twidth, $theight) = getimagesize($file);
|
||||
if ($width != $twidth || $height != $theight) {
|
||||
$tmap = $use."'[".$twidth."x".$theight."!]'";
|
||||
$msg .= " (resize map) ";
|
||||
$msg .= " (resize map)";
|
||||
} else {
|
||||
$tmap = $use;
|
||||
}
|
||||
@@ -1209,16 +1389,30 @@ foreach ($files as $file) {
|
||||
|
||||
}
|
||||
|
||||
$cmd .= $tmap." -compose Divide_Src -composite -quality ".$quality." ";
|
||||
|
||||
if (args("adjust")) {
|
||||
$msg .= " (".args("adjust").") ";
|
||||
$cmd .= "-level ".args("adjust")." ";
|
||||
if (args("blur")) {
|
||||
$msg .= " (".args("blur").")";
|
||||
$overlay = "\( ".$tmap." -blur ".args("blur")." \)";
|
||||
} else {
|
||||
$overlay = $tmap;
|
||||
}
|
||||
|
||||
$cmd .= " ".$overlay." -compose Divide_Src -composite -quality ".$quality." ";
|
||||
|
||||
if (args("levels")) {
|
||||
$msg .= " (".args("levels").") ";
|
||||
$cmd .= "-level ".args("levels")." ";
|
||||
}
|
||||
|
||||
$cmd .= $output;
|
||||
|
||||
echo ".";
|
||||
|
||||
if (args("mix")) {
|
||||
$pass1 = str_replace("convert", "convert \(", $cmd);
|
||||
$pass2 = str_replace("-composite", "-composite \) ".$file." -compose dissolve -define compose:args=".args("mix")." -composite", $pass1);
|
||||
$cmd = $pass2;
|
||||
}
|
||||
|
||||
$thread[] = array($msg, $cmd);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user