Capture full-range/lossless rgb frame from capture card that supports NV12 and YUYV output - image-processing

I am trying to make a program which captures an image, then i need to compare captured image and the input data which i displayed, both should matc pixel by pixel
Here are the details of my capture card
$ v4l2-ctl --list-formats-ext -d /dev/video0
ioctl: VIDIOC_ENUM_FMT
Type: Video Capture
[0]: 'NV12' (Y/CbCr 4:2:0)
Size: Discrete 3840x2160
Interval: Discrete 0.033s (30.000 fps)
Size: Discrete 2560x1440
Interval: Discrete 0.017s (60.000 fps)
Size: Discrete 1920x1080
Interval: Discrete 0.017s (60.000 fps)
Size: Discrete 1280x720
Interval: Discrete 0.017s (60.000 fps)
Size: Discrete 640x480
Interval: Discrete 0.017s (60.000 fps)
[1]: 'YUYV' (YUYV 4:2:2)
Size: Discrete 2560x1440
Interval: Discrete 0.020s (50.000 fps)
Size: Discrete 1920x1080
Interval: Discrete 0.017s (60.000 fps)
Size: Discrete 1280x720
Interval: Discrete 0.017s (60.000 fps)
Size: Discrete 640x480
Interval: Discrete 0.017s (60.000 fps)
[2]: '' (30313050-0000-0010-8000-00aa003)
[3]: '' (e436eb7e-524f-11ce-9f53-0020af0)
$ v4l2-ctl --all
Driver Info:
Driver name : uvcvideo
Card type : ITE HDMI 4K+ Bridge: ITE HDMI 4
Bus info : usb-0000:00:14.0-6
Driver version : 5.18.0
Capabilities : 0x84a00001
Video Capture
Metadata Capture
Streaming
Extended Pix Format
Device Capabilities
Device Caps : 0x04200001
Video Capture
Streaming
Extended Pix Format
Priority: 2
Video input : 0 (Camera 1: ok)
Format Video Capture:
Width/Height : 1920/1080
Pixel Format : 'YUYV' (YUYV 4:2:2)
Field : None
Bytes per Line : 3840
Size Image : 4147200
Colorspace : sRGB
Transfer Function : Rec. 709
YCbCr/HSV Encoding: Rec. 709
Quantization : Default (maps to Limited Range)
Flags :
Crop Capability Video Capture:
Bounds : Left 0, Top 0, Width 1920, Height 1080
Default : Left 0, Top 0, Width 1920, Height 1080
Pixel Aspect: 1/1
Selection Video Capture: crop_default, Left 0, Top 0, Width 1920, Height 1080, Flags:
Selection Video Capture: crop_bounds, Left 0, Top 0, Width 1920, Height 1080, Flags:
Streaming Parameters Video Capture:
Capabilities : timeperframe
Frames per second: 60.000 (60/1)
Read buffers : 0
I have tried using various methods opencv but ffmpeg came the closest
With below command i am able to get good results but not what i want
ffmpeg -y -f v4l2 -pix_fmt NV12 -video_size 1920x1080 -i /dev/video0 -pix_fmt bgra -frames:v 10 webcam%03d.bmp
Reference Image
RGB of Reference image
RGB of captured image
Note :- I am able to capture fine with Aforge on windows, but not with ffmpeg on linux.
Would like to know if anyone has already got solution to this.
Thanks in advance.

We have to mark the input as BT.709 with "TV Range":
ffmpeg -y -f v4l2 -pix_fmt nv12 -video_size 1920x1080 -color_primaries bt709 -color_trc bt709 -colorspace bt709 -color_range tv -i /dev/video0 -pix_fmt bgra -frames:v 10 webcam%03d.bmp
Marking the input color format as BT.709:
By default FFmpeg assumes BT.601 color format, and the input video applies BT.709 color format, so we have to mark the video as BT.709 using -color_primaries bt709 -color_trc bt709 -colorspace bt709 arguments.
When the input color format is BT.709, and FFmpeg convert it as it were BT.601, the result is wrong output colors.
Marking the range as "TV Range":
By default FFmpeg assumes assumes "Limited Range" (TV range), but we may add -color_range tv to be sure.
Note:
"TV Range" applies "Limited range" - the range of Y color channel is [16, 235] (U and V rane is [16, 240]).
(opposed to "Full range" or "PC Range" where YUV range is [0, 255]).
For reproducing the issue, we may use synthetic video (instead of camera).
Create a reference image:
ffmpeg -y -f lavfi -i testsrc=size=192x108:rate=1:duration=1 -pix_fmt bgra ref%03d.bmp
Create NV12 raw frame in BT.709, "Limited Range" (TV Range):
ffmpeg -y -f lavfi -i testsrc=size=192x108:rate=1:duration=1 -vf scale=out_color_matrix=bt709:out_range=tv -pix_fmt nv12 -f rawvideo in.nv12
Convert the raw frame to BMP without marking the color format and range (getting wrong colors):
ffmpeg -y -f rawvideo -pix_fmt nv12 -video_size 192x108 -i in.nv12 -pix_fmt bgra -frames:v 1 wrong_colors_out%03d.bmp
Convert the raw frame to BMP with marking the color format and range (getting correct colors):
ffmpeg -y -f rawvideo -pix_fmt nv12 -video_size 192x108 -color_primaries bt709 -color_trc bt709 -colorspace bt709 -color_range tv -i in.nv12 -pix_fmt bgra -frames:v 1 out%03d.bmp
Ordered left to right:
Reference image, "wrong colors" and "correct colors":

