diff options
-rwxr-xr-x | partextract.c | 84 | ||||
-rwxr-xr-x | trimmer | 384 |
2 files changed, 468 insertions, 0 deletions
diff --git a/partextract.c b/partextract.c new file mode 100755 index 0000000..ad5e55b --- /dev/null +++ b/partextract.c @@ -0,0 +1,84 @@ +#!/usr/bin/tcc -run -O2 +#include <stdio.h> +#include <string.h> + +int main(int argc, char const* argv[]) +{ + static const char *beginning; + static const char *ending; + int beginning_found = 0, beginning_length = 0; + int ending_found = 0, ending_length = 0; + + // TODO: getopt to enable this + int multimatching = 0; + + FILE *fp; + int read; + char buf[4096]; + int i; + + if (argc != 4) { + puts("Usage: partextract <file> <start token> <end token>"); + puts(""); + puts("partextract outputs everything between the start and end token"); + puts("(including the tokens). Only the first match will be output"); + puts("unless multimatching is enabled and overlapping tokens will be ignored."); + return 1; + } + + beginning = argv[2]; + ending = argv[3]; + + beginning_length = strlen(beginning); + ending_length = strlen(ending); + + fp = fopen(argv[1], "rb"); + if (fp == NULL) { + perror(argv[1]); + return 1; + } + + while (!feof(fp)) { + read = fread(buf, sizeof(char), sizeof(buf), fp); + for (i = 0; i < read; i++) { + // find the starting token + if (beginning_found != beginning_length && ending_found != ending_length) { + if (buf[i] == beginning[beginning_found]) { + beginning_found++; + if (beginning_found == beginning_length) { + // we don't allow tokens to overlap + ending_found = 0; + + // We only start outputting if we had the complete start token + // This makes sure the token is in the output too + // and jumps to the next char so the fputc below won't run for + // the last char of the token + fputs(beginning, stdout); + continue; + } + } else if (beginning_found != beginning_length) { + beginning_found = 0; + } + } + + // output until we hit the ending token + if (beginning_found == beginning_length && ending_found != ending_length) { + fputc(buf[i], stdout); + + if (buf[i] == ending[ending_found]) { + ending_found++; + + if (multimatching && ending_found == ending_length) { + beginning_found = 0; + ending_found = 0; + } + } else if (ending_found != ending_length) { + ending_found = 0; + } + } + } + } + + + return 0; +} @@ -0,0 +1,384 @@ +#!/bin/bash +# +# Developed by Fred Weinhaus 5/14/2009 .......... revised 5/15/2009 +# +# USAGE: trimmer [-s sides] [-f fuzzval] [-b bcolor] [-g gcolor] infile outfile +# USAGE: trimmer [-h or -help] +# +# OPTIONS: +# +# -s sides comma delimited list of sides to trim; list can include +# any combination of: north, east, south, west or just "all"; +# default=north +# -f fuzzamt fuzz amount in percent for trimming border color; float; +# fuzzamt>=0; default=0 +# -b bcolor background color for area to trim; any valid IM color +# may be specified; default will be determined automatically +# from the average color of all specified sides +# -g gcolor guard color used to preserve sides not be trimmed; +# any valid IM color that is further from the background color +# than the fuzzval; default will be determined automatically +# as either the complement of the background color, black or +# white whichever is furthest from the background color. +# +### +# +# NAME: TRIMMER +# +# PURPOSE: To trim the background from any number of specified sides of an image. +# +# DESCRIPTION: UNROTATE trims the background from any number of specified sides +# of an image. The user may specify from one to four sides as a comma delimited list +# that is enclosed in quotes. A fuzz value may be specified if the background is not +# a constant color. +# +# +# Arguments: +# +# -s sides --- SIDES is a comma delimited list of the sides to be trimmed enclosed +# in quotes. Any number of sides from one to four may be specified. The list can +# include any combination of: north, east, south, west or just "all". +# The default=north. +# +# -f fuzzamt --- FUZZAMT is the fuzz amount specified as a float percent greater +# than zero (without the % sign). The default is zero which indicates that border +# is a uniform color. Larger values are needed when the border is not a uniform color. +# +# -b bcolor --- BCOLOR is the background color for the area to trim. Any valid IM +# color may be specified. The default will be determined automatically from the +# average color of all specified sides. There is no guarantee that this will be +# accurate. So if it does not work, then you will need to supply the border color. +# +# -g gcolor --- GCOLOR is the guard color used to preserve the side that are not +# to be trimmed. Any valid IM color may be specified that is further from the +# background color than the fuzzval. The default will be determined automatically +# as either the complement of the background color, black or white whichever is +# furthest from the background color. There is no guarantee that this will be +# accurate. So if it does not work, then you will need to supply the guard color. +# +# CAVEAT: No guarantee that this script will work on all platforms, +# nor that trapping of inconsistent parameters is complete and +# foolproof. Use At Your Own Risk. +# +###### +# +# set default values; +sides="north" # comma delimited list: north, south, east, west, all +fuzzamt=0 # fuzz amount in percent +bcolor="" # background color; use none for transparent background +gcolor="" # guard color + +# set directory for temporary files +dir="." # suggestions are dir="." or dir="/tmp" + +# set up functions to report Usage and Usage with Description +PROGNAME=`type $0 | awk '{print $3}'` # search for executable on path +PROGDIR=`dirname $PROGNAME` # extract directory of program +PROGNAME=`basename $PROGNAME` # base name of program +usage1() + { + echo >&2 "" + echo >&2 "$PROGNAME:" "$@" + sed >&2 -n '/^###/q; /^#/!q; s/^#//; s/^ //; 4,$p' "$PROGDIR/$PROGNAME" + } +usage2() + { + echo >&2 "" + echo >&2 "$PROGNAME:" "$@" + sed >&2 -n '/^######/q; /^#/!q; s/^#*//; s/^ //; 4,$p' "$PROGDIR/$PROGNAME" + } + +# function to report error messages +errMsg() + { + echo "" + echo $1 + echo "" + usage1 + exit 1 + } + +# function to test for minus at start of value of second part of option 1 or 2 +checkMinus() + { + test=`echo "$1" | grep -c '^-.*$'` # returns 1 if match; 0 otherwise + [ $test -eq 1 ] && errMsg "$errorMsg" + } + +# test for correct number of arguments and get values +if [ $# -eq 0 ] + then + # help information + echo "" + usage2 + exit 0 +elif [ $# -gt 10 ] + then + errMsg "--- TOO MANY ARGUMENTS WERE PROVIDED ---" +else + while [ $# -gt 0 ] + do + # get parameters + case "$1" in + -h|-help) # help information + echo "" + usage2 + ;; + -s) # sides + shift # to get the next parameter + # test if parameter starts with minus sign + errorMsg="--- INVALID SIDES SPECIFICATION ---" + checkMinus "$1" + sides=$1 + # further testing done later + ;; + -f) # fuzzamt + shift # to get the next parameter - fuzzval + # test if parameter starts with minus sign + errorMsg="--- INVALID FUZZAMT SPECIFICATION ---" + checkMinus "$1" + fuzzamt=`expr "$1" : '\([.0-9]*\)'` + [ "$fuzzamt" = "" ] && errMsg "--- FUZZAMT=$fuzzamt MUST BE A NON-NEGATIVE FLOATING POINT VALUE (with no sign) ---" + fuzzamttest=`echo "$fuzzamt < 0" | bc` + [ $fuzzamttest -eq 1 ] && errMsg "--- FUZZAMT=$fuzzamt MUST BE A NON-NEGATIVE FLOATING POINT VALUE ---" + ;; + -b) # bcolor + shift # to get the next parameter + # test if parameter starts with minus sign + errorMsg="--- INVALID BACKGROUND COLOR SPECIFICATION ---" + checkMinus "$1" + bcolor=$1 + ;; + -g) # gcolor + shift # to get the next parameter + # test if parameter starts with minus sign + errorMsg="--- INVALID GUARD COLOR SPECIFICATION ---" + checkMinus "$1" + gcolor=$1 + ;; + -) # STDIN and end of arguments + break + ;; + -*) # any other - argument + errMsg "--- UNKNOWN OPTION ---" + ;; + *) # end of arguments + break + ;; + esac + shift # next option + done + # + # get infile and outfile + infile=$1 + outfile=$2 +fi + +# test that infile provided +[ "$infile" = "" ] && errMsg "NO INPUT FILE SPECIFIED" + +# test that outfile provided +[ "$outfile" = "" ] && errMsg "NO OUTPUT FILE SPECIFIED" + +# setup temporary files +tmpA="$dir/trimmer_$$.mpc" +tmpB="$dir/trimmer_$$.cache" +trap "rm -f $tmpA $tmpB; exit 0" 0 +trap "rm -f $tmpA $tmpB; exit 1" 1 2 3 15 + +# read the input image into the TMP cached image. +convert -quiet -regard-warnings "$infile" +repage "$tmpA" || + errMsg "--- FILE $infile NOT READABLE OR HAS ZERO SIZE ---" + + +# get sides as array +sides=`echo "$sides" | tr "[:upper:]" "[:lower:]"` +# add comma to end, then remove spaces, then change commas to spaces +sides_list=`echo "${sides}," | sed -n 's/[ ]*//gp' | tr "," " "` +sidesArray=($sides_list) +num=${#sidesArray[*]} + + +# test valid sides +i=0 +while [ $i -lt $num ]; do + side="${sidesArray[$i]}" + case "$side" in + north) ;; + east) ;; + south) ;; + west) ;; + all) ;; + *) echo "--- INVALID SIDE SPECIFIED ---" ;; + esac + i=`expr $i + 1` +done + + +# strip duplicate sides +# change space delimited list to list of one-word rows +# note \012 is octal for line break +sides_list=`echo "$sides_list" | tr " " "\012"` + +# sort list of rows to reorder and remove duplicates +sides_list=`echo $sides_list | sort -u` + +# use grep '.*' to put back into space delimited list from sorted rows +sides_list=`echo $sides_list | grep '.*'` + + +# process image if sides=all or all 4 sides provided, then exit 0 +# otherwise continute +if [ "$sides_list" = "all" -o $num -eq 4 ]; then + convert $tmpA -fuzz ${fuzzamt}% -trim +repage $outfile + exit 0 +fi + + +# change sides to "remove" to sides to "preserve" by +# deleting elements from list of all possible values +pSidesArray=(north east south west) +i=0 +while [ $i -lt $num ]; do + side="${sidesArray[$i]}" + [ "$side" = "north" ] && pnum=0 + [ "$side" = "east" ] && pnum=1 + [ "$side" = "south" ] && pnum=2 + [ "$side" = "west" ] && pnum=3 + unset pSidesArray[$pnum] + i=`expr $i + 1` +done +pnum="${#pSidesArray[*]}" + +# NOTE: must reset array from list to remove empty and non-showing elements +# produced by unset, so that the new array can be indexed properly again. +pSidesArray=(${pSidesArray[*]}) +pnum="${#pSidesArray[*]}" + + +if [ "$bcolor" = "" ]; then + # get average color of specified sides + + # get width and height and sides to be removed + ww=`identify -ping -format "%w" $tmpA` + hh=`identify -ping -format "%h" $tmpA` + [ "${sidesArray[0]}" != "" ] && side1="${sidesArray[0]}" + [ "${sidesArray[1]}" != "" ] && side2="${sidesArray[1]}" + [ "${sidesArray[2]}" != "" ] && side3="${sidesArray[2]}" + [ "${sidesArray[3]}" != "" ] && side4="${sidesArray[3]}" + + # get average colors for all four outer edges + color_north=`convert ${infile} -gravity north -crop ${ww}x1+0+0 +repage -format '%[pixel:s.p{0,0}]' info:` + color_east=`convert ${infile} -gravity east -crop 1x${hh}+0+0 +repage -format '%[pixel:s.p{0,0}]' info:` + color_south=`convert ${infile} -gravity south -crop ${ww}x1+0+0 +repage -format '%[pixel:s.p{0,0}]' info:` + color_west=`convert ${infile} -gravity west -crop 1x${hh}+0+0 +repage -format '%[pixel:s.p{0,0}]' info:` + + # compute global average from only edges (averages) to be removed + # use that for background color + if [ $num -eq 1 ]; then + eval color1="\${color_${side1}}" + bcolor="$color1" + elif [ $num -eq 2 ]; then + eval color1="\${color_${side1}}" + eval color2="\${color_${side2}}" + bcolor=`convert xc: -format "%[pixel:($color1+$color2)/2]" info:` + elif [ $num -eq 3 ]; then + eval color1="\${color_${side1}}" + eval color2="\${color_${side2}}" + eval color3="\${color_${side3}}" + bcolor=`convert xc: -format "%[pixel:($color1+$color2+$color3)/3]" info:` + elif [ $num -eq 4 ]; then + eval color1="\${color_${side1}}" + eval color2="\${color_${side2}}" + eval color3="\${color_${side3}}" + eval color4="\${color_${side4}}" + bcolor=`convert xc: -format "%[pixel:($color1+$color2+$color3+$color4)/4]" info:` + fi +fi + + +if [ "$gcolor" = "" ]; then + # get color complement (negate) to background color for possible guard color + + # note: negating the alpha channel does not work for opaque white "rgba(255,255,255,1)" + # as ncolor becomes "none" as does not make a good guard color + # so just negate ignoring the alpha channel + # that seems to work + ncolor=`convert -size 1x1 xc:"$bcolor" \ + -negate -format '%[pixel:s.p{0,0}]' info:` + + # test differnce between background color (in percent) with complement (in percent) + # and also with opaque black and opaque white + # choose color that is furtherest (largest diff) from background color + # to use as guard color + diffn=`compare -metric rmse -size 1x1 xc:"$bcolor" xc:"$ncolor" null: 2>&1 | \ + sed -n 's/^.*[(]\(.*\)[)].*$/\1/p'` + diffb=`compare -metric rmse -size 1x1 xc:"$bcolor" xc:"black" null: 2>&1 | \ + sed -n 's/^.*[(]\(.*\)[)].*$/\1/p'` + diffw=`compare -metric rmse -size 1x1 xc:"$bcolor" xc:"white" null: 2>&1 | \ + sed -n 's/^.*[(]\(.*\)[)].*$/\1/p'` + diff_max=`convert xc: -format "%[fx:max(max($diffn,$diffb),$diffw)]" info:` + if [ "$diffn" = "$diff_max" ]; then + gcolor="$ncolor" + elif [ "$diffb" = "$diff_max" ]; then + gcolor="black" + elif [ "$diffw" = "$diff_max" ]; then + gcolor="white" + fi +fi + + +# set up spice and chop amounts +i=0 +while [ $i -lt $pnum ]; do + side=${pSidesArray[$i]} + if [ "$side" = "north" ]; then + amountsArray[$i]="0x1+0+0" + elif [ "$side" = "south" ]; then + amountsArray[$i]="0x1+0+0" + elif [ "$side" = "west" ]; then + amountsArray[$i]="1x0+0+0" + elif [ "$side" = "east" ]; then + amountsArray[$i]="1x0+0+0" + fi + i=`expr $i + 1` +done + + +# set up command sequences +# splice +splicer="" +i=0 +while [ $i -lt $pnum ]; do + splicer="$splicer -gravity ${pSidesArray[$i]} -splice ${amountsArray[$i]}" + i=`expr $i + 1` +done + + +# composite +composer="" +i=0 +while [ $i -lt $pnum ]; do + composer="$composer -size 1x1 xc:$gcolor -gravity ${pSidesArray[$i]} -composite" + i=`expr $i + 1` +done + + +# chop +chopper="" +i=0 +while [ $i -lt $pnum ]; do + chopper="$chopper -gravity ${pSidesArray[$i]} -chop ${amountsArray[$i]}" + i=`expr $i + 1` +done + + +# do trimming +convert $tmpA \ + -background "$bcolor" $splicer \ + $composer \ + -fuzz ${fuzzamt}% -trim +repage \ + $chopper \ + $outfile + +exit 0 |