Imagemagick position and size of a sub-image - image

Problem description:
In imagemagick, it is very easy to diff two images using compare, which produces an image with the same size as the two images being diff'd, with the diff data. I would like to use the diff data and crop that part from the original image, while maintaining the image size by filling the rest of the space with alpha.
The approach I am taking:
I am now trying to figure the bounding box of the diff, without luck. For example, below is the script I am using to produce the diff image, see below. Now, I need to find the bounding box of the red color part of the image. The bounding box is demonstrated below, too. Note that the numbers in the image are arbitrary and not the actual values I am seeking.
compare -density 300 -metric AE -fuzz 10% ${image} ${otherImage} -compose src ${OUTPUT_DIR}/diff${i}-${j}.png

You asked quite a while ago - I found the question just today. Since I think the answer might still be of interest, I propose the following.
The trim option of convert removes any edges that are the same color as the corner pixels. The page or virtual canvas information of the image is preserved. Therefore, if you run
convert -trim edPdf.png - | identify -
it gives you:
PNG 157x146 512x406+144+32 8-bit PseudoClass 2c 1.08KB 0.000u 0:00.000
and the values you are looking for are (144,228), where the latter is 406-146-32 because you looked for the lower left corner whereas (+144+32) gives the upper left corner.

Related

Compare images for rendering differences (ignoring antialiasing)

I have a PDF file that I render using Adobe PDF engine as well as another PDF rip. I want to try to identify places where they render differently. The problem is that they both render with slightly different anti-alias, color, and very minimal positional differences. I am more concerned with larger difference. I'd like to compare the attached files match1.png and match2.png. These two should match. The files diff1.png and diff2.png should NOT match. They should fail as the one has a missing letter. I've worked with ImageMagick's compare, but haven't got good enough results to ignore the slight rendering differences and only focus on the major ones. I think the below algorithm could work, but I wasn't sure how to read PNG files and compare them pixel by pixel.
sample image files
compare each pixel in ImageA to the same pixel in ImageB. Also compare to each neighboring pixel in ImageB
Find the nearest matching pixel from all the tested pixels in ImageB.
Store the color difference of this closest matching pixel as difference index for that pixel in ImageA.
Repeat this calculation for each pixel in ImageA.
Normalize the difference values to make the smallest difference a zero. Un-normalized values might be (3,8,5,18) and normalized may be (0,5,2,15) or some other type of normalizing function.
Then count up how many pixels are over a threshold value, say, 5 or whatever. Then this count of 'bad' pixels could be used to calculate the probability that there is a rendering error in one of the images.
This is just a sample algorithm. I wasn't sure how to process PNG files pixel by pixel. I'd be open to a PHP solution or a solution using command line tools such as imagemagick, etc.
If you flicker the two match image (alternately display one then the other and back and forth) you will see that both orange colors are different. It is not just difference due to anti-aliasing at the edges.
So using Imagemagick 6, compare will show quite a lot of change (as red). If using Imagemagick 7, use magick compare. See http://www.imagemagick.org/Usage/compare/#compare.
compare -metric rmse match1.png match2.png match_diff.png
2304.18 (0.0351595)
Here the difference is one 3.5%, but the difference image show a lot of red since every pixel has a different value.
Another way to see the difference is with -compose difference. The whiter the result the more difference.
convert match1.png match2.png -compose difference -composite match_diff2.png
This does not show much different, so all values are only slightly different. But by stretching the dynamic range, you can see where it is most different.
One way to check neighboring pixels is to repeat the compare shifting one image 1 pixel in each direction (using -roll +X+Y) and perhaps trimming off 1 pixel all around each image using -shave XxY. You can do that in a loop and save each result. Then use -evaluate-sequence min to find the minimum (closest pixel values) for each image at the same pixel location. That would be equivalent to search the 3x3 neighborhood of compares. The problem is you won't know which image gave the max result.
P.S. You can also use compare -metric AE -fuzz 5% . The fuzz value will allow the compare to ignore values that within that percent. This does not seem to be working in Imagemagick 6.9.9.10 but is working in Imagemagick 7.0.6.10.
im7 magick compare -metric AE -fuzz 5% match1.png match2.png match_diff3.png
219487
This says that there are 219487 mismatched pixels of any amount. Here are where they differ.
I would blur the images slightly to remove any fine detail, then look for the maximum difference. A simple difference metric is dE76. Spatial CIELAB does something very similar (if a little fancier).
Here's an implementation using php-vips:
#!/usr/bin/env php
<?php
require __DIR__ . '/vendor/autoload.php';
use Jcupitt\Vips;
$a = Vips\Image::newFromFile($argv[1]);
$b = Vips\Image::newFromFile($argv[2]);
$a = $a->gaussblur(2);
$b = $b->gaussblur(2);
$diff = $a->dE76($b);
# output a 500 pixel across difference map
$scale = 500.0 / $diff->width;
$diff->multiply(5)->resize($scale)->writeToFile("map.png");
echo("maximum image difference is " . $diff->max() . " dE76\n");
For your images, I see:
$ ./measure_diff.php match1.png match2.png
maximum image difference is 13.739426612854 dE76
$ ./measure_diff.php diff1.png diff2.png
maximum image difference is 55.040554046631 dE76
The 13 in the first pair is just differences in colour rendering, the 55 in the second is the extra comma. It's very visible on the map image:

