How to detect if video file is pure static? - image-processing

I have a collection of analog video recordings. About 10% of the files are entirely static. How could I programmatically look at all files and delete the files that contain mostly static?
The following utilities have command line options to analyze video, however none have built in functionality to detect the absence of video content.
ffmpeg
ffprobe
HandBrake
I've tried using ffmpeg to export still images and then use image magick to compare the difference between those images. Unfortunately, the difference between an image of static, and actual video content returns nearly the same difference percentage. (9% vs 7%)
ffmpeg -ss 00:30 -i PICT0050.AVI -vframes 1 -q:v 2 output1.jpg
magick compare -metric PSNR output1.jpg output2.jpg diff.jpg
9.2191
magick compare -metric PSNR output1.jpg output3.jpg diff.jpg
7.70127
Comparing sample 1 with sample 2 results in 9% difference
Comparing sample 1 with sample 3 results in 7% difference
Sample 1
Sample 2
Sample 3
Sample 4
Sample 5
Sample 6
Sample 7

It looks like the static images are black and white without any colour - so I would look at the mean saturation and if it is low/zero, I would assume they are static. So, in bash and assuming your images are named sampleXXX.jpg:
for f in sample*jpg; do
convert "$f" -colorspace HSL -channel S -separate -format '%M avg sat=%[fx:int(mean*100)]\n' info:
done
Sample Output
sample1.jpg avg sat=0
sample2.jpg avg sat=0
sample3.jpg avg sat=21
sample4.jpg avg sat=0
sample5.jpg avg sat=39
sample6.jpg avg sat=31
which suggests that samples 1,2 and 4 are static.

Another way is to look at the amount of edges using Imagemagick to rank the amount of noise. The noise images will have more edges.
for img in *; do
edginess=`convert $img -edge 1 -scale 1x1! -format "%[fx:mean]" info:`
echo "$img $edginess"
done
1bidV.jpg 0.0472165
3FJUJ.jpg 0.275502 <---- noise image
QpQvA.jpg 0.332296 <---- noise image
b4Gxy.jpg 0.0428422
gQcXP.jpg 0.0437578
vF1YZ.jpg 0.322911 <---- noise image

Related

ImageMagick: Divide AE distortion by total pixels in fx output info format

