Wie klassifiziere ich Bilder mit ImageMagick? identify -verbose?

Name

imid.pl

Aufgabe

(English summary below) Beispiel, wie sich ImageMagicks? "identify" mit der "-verbose"-Option aus Perl aufrufen und die Ausgabe auswerten laesst. Das Skript versucht Bilddateien nach folgenden Kriterien zu klassifizieren:
  • Farbtyp (Palette oder linear)
  • Farbmodell (Gray, RGB, CMYK)
  • Transparenz (wenn ja: Tiefe des Alphakanals in Bits)
  • Animated (ja, nein)
  • Zahl der Farben: wenn Palette, absolute Zahl; wenn linear, Tiefe in Bits (z.B. 24 oder 32)

Ich finde zwei Elemente des -verbose-Protokolls widerspruechlich:
  1. Bilder koennen gleichzeitig als "Class: DirectClass?" und "Type: Palette" markiert werden. Ersteres steht ja fuer "lineare Farben", zweiteres fuer eine Palette à la GIF.
  2. Bilder koennen als "Typ ohne Matte" markiert sein, aber trotzdem einen Alphakanal haben. Aber Transparenz erfordert doch eine Matte -- oder???
Das Skript versucht, diese Widersprueche aufzuloesen. Fuer Rueckmeldungen waer ich dankbar!

(English speakers: A script demonstrating the extraction of image data from ImageMagick?'s identify -verbose. You'll find a short version of the above discussion at the head of the script.)

Aufruf mit Parametern

perl ./imid.pl # will process all images in the current directory

Skript

#!/usr/bin/perl
# Experimental Perlscript to classify Images, using "identify -verbose",
# into
# - animated vs. still
# - palette vs. linear colours
# - gray vs. rgb vs. cmyk
# - # of colors per palette or total colour resoution in bits
# - opaque vs. transparent (plus # of transparency bits)
#
# Usage:
# Call from any image directory without arguments, will analyze all images
# in the directory and report to STDOUT. 
# Will not write or alter files.
#
# johannes 10/2006

# get list of image files and loop over it
@images = grep /\.(png|gif|jpg|tif|bmp|psd|xcf)/,`ls ./*`;
foreach (@images) {
    chomp;

    @id = identify($_); # identify -verbose $_
    %img = classify($_,@id);
    pretty(%img);
}

1;

sub pretty {
    my %img = @_;
    print substr($img{'name'}."--------------------------------------------------",0,50),"\n";
    print "Animated: $img{animated}   Type: $img{type}   Transparency, bits: $img{transparent}\n";
    print "Color model: $img{model}   Color depth, ".($img{type} eq 'linear'?'bits':'size').": $img{colors}\n";
    print "\n";
}