Related

speed up imagemagick file conversion to monochrome image

$ file in.jp2
in.jp2: JPEG image data, JFIF standard 1.01, aspect ratio, density 1x1, segment length 16, baseline, precision 8, 3560x4810, components 3
I use the following command to convert a .jp2 file to a monochrome pdf file. But it takes 20 seconds to convert a file.
convert in.jp2 -threshold 75% -type bilevel -monochrome -compress Zip out.pdf
Is there a way to speed up the conversion without losing any resolution and increasing the output file size?

how to generate pixel data of an image

I'm working on a video encoder having the part number ADV7391BCPZ. Now i have to generate an image using encoder. The FPGA is feeding the required input to encoder. I want to generate the pixel data of the image interms of Y Cr Cb format because, my encoder will accept only that form of input.
Now i request you to suggest the method to convert an image to its pixel data.
The very simplest way to do that is with ImageMagick in your Terminal:
magick z9mSc.jpg -colorspace YCC txt:
Sample Output
# ImageMagick pixel enumeration: 180,180,255,ycc
0,0: (255,156.098,136.955) #FF9C89 ycc(255,156.098,136.955)
1,0: (255,156.098,136.955) #FF9C89 ycc(255,156.098,136.955)
2,0: (255,156.098,136.955) #FF9C89 ycc(255,156.098,136.955)
3,0: (255,156.098,136.955) #FF9C89 ycc(255,156.098,136.955)
4,0: (255,156.098,136.955) #FF9C89 ycc(255,156.098,136.955)
...
...
That tells you pixel 0,0 has YCC values (255,156.098,136.955) followed by the other pixels.
Or, if you want it in binary, you can do:
magick z9mSc.jpg YCbCr:image.bin
which will give you a file called image.bin with size 97,200 bytes because your image is 180x180 pixels and you will get 3 bytes (Y, Cb and Cr) for each pixel in the binary file.
Note that you can convert your image into its three constituent channels and lay them out side-by-side with Y on the left, Cb in the middle and Cr on the right like this:
magick z9mSc.jpg -colorspace YCBCR -separate +append separated.png
If you do that, you will notice that all the information is in the Y channel and that the other two channels are constant. So, you could save space and just use the Y channel in your FPGA and synthesise constant Cb and Cr channels alongside it. To extract just the Y channel, you can do:
magick z9mSc.jpg -colorspace YCBCR -channel R -separate gray:Y.bin
As you seem to be having trouble using ImageMagick, another option might be to use ffmpeg like this:
ffmpeg -i z9mSc.jpg -f rawvideo -pix_fmt yuv444p image.yuv
which gives you a planar YUV file of 97,200 bytes which seems correct for your 180x180 image.

How to detect if video file is pure static?

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

Convert images and show result using Image Magick

I am manipulating images using Image Magick. Here is the command that I use:
convert source.png -resize 1200 -quality 75 result.jpg
It works as expected. I am no wondering whether there is a way to report a conversion result showing how much compression has been done (in percentage or kilobyte or just by showing the sizes of original and converted images)?
I have tried -monitor switch, but it only shows a progress kind of report during conversion.
(I am using Linux)
The quality setting you use tells ImageMagick how much to compress the image. Alternately, you can compute the ratio of the output file size by the input size. Or the ratio of the output file size with -quality 75 to that when using -quality 100, even though -quality 100 still compresses some.
You can get the size of the image by
convert image -precision 16 -format "%b\n" info:
Setting a large precision will forces the result to be in Bytes, but you will have to remove the B character from the end. So you can do the following to get the percent of output/input file sizes:
outsize=$(convert output -precision 16 -format "%b\n" info: | sed 's/B//g')
insize=$(convert input -precision 16 -format "%b\n" info: | sed 's/B//g')
percent_size=$(convert xc: -format "%[fx:100*$outsize/$insize]\n" info:)

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