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:
Related
I'm trying to do the following:
GIVEN:
Scanned/shot document - a form that is filled by many different programs.
I'm trying to recognize only small part of the data. As shown here:
All symbols are digits except the first one in the top-most field which is a letter.
THE PROBLEM is that I tried tessaract and google ml OCRs but the results is very poor , maybe because it's single symbols in cells, not normal text. I don't know.
So I decided to try I own simple recognizing module.
a) I transform it to grayscale and to B&W then
b) Unfortunately there is not garanteed that fields and at exact same places every time. Also they are not with same size because of the scan/photo.
So I'm dynamically trying to find the places of the fields.
But on there test photos I received for test there is no garantee that lines are stright.
Also the scanned/shot resolution is not always the same.
It would be great if someone can give me advice on the following:
Dynamically finding the fields. (currently my success is about 50% depending of the photo)
How to handle the non-straight lines.
How to detect a single cell content/symbol.
A good way to recognize single symbols/digits/ (compare the source etc.)
And maybe a better B&W-transformation, not a simple threshold.
Try to remove the rectangular frames around the numbers before recognition.
By morphological operations such as closing / opening, you can close the frame at the bottom of the picture, thereby saving the numbers.
convert input.jpg -threshold 90% -fuzz 25% -fill black -floodfill +0+0 white -fill white -floodfill +0+0 black out.png
I am working on a project with DICOM images where I need to compare two DICOM images. The problem is, one is in monochrome 1 and the other is in monochrome 2 (zero means white and black, respectively). How can I convert these pixel intensities to compare them? I am using the "pydicom" toolkit.
Your major problem is not the Photometric Interpretation (MONO1/2).
You cannot compare pixel intensities of two DICOM images unless they refer to the same scale (e.g. Hounsfield Units).
If you have
(0028,1052) RescaleIntercept - present with any value
(0028,1053) RescaleSlope - present with any value
(0028,1054) RescaleType - present with value "OD" or "HU"
Then it is pretty easy: Apply the linear transformation:
<measured value> = <pixel value> * RescaleSlope + RescaleIntercept
The measured values can be compared.
The same is true if you have a non-linear Modality LUT stored as a lookup table in the header, but the same restrictions apply for Rescale Type.
Otherwise I would refrain from comparing pixel values. Of course, it appears to be easy to just invert one of the two images, but the fact that they have different Photometric Interpretation tells me that they have been acquired by different devices or techniques. This means, that the pixel data is visually correct and comparable but not mathematically related.
If it helps, when visualising with matplotlib.pyplot you can use
plt.imshow(image, cmap='gray_r')
to invert the pixels back to Monochrome2 for visual comparison without changing pixel values.
Also,
np.invert(image)
might be a work-around.
I have a binary image below:
it's an image of random abstract picture, and by using matlab, what I wanna do is to detect, how many peaks does it have so I'll know that there are roughly 5 objects in it.
As you can see, there are, 5 peaks in it, so it means there are 5 objects in it.
I've tried using imregionalmax(), but I don't find it usefull, since my image already in binary image. I also tried to use regionprops('Area'), but it shows wrong number since there is no exact whitespace between each object. Thanks in advance
An easy way to do this would be to simply sum across the rows for each column and find the peaks of the result using findpeaks. In the example below, I have opted to use the inverse of the image which will result in positive peaks where the columns are.
rowSum = sum(1 - image, 1);
If we plot this, it looks like the bottom plot
We can then use findpeaks to identify the peaks in this plot. We will apply a 5-point moving average to it to help eliminate false peaks.
[peaks, locations, widths, prominences] = findpeaks(smooth(rowSum));
You can then select the "true" peaks by thresholding based on any of these outputs. For this example we can use prominences and find the more prominent peaks.
isPeak = prominences > 50;
nPeaks = sum(isPeak)
5
Then we can plot the peaks locations to confirm
plot(locations(isPeak), peaks(isPeak), 'r*');
If you have some prior knowledge about the expected widths of the peaks, you could adjust the smooth span to match this expected width and obtain some cleaner peaks when using findpeaks.
Using an expected width of 40 for your image, findpeaks was able to perfectly detect all 5 peaks with no false positive.
findpeaks(smooth(rowSum, 40));
As your they are peaks, they are vertical structures. So in this particular case, you case use projection histograms (also know as histogram projection function): you make all the black pixels fall as if they were effected by gravity. Then you will find a curve of black pixels on the bottom of your image. Then you can count the number of peaks.
Here is the algorithm:
Invert the image (black is normally the absence of information)
Histogram projection
Closing and opening in order to clean the signal and get the final result.
You can add a maxima detection to get the top of the peaks.
I'm working with a library of photos, and I'd like some way of calculating a 'detail' value for each image as a whole. By detail, I mean the amount of contrasting colours, shades and edges, such that an image that's a single flat colour would have 0 detail, and a photo filled with lots of small elements would have a high detail value.
This doesn't have to be super-precise - I'd like to identify images that have a detail value higher than some threshold, and treat them differently. A reasonable guess is good enough.
I have a few ideas that might be feasible:
Save the image as a JPG at a given size and compression level, and check the resulting file size. This basically uses the compression algorithm as the check - detailed images make large files. Seems slow, expensive, and crude, but it wouldn't require a lot of custom work.
Sub-divide the image into a grid, sample points within each square, and compare how unique their values are. It seems like it could work, but would require a fine grid and a lot of samples in order to be useful.
Use an edge-detecting filter like unsharp-mask: Take the original, and a copy sharpened by a known amount, then take the average colour of each. If they are very different, the filter has done a 'lot of work' and therefore the image has a lot of edges (and so a lot of detail). This seems promising, but I'm not sure if it would actually work!
Processing will be done out-of-band, so performance isn't a huge issue. If it takes a few seconds per image, that's fine. I'm using rMagick (imageMagick) and Ruby.
Am I missing something? Is there an easier way?
You could try measuring the entropy and see how that works for your images - it's hard to tell how well it will perform for your needs though.
You get ImageMagick to measure it like this:
identify -format '%[entropy]' input.jpg
You can also measure it using the convert tool like this:
convert -size 100x100 xc:white gradient:red-blue +append -print '%[entropy]' null:
Or you could do, say, a Canny edge detection and then calculate the mean of the resulting black and white image which will tell you what percentage of the pixels are edges as an integer between 0-100, like this:
convert input.jpg -canny 0x1+10%+30% -format "%[fx:int(mean*100)]" info:
12
As suggested by Kurt, you could also look at the number of unique colours divided by the number of pixels. which will obviously give small results for large blocks of constant colour and larger results for photograph-type images.
identify -format "%k different colors, %w (width), %h (height)\n" image.jpg
I liked the edge detection suggestion -
convert input.jpg -canny 0x1+10%+30% -format "%[fx:int(mean*100)]" info:
However, this has some big drawbacks. The biggest is that the values you get are hugely dependent on the size of the image.
For reference, I am interested in using this to get an objective measure of the level of detail in Tibetan thangkas. The price of a thangka is very dependent on the amount of time an artist takes to paint it and that, in turn, is hugely dependent on the amount of detail in the painting. The more painstaking little details the longer it take to paint the thangka. One thangka with a lot of detail can take over a year. Another thangka that is the same size but has much less detail can take just a week.
To be useful, this measure has to be scaled based on the size of the photograph - it should give the same answer if you have a gigapixel photograph as if you have a megapixel photograph as long as you have enough resolution to see the smallest details in the painting.
My first pass with ImageMagick is:
magick convert pic.jpg -canny 0x1+10%+30% -format "%[fx:mean*sqrt(w*h)]" info:
When I test with this image, it works very well.
multi-star picture
I get the same value for this image when I scale it by a factor of 2 and a pretty close value when I scale it down by 10%... but the number is smaller which you should expect because some of the detail is being eliminated when you scale it down.
Unfortunately, when I try it on an actual thangka image it does not work as well:
Actual thangka
When I scale this up I get very different results - by as much as a factor of 1.5. Need to find a better measure.
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.