sub classify {
    my ($filename,@id) = @_;
    my %res;

    $res{'name'} = $filename;

    # parse id output and gather necessary data
    my $tagclass;
    my $tagtype;
    my %channels;
    my $tagcolors;
    my $i; my $state=''; for ($i=1; exists($id[$i]); $i++) {
        # lines preceding Image: tag
        # animated gifs have more than one lines preceding the "Image:" line
        if ($state eq '') {
            if ($id[$i] =~ /^Image:/) {
                $res{'animated'} = '';
                $state = 'waitChannels';
            }
            else {
                $res{'animated'} += 1; # every line is one frame of a movie
            }
        }
        else { # states waitChannels, inChannels, waitEnd
            # color depth per channel: look for it immediately after "Channel depth: " line
            if ($state eq 'inChannels') {
               if ($id[$i] =~ /^\s*(Gray|Red|Green|Blue|Cyan|Magenta|Yellow|Black|Alpha): (\d+)/i) {
                    $channels{lc($1)} = $2;
                    next;
                }
                else {
                    $state = 'waitEnd';
                }
            }
            # image format: any string composed of non-blanks and non-brackets
            # (typically, the line looks line "Format: PNG (Portable Network Graphics)")
            if    ($id[$i] =~ /^\s*Format: ([^ (]+)/) { $res{'format'} = $1 }
            elsif ($id[$i] =~ /^\s*Geometry: (\d+)x(\d+)/) { $res{'width'} = $1; $res{'height'} = $2; }
            elsif ($id[$i] =~ /^\s*Class: /) { $tagclass = $' }
            elsif ($id[$i] =~ /^\s*Type: /) { $tagtype = $' }
            elsif ($id[$i] =~ /^\s*Colors: /) { $tagcolors = $' }
            elsif ($state eq 'waitChannels') {
                ($id[$i] =~ /^\s*Channel depth:/) && ($state = 'inChannels');
            }
            elsif ($state eq 'waitEnd') {
                ($id[$i] =~ /^Image: /) and last;
            }
        }
    }

    # the color model is taken from the following, mutually dependent attributes:
    # 
    # Attribute Name    Possible Values             Meaning
    # --------------    ---------------             -------
    # Class:            PseudoClass                 Image has a color palette
    #                   DirectClass                 Linear colors, no color palette*
    # Type:             Grayscale                   No transparency, usually 8 bits
    #                   GrayscaleMatte              Has transparency, usually 8 bits
    #                   TrueColor                   May have transparency
    #                   TrueColorMatte              May have transparency
    #                   Palette                     May be 24Bt (seen with BMP, TIF) or gray
    #                   PaletteMatte                
    #                   ColorSeparation             CMYK, no transparency???
    # *For DirectClass Images. "Channel depth" has valid info on # of channels, transparency
    #
    # Colorspace:       (ignore)                    (look at channels instead)
    # Channel depth:    
    #    Gray:          1-bits, 8-bits, 16-bits     (for linear images, add channel
    #    Red:                                       depths to get the total colour
    #    Green:                                     resolution, i.e., 8, 24 or 32 bits.
    #    Blue:                                      
    #    Cyan:                                      
    #    Magenta:                                   For linear color images, the 
    #    Yellow:                                    Alpha: depth seems to be the only
    #    Black:                                     secure indicator for presence
    #    Alpha:                                     or absence of transparency.)
    #                   
    # Colors:           Any                         # of colors for palette images
    if ($tagclass =~ /Pseudo/) {
        $res{'type'} = 'palette';
        # gray / rgb? look at Channel hash, not Type: (Type: Palette may be gray)
        ($channels{'gray'}) && ($res{'model'} = 'gray') || ($res{'model'} = 'rgb');
        ($tagtype =~ /Matte/) && ($res{'transparent'} = 1) || ($res{'transparent'} = '');
        $res{'colors'} = 'i'.$tagcolors;
    }
    else {
        $res{'type'} = 'linear';
        if ($channels{'gray'}) {
            $res{'model'} = 'gray';
            $res{'colors'} = $channels{'gray'};
        }
        elsif ($channels{'cyan'} or $channels{'magenta'} or
               $channels{'yellow'} or $channels{'black'}) {
            $res{'model'} = 'cmyk';
            $res{'colors'} = $channels{'cyan'}+$channels{'magenta'}+
                $channels{'yellow'}+$channels{'black'};
        }
        else { # default: rgb
            $res{'model'} = 'rgb';
            $res{'colors'} = $channels{'red'}+$channels{'green'}+$channels{'blue'};
        }
        $res{'transparent'} = ($channels{'alpha'}>0?$channels{'alpha'}:'');
    }

    return %res;
}

sub identify {
    my $imgname = shift;

    # call imagemagick identify with verbose option , return its result
    return `identify -verbose $imgname`;

    # for testing, i used precalculated identify protocols
    # open INF,"<$imgname";
    # my @id = <INF>;
    # close INF;
    # return @id;
}

Ergänzungen, Kommentare

Kommentare werden am besten in folgender Form vorgenommen, damit sie im Inhaltsverzeichnis angezeigt werden (natürlich ohne das <verbatim>):
---### Main.??? - 14 Jul 2003 - Betreff

UtilPerlSkripteSubForm edit

Titel Wie klassifiziere ich Bilder mit ImageMagick? identify -verbose?
Autor HasenJon?
Bereich PerlSkripteDateien
Topic revision: 2006-10-13, ReneeBaecker
 
Bitte die NutzungsBedingungen beachten.
Bei Vorschlägen, Anfragen oder Problemen mit dem PerlCommunityWiki bitten wir um WebBottomBarExample">Rückmeldung.