Это не окончательный ответ на мой собственный вопрос, но это слишком долго для комментария.
Я реализовал идею использования RMS Laplacian. Идея состоит в том, что если яркость изображения представлена массивом пикселей a [i, j], то в любой точке (i, j) мы имеем дискретное приближение к лапласиану L = a [i-1, J] + а [I + 1, J] + а [I, J-1] + а [I, J + 1] -4a [I, J]. Это измеряет четкость изображения. Например, если изображение не в фокусе, L будет ниже. Среднеквадратичное значение лапласиана, R, является квадратным корнем из среднего квадрата лапласиана.
Вот мой код, который вычисляет R для входного PNG-изображения:
#!/usr/bin/ruby
# To batch convert a bunch of JPGs to png:
# perl -e '$i=0; foreach $f(<*.JPG>) {$s=sprintf("%03d",$i); $c="convert $f $s.png"; print "$c\n"; system($c); $i=$i+1;}'
require 'oily_png'
# require 'hsluv'
# http://www.hsluv.org
# https://github.com/hsluv/hsluv-ruby
# sudo gem install hsluv
# Sloppy and probably not physiologically valid, but fast.
# Returns an integer from 0 to 255*3.
def color_to_brightness(c)
return ChunkyPNG::Color::r(c)+ChunkyPNG::Color::g(c)+ChunkyPNG::Color::b(c)
end
def rms_laplacian_from_file(input_file)
image = ChunkyPNG::Image.from_file(input_file)
n = 0
sum = 0
sum_sq = 0
w = image.width
h = image.height
1.upto(w-2) { |i|
### if i%1000==0 then print "i=#{i}\n" end # show progress
next unless i>w/3 && i<(2*w)/3 ## for efficiency, only use center of frame
1.upto(h-2) { |j|
next unless j>h/3 && j<(2*h)/3 ## for efficiency, only use center of frame
next unless rand(10)==0 # for efficiency
a = Hash.new
(-1).upto(1) { |k|
(-1).upto(1) { |l|
c = image[i+k,j+l] # color, represented as a 4-byte rgba value
a[[k,l]] = color_to_brightness(c)
}
}
laplacian = a[[1,0]]+a[[-1,0]]+a[[0,1]]+a[[0,-1]]-4*a[[0,0]]
n = n+1
sum = sum + laplacian
sum_sq = sum_sq + laplacian*laplacian
}
}
sum = sum.to_f/n
sum_sq = sum_sq.to_f/n
rms = Math::sqrt(sum_sq-sum*sum)
return rms
end
ARGV.each { |input_file|
rms = rms_laplacian_from_file(input_file)
print "#{input_file} -- rms=#{rms}\n"
}
Это реализовано в Ruby и работает в Linux с использованием библиотеки oily_png с открытым исходным кодом. Если кто-то заинтересован в том, чтобы попробовать его, он должен почти не требовать изменений для запуска на других платформах, если у вас установлены Ruby и oily_png.
Чтобы проверить, что он измеряет резкость, я взял первое изображение из моего набора из 16, измерил R, а затем добавил 5-пиксельное размытие по Гауссу, используя GIMP, и заново измерил R. Результат был R = 30,8 до размытия и R = 7,8 после. Так что это, кажется, подтверждает, что он измеряет резкость.
Мои 16 изображений пронумерованы от 000 до 015. Глядя на изображения на глаз, я ранее выбрал изображение 003 как лучшее. Это было изображение, на которое я разместил ссылку в вопросе.
Я запустил свой код на 16 снятых снимках и получил следующий вывод:
000.png -- rms=30.809465960392004
001.png -- rms=31.215359700578606
002.png -- rms=31.909926250066476
003.png -- rms=31.83243374839454
004.png -- rms=31.310612756003305
005.png -- rms=30.353258897447564
006.png -- rms=30.61244684985801
007.png -- rms=30.882745734215135
008.png -- rms=28.667104210689384
009.png -- rms=29.862966602367973
010.png -- rms=29.72001987743495
011.png -- rms=30.51274847773823
012.png -- rms=30.84316910530572
013.png -- rms=29.21751498027252
014.png -- rms=29.067434969521976
015.png -- rms=30.831305018709617
Из 16 изображений мой выбор имел второе по величине значение R. Казалось бы, это подтверждает, что эта статистика может быть полезна в качестве альтернативы проверке изображений и субъективной оценке их на глаз.
Моя реализация довольно медленная, и чтобы восполнить это, я сделал несколько вещей, чтобы улучшить ее производительность. Я проверяю только середину поля и пробую лапласиан только в 1/10 точек. В более оптимизированной реализации эти ярлыки могут быть устранены при желании.
Позже мне пришло в голову, что может быть гораздо более простой способ сделать это. Изображение с большим количеством деталей также не должно сжиматься, поэтому самый большой файл JPG может быть просто лучшим. Конечно, выполнение команды ls -lS для вывода списка файлов в порядке уменьшения размера дает список, который был почти в том же порядке, что и файлы, отсортированные по убыванию R:
-rw-rw-r-- 1 bcrowell bcrowell 16970354 Oct 25 15:48 003.png
-rw-rw-r-- 1 bcrowell bcrowell 16927174 Oct 25 15:48 002.png
-rw-rw-r-- 1 bcrowell bcrowell 16903104 Oct 25 15:48 004.png
-rw-rw-r-- 1 bcrowell bcrowell 16882373 Oct 25 15:47 000.png
-rw-rw-r-- 1 bcrowell bcrowell 16861082 Oct 25 15:47 001.png
-rw-rw-r-- 1 bcrowell bcrowell 16817527 Oct 25 15:48 006.png
-rw-rw-r-- 1 bcrowell bcrowell 16816529 Oct 25 15:49 011.png
-rw-rw-r-- 1 bcrowell bcrowell 16793982 Oct 25 15:49 012.png
-rw-rw-r-- 1 bcrowell bcrowell 16786443 Oct 25 15:48 009.png
-rw-rw-r-- 1 bcrowell bcrowell 16773575 Oct 25 15:48 005.png
-rw-rw-r-- 1 bcrowell bcrowell 16771759 Oct 25 15:49 010.png
-rw-rw-r-- 1 bcrowell bcrowell 16765674 Oct 25 15:48 007.png
-rw-rw-r-- 1 bcrowell bcrowell 16764562 Oct 25 15:49 015.png
-rw-rw-r-- 1 bcrowell bcrowell 16750179 Oct 25 15:48 008.png
-rw-rw-r-- 1 bcrowell bcrowell 16732854 Oct 25 15:49 013.png
-rw-rw-r-- 1 bcrowell bcrowell 16684073 Oct 25 15:49 014.png