#!/usr/bin/php 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 (!is_dir(args("dir"))) { msg("Problem with working dir: ".args("dir"), 1); } if (!is_dir("scratch")) { mkdir("scratch"); } //////////////////////////////////////////////////////////////////////////////////////////////// // Review: output a table of stats on source images ////////////////////// if (args("app") == "review") { echo Welcome("List image 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 (isset($attr['Photoshop Quality'])) { $size .= " (Quality = ".$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: combine finished images into a pdf with python img2pdf ////////////////////// } elseif (args("app") == "makepdf") { echo Welcome("Combine images into pdf"); ///////////////////////////////////////////////////////////////////////// $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); msg("Press return to open in Acrobat",2); exec("open ".$dest." -b com.adobe.Acrobat"); fin(); //////////////////////////////////////////////////////////////////////////////////////////////// // DPIset: batch set resolution tags ////////////////////// } elseif (args("app") == "dpiset") { 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 ($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(); //////////////////////////////////////////////////////////////////////////////////////////////// // Deskew: detect skew angle and apply to rotation tag ////////////////////// } elseif (args("app") == "deskew") { echo Welcome("Detect skew angles"); ///////////////////////////////////////////////////////////////////////// $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 ?>