One of the things mentioned was about "linear scale".
kazuo wrote:Thanks for all the options. I'm trying to classify scanned manga/comics images on grayscale and color. The grayscale can be "linear scale" from yellowed/not balanced scans.
This is not pure grayscale, but a image in which the colors all fall in a linear sequence, say from a off-yellow color to a dark bluish color, as an example.
I did some work on this.
http://www.imagemagick.org/Usage/compare/#type_linear
The script is a perl script that takes the 9 colors from a 3x3 matrix average colors of an image by area, and uses 3D vector mathematics to determine how far the nine colors are from a 'best fit' line through all the colors. The script wasn't long but it was very vector orientated.
Best fit line..
- find average of all 9 colors -- the line goes through this
- Get the average direction of all colors from this point (in a average a positive direction).
- Make vector from the average center a unit vector.
Compare all points along with that line
- vector dot product -> distance along that line
- vector cross product -> distance perpendicular to the line -> error
If the error is less than a threshold, colors in image follow a linear scale, or basically colored greyscale.
NOTE perl required the Math::VectorReal package from CPAN (ASIDE: I wrote it!)
The input is a "cmatrix" file with a list of 9 colors (see IM Examples link above)
Code: Select all
#!/usr/bin/perl
=head1 NAME
image_is_linear -- Determine is a image is a linear color line drawing
=head1 SYNOPSIS
image_is_linear [options] threshold [metric_list...]
Options:
-v Verbose results on STDERR, (give just one image)
-t Return the linear error, (how linear a image is) instead
-l only pass linear color image metrics (modified) (>threshold)
-c only pass non-linear image metrics unchanged (<=threshold)
=head1 DESCRIPTION
Given an array of color metrics extracted from an image, determine if all
those metrics are linear to each other (within a given color threshold of the
optimal linear line). A image with linear color metrics, is throuht to be a
line drawing, rather than color image.
The metrics of a linear image is replaced with a new metric giving the
distances along that line, from its closest approach to the origin (black).
If the metric file list contains a list with the special flag '---' it is just
passed on, for later use by the comparision function.
=head1 AUTHOR
Anthony Thyssen 16 June 2007 A.Thyssen_AT_griffith.edu.au
=cut
# -----------------------------------------------------------------------
use strict;
use File::Basename;
use Math::VectorReal qw(:all);
select((select(STDOUT), $| = 1)[0]);
select((select(STDERR), $| = 1)[0]);
sub Usage {
use Pod::Usage;
pod2usage("@_");
exit 10;
}
my( $verbose, $linear_error, $linear_metrics, $color_metrics);
ARGUMENT: # Multi-switch option handling
while( @ARGV && $ARGV[0] =~ s/^-(?=.)// ) {
$_ = shift; {
m/^$/ && do { next }; # next argument
m/^-$/ && do { last }; # End of options
m/^\?/ && do { Usage }; # Usage Help
s/^v// && do { $verbose++; redo }; # more verbose results
s/^t// && do { $linear_error++; redo }; # return the linear 'error'
s/^l// && do { $linear_metrics++; redo }; # pass only linear color metrics
s/^c// && do { $color_metrics++; redo }; # pass non-linear color metrics
Usage( "Unknown Option \"-$_\"\n" );
} continue { next ARGUMENT }; last ARGUMENT;
}
# color distance from the 'linear line'
# Note in RGB space, Black to Primary = 65,536.00
# Black to White = 113,511.68
# Smallest 'Color' image seen = 800 (Approx - smaller definate linear)
# More Typical Grey/Color Cutoff = 1000
# Largest Greyscale-like threshold = 1336 (grey with red parts)
# Largest non-linear threshold seen = 18645 (lots of extreme colors areas)
my $threshold = 200000; # no threshold, convert metrics (default)
$threshold = 1500 if $linear_metrics;
$threshold = 800 if $color_metrics;
$threshold = 1000 if $linear_metrics && $color_metrics; # both!!!
$threshold = shift if $ARGV[0] =~ /^\d+$/; # user selected
$linear_metrics = 1 unless $color_metrics; # pass linear metrics by default.
# ----------------------
my $num = 9; # number of RGB vectors per image (color matrix only)
my $G = (X+Y+Z)->norm; # normalized grey vector.
while (<>) {
s/#.*//; s/\s+$//;
my( $file, @metrics ) = split;
next unless $file;
if ( $file eq '---' ) { # just pass on special comparision flags
print "$file\n";
next;
}
#print "$file @metrics\n";
my @colors; # the color vector metrics
push (@colors, vector( shift @metrics, shift @metrics, shift @metrics) )
while @metrics;
#$Math::VectorReal::FORMAT = "\n\t%+8.1f %+8.1f %+8.1f"; # debug output
#print "$file @colors\n";
$num = @colors unless $num;
if ( $num != @colors ) {
die( "Invalid number of Metrics for \"$file\"\n$_\n" );
}
# get the best linear vector for the given colors
my($c_origin, $c_vector) = linear_vector(@colors);
if ( $verbose ) {
print STDERR "$file\n";
$Math::VectorReal::FORMAT = "[ %+8.6f %+8.6f %+8.6f ]";
print STDERR " Color Vector $c_vector\n";
$Math::VectorReal::FORMAT = "[ %+8.1f %+8.1f %+8.1f ]";
print STDERR " Color Origin $c_origin\n";
printf STDERR " Origin %.1f from line\n", $c_origin->length;
printf STDERR " Color %.1f%% Greyscale\n", ($c_vector . $G) * 100;
}
# Find how non-linear each color metric is...
my @linear; # new metrics for a linear image.
my $error = 0; # how different image is from linearity
#my $max = 0; # the largest 'non-linear' distance.
#my $max_i = 0; # index of the least linear color
for ( my $i=0; $i<$num; $i++ ) {
my $v = $colors[$i] - $c_origin;
my $d = $v.$c_vector; # distance along line from origin
my $x = ($v x $c_vector)->length; # distance away from line
push @linear, $d; # new linear metric
printf STDERR "\t$i : %.4f %.4f\n", $d, $x if $verbose;
$error += $x*$x; # squared error of difference
#$max = $x, $max_i = $i if $x > $max; # find largest non-linear distance
}
$error = sqrt($error/$num); # normalize the error back to RGB space
if ( $linear_error ) {
print int($error), " $file\n"; # just outputing 'linear' error values
}
elsif ( $error <= $threshold ) {
print join(" ", $file, map {int($_+.5)} @linear), "\n" if $linear_metrics;
}
else {
$Math::VectorReal::FORMAT = "%d %d %d";
print "$file @colors\n" if $color_metrics;
}
}
sub linear_vector {
my $avg = O; # The average color of the given colors...
# The color vector will go though this point.
for ( my $i=0; $i<@_; $i++ ) {
$avg += $_[$i];
}
$avg /= scalar(@_);
my $vec = O; # the average direction of color differences
for ( my $i=0; $i<@_; $i++ ) {
my $v = $_[$i]-$avg;
$v = -$v if $v.$avg < 0; # make all directions positive
$vec += $v;
}
$vec = $vec->norm;
# Find the closest position of this vector to the origin.
my $org = $avg - ($avg.$vec)*$vec;
return( $org, $vec );
}