How to efficiently detect bar code target in an image

I have two questions:
Firstly, how to detect the area of bar code target in an image (like the sample images), which may have a few noises.
Secondly, how to efficiently do the detection, for instance, in 1/30 seconds.
Squash (resize) the image till it is only 1 pixel tall, then normalise it to the full range of 0-255 and threshold. I am using ImageMagick at the command-line here - it is installed on most Linux distros and is available for OSX and Windows also with Python, PHP, Ruby, C/C++ bindings.
convert barcode.png -resize x1! -scale x10! -normalize -threshold 50% result.png
I have then scaled it to 10 pixels tall so you can actually see it on here - but you would keep the original width and have a height of one pixel. Then just find the first white pixel in your single row of pixels.
Your recently added, smaller barcode gives this:

How to categorize by color in Ruby like Dribbble?

Dribble has a great feature that lets you browse shots by similar colors:
What is the easiest way to generate something like this in Ruby? Are there libraries or services that can manage this kind of processing? I currently have 26k images which I'll need to process and I'm evaluating the best way to do so.
This will most likely rely on the imagemagick utility in some capacity. A quick search of available libraries turned up the Miro gem available here: https://github.com/jonbuda/miro.
Not sure which aspect of the problem you need help with - generating the swatches of colour or sorting by similar colours. Anyway, here is how you can use ImageMagick to generate the 6 best colours to represent an image and make that into a colour swatch of a resonable size:
convert input.png -colors 6 -unique-colors -scale 5000% swatch.png
If you want the colours as RGB triplets, just change the command to this:
convert input.png -colors 6 -unique-colors +matte -colorspace RGB txt:
# ImageMagick pixel enumeration: 6,1,255,rgb
0,0: (1623,1472,1531) #060606 rgb(6,6,6)
1,0: (10693,4106,4231) #2A1010 rgb(42,16,16)
2,0: (23082,8867,9471) #5A2325 rgb(90,35,37)
3,0: (8667,28247,37488) #226E92 rgb(34,110,146)
4,0: (40714,34524,37545) #9E8692 rgb(158,134,146)
5,0: (59611,58620,58816) #E8E4E5 rgb(232,228,229)
And if you want to find the distance between two colours, e.g. the first and last colours listed above, you can use this:
compare -metric RMSE xc:"rgb(232,228,229)" xc:"rgb(6,6,6)" null:
57484 (0.87715)
The number in parentheses means that the colour distance in the RGB colour cube is 87% of the distance between black and white - i.e. normalised to the diagonal of the colour cube as 100%. As the first number is nearly black, i.e. rgb(0,0,0) and the second is nearly white, i.e. rgb(255,255,255), the distance between the colours is 87%.
There are Ruby bindings for ImageMagick - here and here.

Change Pixel Values of Image to remove Date

