0.6.6
This commit is contained in:
376
leaf.php
376
leaf.php
@@ -2,16 +2,25 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
// Leaf - Tools for Book Scans
|
// Leaf - Tools for Book Scans
|
||||||
$version = "0.6.3";
|
$version = "0.6.6";
|
||||||
$time_start = microtime(true);
|
$time_start = microtime(true);
|
||||||
|
date_default_timezone_set("America/Los_Angeles");
|
||||||
|
|
||||||
// Functions
|
// Functions
|
||||||
|
|
||||||
function fin() {
|
function fin($print = null) {
|
||||||
|
if (isset($print)) {
|
||||||
|
echo "\n".$print;
|
||||||
|
}
|
||||||
echo "\nFinished in ".floor($time = microtime(true)-$_SERVER["REQUEST_TIME_FLOAT"])." seconds\n";
|
echo "\nFinished in ".floor($time = microtime(true)-$_SERVER["REQUEST_TIME_FLOAT"])." seconds\n";
|
||||||
die;
|
die;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function ask($text) {
|
||||||
|
echo $text."\n";
|
||||||
|
return trim(fgets(fopen("php://stdin","r")));
|
||||||
|
}
|
||||||
|
|
||||||
function msg($text, $action = null) {
|
function msg($text, $action = null) {
|
||||||
if ($action == 1) {
|
if ($action == 1) {
|
||||||
echo bashcolor($text."\n","red"); die;
|
echo bashcolor($text."\n","red"); die;
|
||||||
@@ -35,7 +44,7 @@ function args($query) {
|
|||||||
}
|
}
|
||||||
if ($query == "app" && isset($argv[1])) {
|
if ($query == "app" && isset($argv[1])) {
|
||||||
return $argv[1];
|
return $argv[1];
|
||||||
} elseif ($query == "dir") {
|
} elseif ($query == "dir" && count($argv) > 2) {
|
||||||
return chop($argv[count($argv)-1], "/")."/";
|
return chop($argv[count($argv)-1], "/")."/";
|
||||||
} elseif (isset($opt[$query])) {
|
} elseif (isset($opt[$query])) {
|
||||||
return $opt[$query];
|
return $opt[$query];
|
||||||
@@ -89,29 +98,263 @@ if (!is_dir("scratch")) {
|
|||||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// Help
|
// Help
|
||||||
//////////////////////
|
//////////////////////
|
||||||
if (!args("app")) {
|
if (!args("app") | args("app") == "help" | args("app") == "-help" | args("app") == "--help") {
|
||||||
/////////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
$help = "Leaf $version
|
$help = "Leaf $version
|
||||||
USAGE: leaf [mode] [-options] directory
|
USAGE: leaf [mode] [-options] directory
|
||||||
|
|
||||||
Modes:
|
Modes:
|
||||||
review print a table of image statistics
|
desort remove image sequence prefix
|
||||||
dpiset set image dpi
|
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
|
-x=<dpi> specify dpi
|
||||||
-height=<inches> calculate dpi from specified height
|
-height=<inches> calculate dpi from specified height
|
||||||
makepdf combine images into a pdf
|
sort sort files as AAABBB -> ABABAB
|
||||||
stripcrop strip exif crop values from images
|
-m=<num> specify midpoint (cover image)
|
||||||
|
strip strip exif crop values from images with exiftool
|
||||||
|
|
||||||
";
|
";
|
||||||
echo $help;
|
echo $help;
|
||||||
fin();
|
fin();
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// Review: output a table of stats on source images
|
// 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") {
|
} elseif (args("app") == "review") {
|
||||||
echo Welcome("List image statistics");
|
echo Welcome("List image dimension statistics");
|
||||||
/////////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
$files = glob(args("dir")."*.*");
|
$files = glob(args("dir")."*.*");
|
||||||
@@ -146,8 +389,12 @@ foreach ($files as $file) {
|
|||||||
}
|
}
|
||||||
$print[] = bashcolor($file, $hilite[pathinfo($file, PATHINFO_EXTENSION)]);
|
$print[] = bashcolor($file, $hilite[pathinfo($file, PATHINFO_EXTENSION)]);
|
||||||
$size = $attr['File Size'];
|
$size = $attr['File Size'];
|
||||||
|
if ($ext != "DNG") { $quality = @exec("identify -format '%Q' ".$file); }
|
||||||
|
if (isset($quality)) {
|
||||||
|
$size .= " [Q=".$quality."]";
|
||||||
|
}
|
||||||
if (isset($attr['Photoshop Quality'])) {
|
if (isset($attr['Photoshop Quality'])) {
|
||||||
$size .= " (Quality = ".$attr['Photoshop Quality'].")";
|
$size .= " [PQ=".$attr['Photoshop Quality']."]";
|
||||||
}
|
}
|
||||||
$print[] = $size;
|
$print[] = $size;
|
||||||
if (isset($attr['Profile Description'])) {
|
if (isset($attr['Profile Description'])) {
|
||||||
@@ -187,10 +434,10 @@ foreach ($files as $file) {
|
|||||||
fin();
|
fin();
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// Makepdf: combine finished images into a pdf with python img2pdf
|
// Makepdf
|
||||||
//////////////////////
|
//////////////////////
|
||||||
} elseif (args("app") == "makepdf") {
|
} elseif (args("app") == "makepdf") {
|
||||||
echo Welcome("Combine images into pdf");
|
echo Welcome("Combine finished images into a pdf with python img2pdf");
|
||||||
/////////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
$input = args("dir")."*.*";
|
$input = args("dir")."*.*";
|
||||||
@@ -208,9 +455,73 @@ exec("open ".$dest." -b com.adobe.Acrobat.Pro");
|
|||||||
fin();
|
fin();
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// DPIset: batch set resolution tags
|
// 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") == "dpiset") {
|
} 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");
|
echo Welcome("Batch set EXIF resolution tags");
|
||||||
/////////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
@@ -250,10 +561,39 @@ foreach ($files as $file) {
|
|||||||
fin();
|
fin();
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// Deskew: detect skew angle and apply to rotation tag
|
// 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") {
|
} elseif (args("app") == "deskew") {
|
||||||
echo Welcome("Detect skew angles");
|
echo Welcome("Detect skew angle and apply to EXIF tags");
|
||||||
/////////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
$deskew_max_angle = ".4";
|
$deskew_max_angle = ".4";
|
||||||
|
|||||||
Reference in New Issue
Block a user