bashvideo

Detect if video is black and white in bash


I have a folder with hundreds of films, and I'd like to separate the color ones from black and white. Is there a bash command to do this for general video files?

I already extract a frame:

ffmpeg -ss 00:15:00 -i vid.mp4 -t 1 -r 1/1 image.bmp

How can I check if the image has a color component?


Solution

  • I never found out why video processing questions are answered on SO but as they typically are not closed, i'll do my best... As this is a developer board, i cannot recommend any ready commandline tool to use for your bash command, nor do i know any. Also i cannot give a bash only solution because i do not know how to process binary data in bash.

    To get out if an image is grey or not, you'll need to check each pixel for it's color and "guess" if it is kind of grey. As others say in the comments, you will need to analyze multiple pictures of each video to get a more accurete result. For this you could possibly use the scene change detection filter of ffmpeg but thats another topic.

    I'd start by resizing the image to save processing power, e.g. to 4x4 pixels. Also make sure you guarantee the colorspace or better pix_format is known so you know what a pixel looks like.

    Using this ffmpeg line, you would extract one frame in 4x4 pixels to raw RGB24:

    ffmpeg -i D:\smpte.mxf -pix_fmt rgb24 -t 1 -r 1/1 -vf scale=4:4 -f rawvideo d:\out_color.raw
    

    The resulting file contains exactly 48 bytes, 16 pixels each 3 bytes, representing R,G,B color. To check if all pixels are gray, you need to compare the difference between R G and B. Typically R G and B have the same value when they are gray, but in reality you will need to allow some more fuzzy matching, e.g. if all values are the same +-10.

    Some example perl code:

    use strict;
    use warnings;
    
    my $fuzz = 10;
    my $inputfile ="d:\\out_grey.raw";
    die "input file is not an RGB24 raw picture." if ( (-s $inputfile) %3 != 0);
    open (my $fh,$inputfile);
    binmode $fh;
    
    my $colordetected = 0;
    
    for (my $i=0;$i< -s $inputfile;$i+=3){
        my ($R,$G,$B);
        read ($fh,$R,1);
        $R = ord($R);
        read ($fh,$B,1);
        $B = ord($B);
        read ($fh,$G,1);
        $G = ord($G);
         if ( $R >= $B-$fuzz  and  $R <= $B+$fuzz and  $B >= $G-$fuzz and  $B <= $G+$fuzz )  {
           #this pixel seems gray
         }else{
            $colordetected ++,
         } 
    }
    
    if ($colordetected != 0){
        print "There seem to be colors in this image"
    }