I am trying to use ImageMagick 7 to detect if a specific channel in an image is largely pure black and pure white (plus a little antialiasing, and there's a chance the image could be pure black). This is to distinguish from another kind of image that shares a naming convention but has photographic-like image data in the r/g/b channels.
(Basically both image types are specular maps from different engines. The one I'm trying to differentiate here is more modern and has the metallic map in the blue channel; the other is much older and just has the specular colour in the RGB channels and the gloss map in the alpha.)
Currently I'm comparing the channel to a clone of itself that has had a 50% threshold applied, using the AE metric to see if it's largely the same apart from a small amount of antialiasing, and a fuzz of 1% to account for occasional aberration from pure black/white. This command works, but of course at the moment it only returns the number of distorted pixels:
magick ( "file.png" -channel b -separate ) ^
( +clone -channel b -separate -threshold 50% ) ^
-fuzz 1% -metric AE -compare ^
-format "%[distortion]" info:
Because the input image sizes will vary, I want to divide the distortion by the total number of pixels in the image to get the relative amount of the image that's not pure black/white -- under 10% has seemed good so far in my manual testing -- but I can't get the format syntax right. Everything I've tried -- for example "%[fx:%[distortion]/w*h]" -- has given the magick: undefined variable `[distortion]' # error/fx.c/FxGetSymbol/1169 error.
What syntax should I use? (And if there's a better way to do what I'm doing, I always appreciate it!)
I believe the following is what you want in Imagemagick. Basically you save the distortion in -set option: argument and then use it in -fx later.
However, +clone gives you just the b channel, so there should be no need for -channel b -separate in your second line.
magick ( "file.png" -channel b -separate ) ^
( +clone -threshold 50% ) ^
-fuzz 1% -metric AE -compare ^
-set option:distort "%[distortion]" ^
-format "%[fx:distort/(w*h)]" info:
Fred (#fmw42) has already provided an excellent method. There is another method for differentiating pure black and white images from greyscale images with a fuller tonal scale which may interest you. Credit to Anthony Thyssen for the technique described here.
If you use -solarize 50% in ImageMagick it inverts all the highlights, so it effectively folds your histogram in half and all the whites become pure black and all the near-whites become near blacks. The command looks like this:
magick INPUT -solarize 50% OUTPUT
So, if I apply that to a couple of input images - the first one pure black and near white, the second a greyscale - and show the corresponding output image on the right you'll see the effect:
If you now inspect the mean and standard deviation of the two solarised images:
magick {a,b}-sol.jpg -format "%f, mean: %[mean], stdev: %[standard-deviation]\n" info:
a-sol.jpg, mean: 2328.91, stdev: 3175.67
b-sol.jpg, mean: 16319.5, stdev: 9496.04
you can see that the mean and standard deviation of the first (pure black and white) image is low because all the bright whites have folded to near blacks, whereas the mean and standard deviation of the greyscale image are both higher because the tones are more spread out.

How to detect frames to keep or discard based on luminosity levels?

I have a series of images from a slow motion capture of pulsing electrical discharges. Many of the frames are nearly black. I would like to selectively keep the frames that are more interesting; eg have more luminosity.
I've considered using ImageMagick or GraphicsMagick (or any other; not married to any tool - I'm up for more efficient suggestions).
How would I go about selecting such images and then discarding the other images without appreciable luminosity levels? I'm assuming that I have to establish a baseline first of "black" and then perhaps visually find the least luminous frame image and then use that as the lower limit to use for getting meaningful images / frames...
Example of DISCARD ("empty" frame):
Example of KEEP (frame with "data"):
I would suggest ImageMagick to Erode the image (clean-up noise), reduce data to a monochrome binary image, and print the statistical mean of the image.
convert 5HzsV.jpg -format "%[mean]" -monochrome -morphology Erode Diamond info:
# => 0
convert lLZFX.jpg -format "%[mean]" -monochrome -morphology Erode Diamond info:
# => 149.992
So a bash script might be as easy as...
for image in $(ls *.jpg)
do
L=$(convert "$image" -format "%[mean]" -monochrome -morphology Erode Diamond info:)
if [[ $L -gt 0 ]]; then
echo "Image $image is not empty! # $L"
fi
done
Of course that can be adjusted to meet your needs.
The way images are encoded, you'll likely find that the 'interesting' images are bigger, because the uniformly dark background compresses better than a random spark. For instance, your empty Jpeg is 21K while the interesting one is 39K.

Compare two images and get the percent difference

I known some ways to compare 2 images with ImageMagick or OpenCV
Using Objective-C, is there any way to compare two images and get a % difference value returned?
http://www.imagemagick.org/Usage/compare/#methods
How can I quantify difference between two images?
Image comparison - fast algorithm.
But for my case, I also have the same character with different position.
Image1:
Image2:
or
or
So, what should I do now to find the % difference value between Image1 and Image2?
This actually answers your question - which doesn't in fact ask anything about images 3 and 4 - but I fear it will not help you much.
As #GPPK suggests, you need to trim the extraneous material off around your kanji characters, which you can do with the -trim command in ImageMagick. I have added a thin red border so you can see where the edges are:
convert kanji2.png -trim kanji2-t.png
If you want do that to images 1 and 2, and then compare them, you can do that all in one go like this:
convert -metric ae kanji1.png kanji2.png -trim -compare -format "%[distortion]" info:
0
which shows there are zero pixels different in the resulting images if you trim kanji1 and kanji2.
If you compare the trimmed kanji1 and kanji3 like this, you get:
convert -metric AE kanji1.png kanji3.png -trim -compare -format "%[distortion]" info:
893184
which indicates 900,000 pixels of 5,000,000 are different.
Likewise, if you compare kanji1 and kanji4:
convert -metric AE kanji1.png kanji4.png -trim -compare -format "%[distortion]" info:
1.14526e+06
or 1.1 million of 5 million.
But this doesn't help when your images are a different size (scale), or rotated.
You could scale your images to a normalised size before comparing, and I guess that might help you become a bit more "scale invariant":
convert -metric AE kanji1.png kanji4.png -trim -scale 1000x1000! -compare -format "%[distortion]" info:
You could also rotate your images using a little iterative procedure that rotates the images through say +/- 20 degrees and chooses the one with the smallest trimmed bounding box to become a little more "orientation invariant". But then you will still have a problem if the characters are sheared, or fatter, or thinner, or brighter, or darker, or contrastier... I think you need to look into "Template Matching".

How can I reduce the dimensions of gif images with ImageMagick?

What flag in magick.exe convert <flags> will reduce the dimensions of my image?
This is the command I'm using: magick.exe input.png[0] -depth 8 -type Grayscale -dresize 400x300.
The [0] after the source-image filename is meant to strip the first frame of any animated gifs.
How can I do reduce my output gif sizes? My file sizes are too large: my outputs need to be less than 100k. Any methods other than reducing the dimensions are also welcome.
convert test.gif -fuzz 10% -layers Optimize result.gif
Adding a -fuzz 2% produced a better optimization, but still not very good. At -fuzz 15% It isolated the differences for frame optimization to just the visible color band changes I noted before.
At 25% the differences were almost to just the text changes.
Finally at a massive 30% fuzz factor (ignore color changes below that figure, did it optimize to just the text changes.
You can try gifsicle:
gifsicle -O3 old.gif -o new.gif
If it's an animation, you could try skipping frames (see how it works).
magick.exe convert -resize 100x100 .\step1.jpg .\step2.jpg -delay 100 -loop 0 animation.gif
Thanks to Nate.
//IMPORTANT: -resize should come first.
//BY DEFAULT: the aspect ratio will be locked with the longest dimension being set to 100px.
//GENERALIZATION: this order `magick convert 1st<input_file(s)> 2nd<switch(es)> 3rd<output_file>`.
//IMPORTANT-RELATED: `-delay` will not work on second brush! Use `-set delay` for existing files.
Please, also be sure to refresh your folder view to ensure that you're not viewing old output files: Windows Explorer (F5).
Try to use the option:
-type Palette
It might help to reduce your gif file sizes smaller, oh and I believe -depth 8 can only be used for png images.

Recommendation for compressing JPG files with ImageMagick

I want to compress a JPG image file with ImageMagick but can't get much difference in size. By default the output size is bigger than the input. I don't know why, but after adding some +profile options and setting down the quality I can get an smaller size but still similar to original.
The input image is 255kb, the processed image is 264kb (using +profile to remove profiles and setting quality to 70%). Is there any way to compress that image to 150kb at least? Is that possible? What ImageMagick options can I use?
I use always:
quality in 85
progressive (comprobed compression)
a very tiny gausssian blur to optimize the size (0.05 or 0.5 of radius) depends on the quality and size of the picture, this notably optimizes the size of the jpeg.
Strip any comment or EXIF metadata
in imagemagick should be
convert -strip -interlace Plane -gaussian-blur 0.05 -quality 85% source.jpg result.jpg
or in the newer version:
magick source.jpg -strip -interlace Plane -gaussian-blur 0.05 -quality 85% result.jpg
Source.
From #Fordi in the comments (Don't forget to upvote him if you like this):
If you dislike blurring, use -sampling-factor 4:2:0 instead. What this does is reduce the chroma channel's resolution to half, without messing with the luminance resolution that your eyes latch onto. If you want better fidelity in the conversion, you can get a slight improvement without an increase in filesize by specifying -define jpeg:dct-method=float - that is, use the more accurate floating point discrete cosine transform, rather than the default fast integer version.
I'm using the Google Pagespeed Insights image optimization guidelines, and for ImageMagick they recommend the following:
-sampling-factor 4:2:0
-strip
-quality 85 [it can vary, I use range 60-80, lower number here means smaller file]
-interlace
-colorspace RGB
Command in ImageMagick:
convert image.jpg -sampling-factor 4:2:0 -strip -quality 85 -interlace JPEG -colorspace RGB image_converted.jpg
With these options I get up to 40% savings in JPEG size without much visible loss.
Just saying for those who using Imagick class in PHP:
$im -> gaussianBlurImage(0.8, 10); //blur
$im -> setImageCompressionQuality(85); //set compress quality to 85
Once I needed to resize photos from camera for developing:
Original filesize: 2800 kB
Resolution: 3264x2448
Command:
mogrify -quality "97%" -resize 2048x2048 -filter Lanczos -interlace Plane -gaussian-blur 0.05
Result filesize 753 kB
Resolution 2048x2048
and I can't see any changes in full screen with my 1920x1080 resolution monitor. 2048 resolution is the best for developing 10 cm photos at maximum quality of 360 dpi. I don't want to strip it.
edit: I noticed that I even get much better results without blurring. Without blurring filesize is 50% of original, but quality is better (when zooming).
#JavisPerez -- Is there any way to compress that image to 150kb at least? Is that
possible? What ImageMagick options can I use?
See the following links where there is an option in ImageMagick to specify the desired output file size for writing to JPG files.
http://www.imagemagick.org/Usage/formats/#jpg_write
http://www.imagemagick.org/script/command-line-options.php#define
-define jpeg:extent={size}
As of IM v6.5.8-2 you can specify a maximum output filesize for the JPEG image. The size is specified with a suffix. For example "400kb".
convert image.jpg -define jpeg:extent=150kb result.jpg
You will lose some quality by decompressing and recompressing in addition to any loss due to lowering -quality value from the input.
I would add an useful side note and a general suggestion to minimize JPG and PNG.
First of all, ImageMagick reads (or better "guess"...) the input jpeg compression level and so if you don't add -quality NN at all, the output should use the same level as input. Sometimes could be an important feature. Otherwise the default level is -quality 92 (see www.imagemagick.org)
The suggestion is about a really awesome free tool ImageOptim, also for batch process.
You can get smaller jpgs (and pngs as well, especially after the use of the free ImageAlpha [not batch process] or the free Pngyu if you need batch process).
Not only, these tools are for Mac and Win and as Command Line (I suggest installing using Brew and then searching in Brew formulas).
I added -adaptive-resize 60% to the suggested command, but with -quality 60%.
convert -strip -interlace Plane -gaussian-blur 0.05 -quality 60% -adaptive-resize 60% img_original.jpg img_resize.jpg
These were my results
img_original.jpg = 13,913KB
img_resized.jpg = 845KB
I'm not sure if that conversion destroys my image too much, but I honestly didn't think my conversion looked like crap. It was a wide angle panorama and I didn't care for meticulous obstruction.
Here's a complete solution for those using Imagick in PHP:
$im = new \Imagick($filePath);
$im->setImageCompression(\Imagick::COMPRESSION_JPEG);
$im->setImageCompressionQuality(85);
$im->stripImage();
$im->setInterlaceScheme(\Imagick::INTERLACE_PLANE);
// Try between 0 or 5 radius. If you find radius of 5
// produces too blurry pictures decrease to 0 until you
// find a good balance between size and quality.
$im->gaussianBlurImage(0.05, 5);
// Include this part if you also want to specify a maximum size for the images
$size = $im->getImageGeometry();
$maxWidth = 1920;
$maxHeight = 1080;
// ----------
// | |
// ----------
if($size['width'] >= $size['height']){
if($size['width'] > $maxWidth){
$im->resizeImage($maxWidth, 0, \Imagick::FILTER_LANCZOS, 1);
}
}
// ------
// | |
// | |
// | |
// | |
// ------
else{
if($size['height'] > $maxHeight){
$im->resizeImage(0, $maxHeight, \Imagick::FILTER_LANCZOS, 1);
}
}
Did some experimenting myself here and boy does that Gaussian blur make a nice different. The final command I used was:
mogrify * -sampling-factor 4:2:0 -strip -quality 88 -interlace Plane -define jpeg:dct-method=float -colorspace RGB -gaussian-blur 0.05
Without the Gaussian blur at 0.05 it was around 261kb, with it it was around 171KB for the image I was testing on. The visual difference on a 1440p monitor with a large complex image is not noticeable until you zoom way way in.
An very old but helpful answer.
I need to say, to serious large photography, -gaussian-blur is not acceptable, rather than compress ratio.
Comparing below, %95 with -gaussian-blur 0.05 vs. %85 without blurring. Original 17.5MB (8MP with much defail), %95 without blurring 5MB, %85 without blurring 3036KB, %95 with blurring 3365KB.
Comparing between blurring and compress ratio
Maybe lower blurring like 0.02 will work better.
If the image has big dimenssions is hard to get good results without resizing, below is a 60 percent resizing which for most of the purposes doesn't destroys too much of the image.
I use this with good result for gray-scale images (I convert from PNG):
ls ./*.png | xargs -L1 -I {} convert {} -strip -interlace JPEG -sampling-factor 4:2:0 -adaptive-resize 60% -gaussian-blur 0.05 -colorspace Gray -quality 20 {}.jpg
I use this for scanned B&W pages get them to gray-scale images (the extra arguments cleans shadows from previous pages):
ls ./*.png | xargs -L1 -I {} convert {} -strip -interlace JPEG -sampling-factor 4:2:0 -adaptive-resize 60% -gaussian-blur 0.05 -colorspace Gray -quality 20 -density 300 -fill white -fuzz 40% +opaque "#000000" -density 300 {}.jpg
I use this for color images:
ls ./*.png | xargs -L1 -I {} convert {} -strip -interlace JPEG -sampling-factor 4:2:0 -adaptive-resize 60% -gaussian-blur 0.05 -colorspace RGB -quality 20 {}.jpg

Resources