Percentage difference between images: Difference between revisions
m (→{{header|REBOL}}: Added explanatory note.) |
(→{{header|REBOL}}: Substantially rewrote REBOL example to use built-in image processing.) |
||
Line 306: | Line 306: | ||
Title: "Percent Image Difference" |
Title: "Percent Image Difference" |
||
Author: oofoe |
Author: oofoe |
||
Date: 2009-12- |
Date: 2009-12-31 |
||
URL: http://rosettacode.org/wiki/Percentage_of_difference_between_2_images |
URL: http://rosettacode.org/wiki/Percentage_of_difference_between_2_images |
||
] |
] |
||
; Load |
; Load from local storage. Un/comment as preferred. |
||
⚫ | |||
⚫ | |||
; Download from rosettacode.org. |
|||
a: load-image http://rosettacode.org/mw/images/3/3c/Lenna50.jpg |
|||
b: load-image http://rosettacode.org/mw/images/b/b6/Lenna100.jpg |
|||
⚫ | |||
⚫ | |||
if a/size <> b/size [print "Image dimensions must match." halt] |
if a/size <> b/size [print "Image dimensions must match." halt] |
||
; Compute difference. REBOL has built-in image processing as part of |
|||
; Compare a and b images. Since I'm touching the pixels anyway, I go |
|||
; its GUI package that I can take advantage of here: |
|||
; ahead and build a difference image ('d') to look at. I don't bother |
|||
; iterating through x and y coordinates; since the image data is a list |
|||
diff: to-image layout/tight [image a effect [difference b]] |
|||
; I can just rip through it linearly with 'repeat'. REBOL tuples |
|||
; are in the form ###.###.### or ###.###.###.### and are often used to |
|||
; Calculate deviation. I use 'repeat' to rip through the image pixels |
|||
; hold things like IP addresses, or in this case RGB/A colours. |
|||
; (it knows how to deal with images) and sum, then average. Note that |
|||
; I can treat the image like an array to get number of pixels. |
|||
t: 0 |
t: 0 |
||
repeat p diff [t: t + p/1 + p/2 + p/3] |
|||
diff: make image! 512x512 |
|||
⚫ | |||
repeat i npixels: a/size/x * a/size/y [ |
|||
delta: to-tuple reduce [ |
|||
abs (first a/:i) - first b/:i |
|||
abs (second a/:i) - second b/:i |
|||
abs (third a/:i) - third b/:i |
|||
] |
|||
diff/:i: delta |
|||
t: t + delta/1 + delta/2 + delta/3 |
|||
] |
|||
⚫ | |||
; Optional: Since I now have a difference image, I may as well show |
; Optional: Since I now have a difference image, I may as well show |
||
Line 340: | Line 337: | ||
; various images. |
; various images. |
||
flip: func |
flip: func [ |
||
"Change to new image and label." |
|||
⚫ | |||
name [word!] "Image to switch to." |
|||
⚫ | |||
; Because the differences between the images are very small, I enhance |
|||
; the diff with a high contrast to make the result easier to |
|||
; see. Comment this out for the "pure" image. |
|||
diff: to-image layout/tight [image diff effect [contrast 100]] |
|||
view layout [ |
view l: layout [ |
||
x: image diff |
x: image diff |
||
across |
across |
||
Line 353: | Line 358: | ||
Output: |
Output: |
||
<pre> |
<pre>Difference: 1.62559309816049%</pre> |
||
Note that this image has been contrast enhanced to highlight the differences. |
|||
Because I didn't normalize or otherwise enhance the difference image, it's very faint and may be hard to see on some LCD monitors. However, this is a lossless %.png image so all the detail is there. Try loading this screenshot of the GUI into your favourite image processing program and using the colour picker or contrast enhancement to see the JPEG edge artifacts. |
|||
[[File:pdiff.png]] |
[[File:pdiff.png]] |
Revision as of 10:32, 31 December 2009
You are encouraged to solve this task according to the task description, using any language you may know.
Compute the percentage of difference between 2 JPEG images of the same size. Alternatively, compare two bitmaps as defined in basic bitmap storage.
Useful for comparing two JPEG images saved with a different compression ratios.
You can use these pictures for testing:
The expected difference for these two images is 1.62125%
Ada
<lang ada>type Count is mod 2**64;</lang> 1-norm distance in the luminance space: <lang ada>function "-" (Left, Right : Luminance) return Count is begin
if Left > Right then return Count (Left) - Count (Right); else return Count (Right) - Count (Left); end if;
end "-";</lang> 1-norm distance in the color space multiplied to 3: <lang ada>function "-" (Left, Right : Pixel) return Count is begin
return (Left.R - Right.R) + (Left.G - Left.G) + (Left.B - Right.B);
end "-";</lang> Mean of 1-norm distances. Constraint_Error is propagated when images have different size. <lang ada>function Diff (Left, Right : Image) return Float is
Offs_I : constant Integer := Right'First (1) - Left'First (1); Offs_J : constant Integer := Right'First (2) - Left'First (2); Sum : Count := 0;
begin
if Left'Length (1) /= Right'Length (1) or else Left'Length (2) /= Right'Length (2) then raise Constraint_Error; end if; for I in Left'Range (1) loop for J in Left'Range (2) loop Sum := Sum + (Left (I, J) - Right (I + Offs_I, J + Offs_J)); end loop; end loop; return Float (Sum) / (3.0 * Float (Left'Length (1) * Left'Length (2)));
end Diff;</lang> Example of use: <lang ada> F1, F2 : File_Type; begin
Open (F1, In_File, "city.ppm"); Open (F2, In_File, "city_emboss.ppm"); Ada.Text_IO.Put_Line ("Diff" & Float'Image (Diff (Get_PPM (F1), Get_PPM (F2)))); Close (F1); Close (F2);</lang>
C
The read_image function is from here.
<lang c>#include <stdio.h>
- include <stdlib.h>
- include <math.h>
/* #include "imglib.h" */
- define RED_C 0
- define GREEN_C 1
- define BLUE_C 2
- define GET_PIXEL(IMG, X, Y) ((IMG)->buf[ (Y) * (IMG)->width + (X) ])
int main(int argc, char **argv) {
image im1, im2; double totalDiff = 0.0; unsigned int x, y; if ( argc < 3 ) { fprintf(stderr, "usage:\n%s FILE1 FILE2\n", argv[0]); exit(1); } im1 = read_image(argv[1]); if ( im1 == NULL ) exit(1); im2 = read_image(argv[2]); if ( im2 == NULL ) { free_img(im1); exit(1); } if ( (im1->width != im2->width) || (im1->height != im2->height) ) { fprintf(stderr, "width/height of the images must match!\n"); } else { /* BODY: the "real" part! */ for(x=0; x < im1->width; x++) { for(y=0; y < im1->width; y++) { totalDiff += fabs( GET_PIXEL(im1, x, y)[RED_C] - GET_PIXEL(im2, x, y)[RED_C] ) / 255.0; totalDiff += fabs( GET_PIXEL(im1, x, y)[GREEN_C] - GET_PIXEL(im2, x, y)[GREEN_C] ) / 255.0; totalDiff += fabs( GET_PIXEL(im1, x, y)[BLUE_C] - GET_PIXEL(im2, x, y)[BLUE_C] ) / 255.0; } } printf("%lf\n", 100.0 * totalDiff / (double)(im1->width * im1->height * 3) ); /* BODY ends */ } free_img(im1); free_img(im2);
}</lang>
The output on Lenna is:
1.625587
Common Lisp
This is based upon the C version. Strangely enough, the percentage is 1.77% which is off by about a tenth of a percent.
<lang lisp>(require 'cl-jpeg)
- the JPEG library uses simple-vectors to store data. this is insane!
(defun compare-images (file1 file2)
(declare (optimize (speed 3) (safety 0) (debug 0))) (multiple-value-bind (image1 height width) (jpeg:decode-image file1) (let ((image2 (jpeg:decode-image file2))) (loop for i of-type (unsigned-byte 8) across (the simple-vector image1) for j of-type (unsigned-byte 8) across (the simple-vector image2) sum (the fixnum (abs (- i j))) into difference of-type fixnum finally (return (coerce (/ difference width height #.(* 3 255)) 'double-float))))))
CL-USER> (* 100 (compare-images "Lenna50.jpg" "Lenna100.jpg")) 1.774856467652165d0</lang>
E
By dividing only at the end, we work with integers only as the sum and avoid floating-point error from adding small numbers (per-pixel difference) to large ones (sum of differences).
<lang e>def imageDifference(a, b) {
require(a.width() == b.width()) require(a.height() == b.height()) def X := 0..!(a.width()) def Y := 0..!(a.height()) var sumByteDiff := 0 for y in Y { for x in X { def ca := a[x, y] def cb := b[x, y] sumByteDiff += (ca.rb() - cb.rb()).abs() \ + (ca.gb() - cb.gb()).abs() \ + (ca.bb() - cb.bb()).abs() } println(y) } return sumByteDiff / (255 * 3 * a.width() * a.height())
}
def imageDifferenceTask() {
println("Read 1...") def a := readPPM(<import:java.io.makeFileInputStream>(<file:Lenna50.ppm>)) println("Read 2...") def b := readPPM(<import:java.io.makeFileInputStream>(<file:Lenna100.ppm>)) println("Compare...") def d := imageDifference(a, b) println(`${d * 100}% different.`)
}</lang>
The result on the provided images is 1.6255930981604882%.
Forth
<lang forth>: pixel-diff ( pixel1 pixel2 -- n )
over 255 and over 255 and - abs >r 8 rshift swap 8 rshift over 255 and over 255 and - abs >r 8 rshift swap 8 rshift - abs r> + r> + ;
- bdiff ( bmp1 bmp2 -- fdiff )
2dup bdim rot bdim d<> abort" images not comparable" 0e ( F: total diff ) dup bdim * >r ( R: total pixels ) bdata swap bdata r@ 0 do over @ over @ pixel-diff 0 d>f f+ cell+ swap cell+ loop 2drop r> 3 * 255 * 0 d>f f/ ;
- .bdiff ( bmp1 bmp2 -- )
cr bdiff 100e f* f. ." percent different" ;</lang>
Fortran
<lang fortran>program ImageDifference
use RCImageBasic use RCImageIO
implicit none
integer, parameter :: input1_u = 20, & input2_u = 21
type(rgbimage) :: lenna1, lenna2 real :: totaldiff
open(input1_u, file="Lenna100.ppm", action="read") open(input2_u, file="Lenna50.ppm", action="read") call read_ppm(input1_u, lenna1) call read_ppm(input2_u, lenna2) close(input1_u) close(input2_u)
totaldiff = sum( (abs(lenna1%red - lenna2%red) + & abs(lenna1%green - lenna2%green) + & abs(lenna1%blue - lenna2%blue)) / 255.0 )
print *, 100.0 * totaldiff / (lenna1%width * lenna1%height * 3.0)
call free_img(lenna1) call free_img(lenna2)
end program ImageDifference</lang>
This gives 1.6555154.
MAXScript
<lang maxscript>fn diffImages = ( local img1 = selectBitmap caption:"Select Image 1" local img2 = selectBitmap caption:"Select Image 2" local totalDiff = 0 for i in 0 to (img1.height-1) do ( local img1Row = getPixels img1 [0, i] img1.width local img2Row = getPixels img2 [0, i] img2.width
for j in 1 to img1.width do ( totalDiff += (abs (img1Row[j].r - img2Row[j].r)) / 255.0 totalDiff += (abs (img1Row[j].g - img2Row[j].g)) / 255.0 totalDiff += (abs (img1Row[j].b - img2Row[j].b)) / 255.0 ) ) format "Diff: %\%\n" (totalDiff / ((img1.width * img1.height * 3) as float) * 100) )</lang>
J
<lang j> require 'media/image3'
'Lenna50.jpg' (+/@,@:|@:- % 2.55 * */@$@])&read_image 'Lenna100.jpg'
1.62559</lang>
OCaml
<lang ocaml>#! /usr/bin/env ocaml
- directory "+glMLite/"
- load "jpeg_loader.cma"
- load "bigarray.cma"
open Jpeg_loader
let () =
let img1, width1, height1, col_comp1, color_space1 = load_img (Filename Sys.argv.(1)) and img2, width2, height2, col_comp2, color_space2 = load_img (Filename Sys.argv.(2)) in
assert(width1 = width2); assert(height1 = height2); assert(col_comp1 = col_comp2); (* number of color components *) assert(color_space1 = color_space2);
let img1 = Bigarray.array3_of_genarray img1 and img2 = Bigarray.array3_of_genarray img2 in
let sum = ref 0.0 and num = ref 0 in
for x=0 to pred width1 do for y=0 to pred height1 do for c=0 to pred col_comp1 do let v1 = float img1.{x,y,c} and v2 = float img2.{x,y,c} in let diff = (abs_float (v1 -. v2)) /. 255. in sum := diff +. !sum; incr num; done; done; done;
let diff_percent = !sum /. float !num in Printf.printf " diff: %f percent\n" diff_percent;
- </lang>
Python
<lang python>from itertools import izip import Image
i1 = Image.open("image1.jpg") i2 = Image.open("image2.jpg") assert i1.mode == i2.mode, "Different kinds of images." assert i1.size == i2.size, "Different sizes."
pairs = izip(i1.getdata(), i2.getdata()) if len(i1.getbands()) == 1:
# for gray-scale jpegs dif = sum(abs(p1-p2) for p1,p2 in pairs)
else:
dif = sum(abs(c1-c2) for p1,p2 in pairs for c1,c2 in zip(p1,p2))
ncomponents = i1.size[0] * i1.size[1] * 3 print "Difference (percentage):", (dif / 255.0 * 100) / ncomponents</lang>
REBOL
<lang REBOL>REBOL [ Title: "Percent Image Difference" Author: oofoe Date: 2009-12-31 URL: http://rosettacode.org/wiki/Percentage_of_difference_between_2_images ]
- Load from local storage. Un/comment as preferred.
- a
- load-image %lenna50.jpg
- b
- load-image %lenna100.jpg
- Download from rosettacode.org.
a: load-image http://rosettacode.org/mw/images/3/3c/Lenna50.jpg b: load-image http://rosettacode.org/mw/images/b/b6/Lenna100.jpg
if a/size <> b/size [print "Image dimensions must match." halt]
- Compute difference. REBOL has built-in image processing as part of
- its GUI package that I can take advantage of here
diff: to-image layout/tight [image a effect [difference b]]
- Calculate deviation. I use 'repeat' to rip through the image pixels
- (it knows how to deal with images) and sum, then average. Note that
- I can treat the image like an array to get number of pixels.
t: 0 repeat p diff [t: t + p/1 + p/2 + p/3] print rejoin ["Difference: " 100 * t / (255 * 3 * length? diff) "%"]
- Optional
- Since I now have a difference image, I may as well show
- it. Use the buttons or keys 'a', 'b' and 'd' to switch between the
- various images.
flip: func [ "Change to new image and label." name [word!] "Image to switch to." ][x/text: rejoin ["Image " name] x/image: get name show x]
- Because the differences between the images are very small, I enhance
- the diff with a high contrast to make the result easier to
- see. Comment this out for the "pure" image.
diff: to-image layout/tight [image diff effect [contrast 100]]
view l: layout [ x: image diff across button "a" #"a" [flip 'a] button "b" #"b" [flip 'b] button "difference" #"d" [flip 'diff] ]</lang>
Output:
Difference: 1.62559309816049%
Note that this image has been contrast enhanced to highlight the differences.
Ruby
uses the raster_graphics
library
<lang ruby>require 'raster_graphics'
class RGBColour
# the difference between two colours def -(a_colour) (@red - a_colour.red).abs + (@green - a_colour.green).abs + (@blue - a_colour.blue).abs end
end
class Pixmap
# the difference between two images def -(a_pixmap) if @width != a_pixmap.width or @height != a_pixmap.height raise ArgumentError, "can't compare images with different sizes" end sum = 0 each_pixel {|x,y| sum += self[x,y] - a_pixmap[x,y]} Float(sum) / (@width * @height * 255 * 3) end
end
lenna50 = Pixmap.open_from_jpeg('Lenna50.jpg') lenna100 = Pixmap.open_from_jpeg('Lenna100.jpg')
puts "difference: %.5f%%" % (100.0 * (lenna50 - lenna100))</lang>
produces:
difference: 1.62559%
Tcl
This version uses the Img package, but only to provide a convenient JPEG loader; it's utterly unnecessary for the process of actually computing the difference. <lang tcl>package require Tk
proc imageDifference {img1 img2} {
if {
[image width $img1] != [image width $img2] || [image height $img1] != [image height $img2]
} then {
return -code error "images are different size"
} set diff 0 for {set x 0} {$x<[image width $img1]} {incr x} {
for {set y 0} {$y<[image height $img1]} {incr y} { lassign [$img1 get $x $y] r1 g1 b1 lassign [$img2 get $x $y] r2 g2 b2 incr diff [expr {abs($r1-$r2)+abs($g1-$g2)+abs($b1-$b2)}] }
} expr {$diff/double($x*$y*3*255)}
}
- Package only used for JPEG loader
package require Img image create photo lenna50 -file lenna50.jpg image create photo lenna100 -file lenna100.jpg puts "difference is [expr {[imageDifference lenna50 lenna100]*100.}]%" exit ;# Need explicit exit here; don't want a GUI</lang> It produces this output:
difference is 1.6255930981604882%
Vedit macro language
This implementation compares two BMP images.
<lang vedit>Chdir("|(USER_MACRO)\Rosetta\data") File_Open("Lenna50.bmp", BROWSE)
- 10 = Buf_Num // #10 = buffer for 1st image
File_Open("Lenna100.bmp", BROWSE)
- 20 = Buf_Num // #20 = buffer for 2nd image
Goto_Pos(10) // offset to pixel data Goto_Pos(Cur_Char + Cur_Char(1)*256) Buf_Switch(#10) Goto_Pos(10) Goto_Pos(Cur_Char + Cur_Char(1)*256)
- 15 = 0 // #15 = difference
- 16 = 0 // #16 = total number of samples
While(!At_EOF) {
#11 = Cur_Char; Char Buf_Switch(#20) #21 = Cur_Char; Char #15 += abs(#11-#21) #16++ Buf_Switch(#10)
}
- 19 = #15 / (#16*256/100000)
M("Sum of diff: ") NT(#15) M("Total bytes: ") NT(#16) M("Difference: ") NT(#19/1000,LEFT+NOCR) M(".") NT(#19%1000,LEFT+NOCR) M("%\n")
Buf_Switch(#10) Buf_Quit(OK) Buf_Switch(#20) Buf_Quit(OK)</lang>
Output, when comparing the Lenna images that were converted to BMP:
Sum of diff: 3259967 Total bytes: 786432 Difference: 1.619%