This commit is contained in:
2019-05-07 01:54:51 -07:00
parent bf12ac7237
commit 1744484ff4

217
leaf.php
View File

@@ -1,7 +1,7 @@
<?php <?php
// Leaf - Tools for Book Scans // Leaf - Tools for Book Scans
$version = "0.8.4"; $version = "0.8.6";
$debug = 1; $debug = 1;
$time_start = microtime(true); $time_start = microtime(true);
date_default_timezone_set("America/Los_Angeles"); date_default_timezone_set("America/Los_Angeles");
@@ -23,13 +23,25 @@ function levels_ps_to_imgk($string) {
if (count($parts) != 3) { if (count($parts) != 3) {
msg("Invalid levels string",1); msg("Invalid levels string",1);
} else { } else {
$black = ($parts[0]/253)."%"; $black = (number_format(($parts[0]/253),5)*100)."%";
$white = (number_format(($parts[1]/255),5)*100)."%"; $white = (number_format(($parts[1]/255),5)*100)."%";
$gamma = $parts[2]; $gamma = $parts[2];
return $black.",".$white.",".$gamma; return $black.",".$white.",".$gamma;
} }
} }
function unsharp_ps_to_imgk($string) {
$parts = explode(",", $string);
if (count($parts) != 3) {
msg("Invalid levels string",1);
} else {
$radius = "0x".($parts[1]+1);
$amount = $parts[0]/100;
$threshold = $parts[2]/255;
return $radius."+".$amount."+".$threshold;
}
}
function multiexec($thread, $x, $null = true) { function multiexec($thread, $x, $null = true) {
global $debug; global $debug;
$batch = array_chunk($thread,$x); $batch = array_chunk($thread,$x);
@@ -45,14 +57,15 @@ function multiexec($thread, $x, $null = true) {
} else { } else {
$echo = implode("", $msg); $echo = implode("", $msg);
} }
if ($null) { if ($debug || !$null) {
$exec = implode(" > /dev/null & ",$cmd)." & wait"; $pipe = "";
} else { } else {
$exec = implode(" & ",$cmd)." & wait"; $pipe = " 2>&1";
} }
$exec = implode($pipe." & ",$cmd).$pipe." & wait";
echo $echo; echo $echo;
if ($debug) { if ($debug) {
echo "\n\n".$exec."\n\n"; echo "\n".str_replace($pipe." & ", "\n\n", $exec)."\n";
} }
@exec($exec); @exec($exec);
} }
@@ -118,6 +131,10 @@ function args($query) {
return chop($argv[count($argv)-1], "/")."/"; return chop($argv[count($argv)-1], "/")."/";
} elseif ($query == "levels" && isset($opt['levels'])) { } elseif ($query == "levels" && isset($opt['levels'])) {
return levels_ps_to_imgk($opt['levels']); return levels_ps_to_imgk($opt['levels']);
} elseif ($query == "plevels" && isset($opt['plevels'])) {
return levels_ps_to_imgk($opt['plevels']);
} elseif ($query == "unsharp" && isset($opt['unsharp'])) {
return unsharp_ps_to_imgk($opt['unsharp']);
} elseif (isset($opt[$query])) { } elseif (isset($opt[$query])) {
return $opt[$query]; return $opt[$query];
} else { } else {
@@ -191,7 +208,8 @@ deskew detect rotation angles
depaper divide page with itself using mask (better despine) depaper divide page with itself using mask (better despine)
-mask=<file> mask file for spine-left image -mask=<file> mask file for spine-left image
-mpush=<levels> levels for divide map (ex. \"0-253,0-255,0-2\") -mpush=<levels> levels for divide map (ex. \"0-253,0-255,0-2\")
-mix=<0-100> percentage mix with original (90% = mostly og) -mix=<0-100> (null) percentage mix with original (90% = mostly og)
-skip=<label> (null) skip files with this label
despine remove shadow from spine despine remove shadow from spine
-levels=<params> levels for overlay (ex. \"0-253,0-255,0-2\") -levels=<params> levels for overlay (ex. \"0-253,0-255,0-2\")
-width=<pixels> (300) spine width in pixels -width=<pixels> (300) spine width in pixels
@@ -216,8 +234,13 @@ generate create final jpg images for pdf creation
-levels=<params> photoshop levels adjustment (ex. \"0-253,0-255,0-2\") -levels=<params> photoshop levels adjustment (ex. \"0-253,0-255,0-2\")
-px=<num> png size multiplier -px=<num> png size multiplier
-plevels=<params> png levels adjustment (otherwise calculated from adjust) -plevels=<params> png levels adjustment (otherwise calculated from adjust)
-pt=<0-255> use threshold method with photoshop level value
-pc=<2,2!,3> (2) colors for final png (2! = intermediary dithering)
-unsharp=<params> photoshop unsharp parameters (ex. \"100,1,0\")
match move files and set crop values to match source dir match move files and set crop values to match source dir
-source=<dir> read FROM dir -source=<dir> read FROM dir
notes save leaf shell history to text file
-lines=<num> (100) lines to read from bash_history
profile apply xmp profile to images (requires exiv2 > 0.25) profile apply xmp profile to images (requires exiv2 > 0.25)
-file=<file> xmp profile -file=<file> xmp profile
resort reorder image sequence by adding a new image resort reorder image sequence by adding a new image
@@ -238,6 +261,34 @@ strip strip exif crop values from images with exiftool
echo $help; echo $help;
fin(); fin();
////////////////////////////////////////////////////////////////////////////////////////////////
// Note: Notes
//////////////////////
} elseif (args("app") == "notes") {
echo Welcome("Save shell commands to text file");
/////////////////////////////////////////////////////////////////////////
if (args("lines")) {
$lines = args("lines");
} else {
$lines = 100;
}
$notes = shell_exec("tail -n ".$lines." /Users/peter/.bash_history");
foreach (explode("\n", $notes) as $line) {
if (substr($line, 0, 4) == "leaf") {
$keep[] = $line;
}
}
if (!isset($keep[0])) {
msg("Error reading bash history",1);
} else {
file_put_contents("leaf_notes.txt", implode("\n", $keep));
}
fin();
//////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////
// Note: Brightness_table // Note: Brightness_table
////////////////////// //////////////////////
@@ -265,6 +316,12 @@ if (!file_exists(args("mask"))) {
msg("Can't open mask",1); msg("Can't open mask",1);
} }
if (!args("mpush")) {
msg("Please specify divide levels with mpush",1);
} else {
$mpush = levels_ps_to_imgk(args("mpush"));
}
if (count(glob($dest."/*.*"))) { if (count(glob($dest."/*.*"))) {
msg("Files already exist in ".$dest.". Move to trash?",2); msg("Files already exist in ".$dest.". Move to trash?",2);
exec("/opt/local/bin/trash -a ".$dest); exec("/opt/local/bin/trash -a ".$dest);
@@ -273,17 +330,32 @@ if (count(glob($dest."/*.*"))) {
mkdir($dest); mkdir($dest);
} }
msg("Building threads...");
$files = glob(args("dir")."*.*"); $files = glob(args("dir")."*.*");
foreach ($files as $file) { foreach ($files as $file) {
$ext = pathinfo($file,PATHINFO_EXTENSION); $ext = pathinfo($file,PATHINFO_EXTENSION);
$result = $dest."/".basename($file); $result = $dest."/".basename($file);
list ($width, $height) = getimagesize($file); list ($width, $height) = getimagesize($file);
$divided = "\( ".$file." \( ".$file." -colorspace gray -level ".$mpush." \) -compose Divide_Src -composite \)";
if (substr(basename($file), 0, 3) % 2 == 0) { if (substr(basename($file), 0, 3) % 2 == 0) {
$cmd = "convert test.jpg \( ".$file." \( ".args("mask")."[".$width."x".$height."] -rotate 180 \) -alpha Off -compose CopyOpacity -composite \) -compose Over -composite ".$result; $cmd = "convert ".$file." \( ".$divided." \( ".args("mask")."[".$width."x".$height."!] -flop \) -alpha Off -compose CopyOpacity -composite \) -compose Over -composite ".$result;
} else { } else {
$cmd = "convert test.jpg \( ".$file." ".args("mask")."[".$width."x".$height."] -alpha Off -compose CopyOpacity -composite \) -compose Over -composite ".$result; $cmd = "convert ".$file." \( ".$divided." ".args("mask")."[".$width."x".$height."!] -alpha Off -compose CopyOpacity -composite \) -compose Over -composite ".$result;
}
if (args("mix")) {
$pass1 = str_replace("convert", "convert \(", $cmd);
$pass2 = str_replace("-composite ".$result, "-composite \) ".$file." -auto-orient -compose dissolve -define compose:args=".args("mix")." -composite ".$result, $pass1);
$cmd = $pass2;
} }
$msg = "Converting ".$file; $msg = "Converting ".$file;
if (args("skip")) {
$label = trim(shell_exec("exiftool -s -s -s -Label ".$file));
if ($label == args("skip")) {
$msg .= " <Skipping label=".args("skip").">";
$cmd = "cp ".$file." ".$result;
}
}
$thread[] = array($msg, $cmd); $thread[] = array($msg, $cmd);
} }
@@ -347,7 +419,6 @@ foreach ($files as $file) {
} else { } else {
$quality = $default_quality; $quality = $default_quality;
} }
$msg = $file." matched, despining..."; $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); $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);
@@ -429,7 +500,7 @@ fin();
// Note: Generate // Note: Generate
////////////////////// //////////////////////
} elseif (args("app") == "generate") { } elseif (args("app") == "generate") {
echo Welcome("Generate final jpg images"); echo Welcome("Generate final images");
///////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////
$dest = rtrim(args("dir"), '/')."_generated"; $dest = rtrim(args("dir"), '/')."_generated";
@@ -484,7 +555,7 @@ foreach ($files as $file) {
} }
if ($parts[0] == "bitmap") { if ($parts[0] == "bitmap") {
$bitmap[] = array($file,$width,$height); $bitmap[] = array($file,$width,$height);
} elseif ($parts[1] == "True") { } elseif ($parts[1] == "True" || $parts[0] == "grayscale") {
$grey[] = array($file,$width,$height); $grey[] = array($file,$width,$height);
} elseif ($ratio < 3) { } elseif ($ratio < 3) {
$color[] = array($file,$width,$height); $color[] = array($file,$width,$height);
@@ -559,13 +630,15 @@ if (isset($spine)) {
foreach ($jpg as $parts) { foreach ($jpg as $parts) {
continue;
$file = $parts[0]; $file = $parts[0];
$output = $dest."/".basename($file); $output = $dest."/".basename($file);
if (isset($parts[3]) && $parts[3] == 1) { if (isset($parts[3]) && $parts[3] == 1) {
$msg = "Processing greyscale page ".$file." @ ".$dims."!"; $msg = "Processing greyscale page ".$file." @ ".$dims."!";
$cmd = "convert -resize ".$dims."\! -colorspace gray "; $cmd = "convert -resize ".$dims."\! -colorspace gray -type TrueColor ";
} elseif (isset($parts[3]) && $parts[3] == 2) { } elseif (isset($parts[3]) && $parts[3] == 2) {
@@ -578,6 +651,11 @@ foreach ($jpg as $parts) {
$msg = "Processing color page ".$file." @ ".$dims."!"; $msg = "Processing color page ".$file." @ ".$dims."!";
$cmd = "convert -resize ".$dims."\! "; $cmd = "convert -resize ".$dims."\! ";
if (args("modulate")) {
$msg .= " (".args("modulate").")";
$cmd .= "-modulate ".args("modulate")." ";
}
} }
if (args("levels")) { if (args("levels")) {
@@ -585,6 +663,11 @@ foreach ($jpg as $parts) {
$cmd .= "-level ".args("levels")." "; $cmd .= "-level ".args("levels")." ";
} }
if (args("unsharp")) {
$msg .= " (".args("unsharp").")";
$cmd .= "-unsharp ".args("unsharp")." ";
}
$msg .= " [Q=".$quality."]"; $msg .= " [Q=".$quality."]";
$cmd .= "-quality ".$quality." ".$file." ".$output; $cmd .= "-quality ".$quality." ".$file." ".$output;
@@ -602,6 +685,43 @@ multiexec($thread_b,$xt);
if (isset($bitmap)) { if (isset($bitmap)) {
if (!args("pt")) {
if (args("levels")) {
$adjust = args("levels");
} else {
$adjust = "0%,100%,1";
}
$parts = explode(",", $adjust);
$black = rtrim($parts[0],"%");
$white = rtrim($parts[1],"%");
$gamma = $parts[2];
if (args("plevels")) {
$radjust = args("plevels");
} else {
$radjust = ($black+60)."%".",".($white-15)."%".",".($gamma-.8);
}
msg("Checking bitmap output for brightness...");
$testfile = $bitmap[0][0];
$level = chop(shell_exec("convert \( ".$testfile." -resize ".$pngdims."\! -level ".$radjust." -monochrome -colors 2 -depth 1 \) -colorspace Gray -format \"%[fx:quantumrange*image.mean]\" info:"));
if ($level < 20000) {
msg("Image is dark, applying negate");
$negate = "-negate ";
} else {
msg("Image is bright, skipping negate");
$negate = "";
}
} else {
$threshold = (number_format((args("pt")/255),5)*100)."%";
}
foreach ($bitmap as $parts) { foreach ($bitmap as $parts) {
$file = $parts[0]; $file = $parts[0];
@@ -609,30 +729,47 @@ if (isset($bitmap)) {
$output = $dest."/".basename($file,$ext)."png"; $output = $dest."/".basename($file,$ext)."png";
$msg = "Processing bitmap page ".$file." @ ".$pngdims."!"; $msg = "Processing bitmap page ".$file." @ ".$pngdims."!";
$cmd = "convert -resize ".$pngdims."\! ";
if (args("levels")) { if (args("pt")) {
$adjust = args("levels");
} else {
$adjust = "0%,100%,1";
}
$parts = explode(",", $adjust); if (args("pc") == "2!") { // a b&w bitmap image with an intermediary antialiased step (darker text)
$black = rtrim($parts[0],"%");
$white = rtrim($parts[1],"%");
$gamma = $parts[2];
if (args("plevels")) {
$radjust = args("plevels");
} else {
$radjust = ($black+60)."%".",".($white-15)."%".",".($gamma-.7);
}
$msg .= " (".$radjust.")";
$cmd .= "-level ".$radjust." ";
$cmd .= "-monochrome -colors 2 -depth 1 -negate ".$file." ".$output; $msg .= " (".$threshold.", ".$pl.")";
$cmd = "convert \( \( \( ".$file." -threshold ".$threshold." \) -set colorspace sRGB -type truecolor \) -resize ".$pngdims."\! \) -threshold 95% ".$output;
} elseif (args("pc") == "3") { // a b&w bitmap image with an additional color for antialiasing
$msg .= " (".$threshold.", ".$pl.")";
$cmd = "convert \( \( \( ".$file." -threshold ".$threshold." \) -set colorspace sRGB -type truecolor \) -resize ".$pngdims."\! \) -set colorspace Grayscale -colors 4 -depth 2 ".$output;
} else { // a b&w bitmap image
$msg .= " (".$threshold.")";
$cmd = "convert -resize ".$pngdims."\! -threshold ".$threshold." ".$file." ".$output;
}
} else {
if (args("pc") == "2!") { // a b&w bitmap image with an intermediary antialiased step (darker text)
$msg .= " (".$radjust.", ".$pl.")";
$cmd = "convert \( \( \( ".$file." -level ".$radjust." -monochrome -colors 2 -depth 1 ".$negate."\) -set colorspace sRGB -type truecolor \) -resize ".$pngdims."\! \) -threshold 95% ".$output;
} elseif (args("pc") == "3") { // a b&w bitmap image with an additional color for antialiasing
$msg .= " (".$radjust.", ".$pl.")";
$cmd = "convert \( ".$file." -level ".$radjust." -resize ".$pngdims."\! \) -set colorspace Grayscale -colors 4 -depth 2 ".$output;
} else { // a b&w bitmap image
$msg .= " (".$radjust.")";
$cmd = "convert -resize ".$pngdims."\! -level ".$radjust." -monochrome -colors 2 -depth 1 ".$negate.$file." ".$output;
}
}
$thread_c[] = array($msg, $cmd); $thread_c[] = array($msg, $cmd);
$thread_d[] = array(".","exiftool -overwrite_original -PixelsPerUnitX=".$pngdpi." -PixelsPerUnitY=".$pngdpi." ".$output); $thread_d[] = array(".","exiftool -overwrite_original -PixelsPerUnitX=".$pngdpi." -PixelsPerUnitY=".$pngdpi." ".$output);
@@ -1560,10 +1697,16 @@ if (!args("nomap")) {
$size = $deskew_size."x".$deskew_size; $size = $deskew_size."x".$deskew_size;
if (substr(basename($file), 0, 3) % 2 == 0) { if (strtolower($ext) == "dng") {
$cmd = "convert ".$file." -auto-orient -crop ".$area_right." -level ".$deskew_contrast."%,".(100-$deskew_contrast)."% -colorspace Gray -resize ".$size." ".$dmfile; $adj = "0%,".(100-$deskew_contrast)."%";
} else { } else {
$cmd = "convert ".$file." -auto-orient -crop ".$area_left." -level ".$deskew_contrast."%,".(100-$deskew_contrast)."% -colorspace Gray -resize ".$size." ".$dmfile; $adj = $deskew_contrast."%,".(100-$deskew_contrast)."%";
}
if (substr(basename($file), 0, 3) % 2 == 0) {
$cmd = "convert ".$file." -auto-orient -crop ".$area_right." -level ".$adj." -colorspace Gray -resize ".$size." ".$dmfile;
} else {
$cmd = "convert ".$file." -auto-orient -crop ".$area_left." -level ".$adj." -colorspace Gray -resize ".$size." ".$dmfile;
} }
$msg = "Creating dmap for ".$file; $msg = "Creating dmap for ".$file;