What is the most efficient technique to remove the date that a Camera embeds on any image it takes.
The task is to prepare a script/code/software that shall remove the date from the given input image file (jpeg, png).
Please let me know an optimum way to accomplish this.
Thank you.
Here is an alternative approach to the question. You can determine the average colour of the bottom right corner of the image (size 250px wide x 100px high) with Imageagick like this:
ave=$(convert sign.jpg -gravity southeast -crop 250x100+0+0 -scale 1x1 -format "%[pixel:p{0,0}]" info:)
That will give you the value srgb(199,181,119) for this image. Now you can create a rectangle (200px x 50px) that colour and overlay it onto the image and then blur the edges a little to blend it in:
convert sign.jpg \( -size 200x50 xc:"$ave" \) -geometry +970+780 -composite -region 240x90+950+760 -blur 0x10 out.jpg
I am not sure if you were hoping for something that is forensically indetectable or something that more or less removes the distraction somewhat. Hopefully the latter :-)
A little measuring around shows that the date is located at the following position:
150x40+650+520
i.e. 150 pixels wide by 40 pixels high, located 650 pixels to the right of the top left corner and 520 pixels down from the top left corner.
So, one approach would be to copy the piece of the image directly below that, and paste it on top of the date, which can be done in ImageMagick in one command like this:
convert sign.jpg \( +clone -crop 150x40+650+560 +repage \) -geometry +650+520 -composite out.jpg
That says... take the original image and create a copy of it (+clone), then cut out the piece specified after the crop command command and reset that as though it was the top left corner (+repage). Then paste (-composite) that image at offset +650+520 on top of the original image and save the result as out.jpg.
It is not a beautifully engiineered solution, but may be good enough. It may be desirable to blur the area a little, to help disguise it. Alternatively, it may be possible to select the colours within the date and make them transparent, then to displace the original image a little behind the transparent holes to fill them - I didn't choose that option because it is harder and because you may not like ImageMagick anyway, and there are actually several colours within the date field ranging from browns to golden yellows and selecting them without affecting the remainder of the image might start getting fun!
ImageMagick is free and available for Windows, OSX, Linux etc from here. It is ready installed on most Linux distros anyway.

Remove background color in image processing for OCR

I am trying to remove background color so as to improve the accuracy of OCR against images. A sample would look like below:
I'd keep all letters in the post-processed image while just removing the light purple color textured background. Is it possible to use some open source software such as Imagemagick to convert it to a binary image (black/white) to achieve this goal? What if the background has more than one color? Would the solution be the same?
Further, what if I also want to remove the purple letters (theater name) and the line so as to only keep the black color letters? Simple cropping might not work because the purple letters could appear at other places as well.
I am looking for a solution in programming, rather than via tools like Photoshop.
You can do this using GIMP (or any other image editing tool).
Open your image
Convert to grayscale
Duplicate the layer
Apply Gaussian blur using a large kernel (10x10) to the top layer
Calculate the image difference between the top and bottom layer
Threshold the image to yield a binary image
Blurred image:
Difference image:
Binary:
If you're doing it as a once-off, GIMP is probably good enough. If you expect to do this many times over, you could probably write an imagemagick script or code up your approach using something like Python and OpenCV.
Some problems with the above approach:
The purple text (CENTURY) gets lost because it isn't as contrasting as the other text. You could work your way around it by thresholding different parts of the image differently, or by using local histogram manipulation methods
The following shows a possible strategy for processing your image, and OCR it
The last step is doing an OCR. My OCR routine is VERY basic, so I'm sure you may get better results.
The code is Mathematica code.
Not bad at all!
In Imagemagick, you can use the -lat function to do that.
convert image.jpg -colorspace gray -negate -lat 50x50+5% -negate result.jpg
convert image.jpg -colorspace HSB -channel 2 -separate +channel \
-white-threshold 35% \
-negate -lat 50x50+5% -negate \
-morphology erode octagon:1 result2.jpg
You can apply blur to the image, so you get almost clear background. Then divide each color component of each pixel of original image by the corresponding component of pixel on the background. And you will get text on white background. Additional postprocessing can help further.
This method works in the case if text is darker then the background (in each color component). Otherwise you can invert colors and apply this method.
If your image is captured as RGB, just use the green image or quickly convert the bayer pattern which is probably #misha's convert to greyscale solutions probably do.
Hope this helps someone
Using one line code you can get is using OpenCV and python
#Load image as Grayscale
im = cv2.imread('....../Downloads/Gd3oN.jpg',0)
#Use Adaptivethreshold with Gaussian
th = cv2.adaptiveThreshold(im,255,cv2.ADAPTIVE_THRESH_GAUSSIAN_C,cv2.THRESH_BINARY,11,2)
Here's the result
Here's the link for Image Thresholding in OpenCV

Resources