Commit 25391245 authored by Sushil BHATTACHARJEE's avatar Sushil BHATTACHARJEE

Merge branch 'cleanup' into 'master'

Code clean-up

See merge request !1
parents 208a18f3 dbdd3b4f
Pipeline #8443 passed with stages
in 11 minutes and 44 seconds
......@@ -4,6 +4,7 @@
from .galbally_iqm_features import compute_quality_features
from .msu_iqa_features import compute_msu_iqa_features
def get_config():
"""
Returns a string containing the configuration information.
......@@ -13,7 +14,5 @@ def get_config():
return bob.extension.get_config(__name__)
# gets sphinx autodoc done right - don't remove it
__all__ = [_ for _ in dir() if not _.startswith('_')]
......@@ -8,48 +8,51 @@ Created on 25 Sep 2015
'''
#import re
#import os
import math
import numpy as np
import scipy as sp
import scipy.signal as ssg
import scipy.ndimage.filters as snf
import bob.ip.base
"""
Main function to be called, to extract a set of image quality-features computed for the input image
:param image: 2d numpy array. Should contain input image of size [M,N] (i.e. M rows x N cols).
:return featSet: a tuple of float-scalars, each representing one image-quality measure.
compute_quality_features is the main function to be called, to extract a set of
image quality-features computed for the input image
:param image: 2d numpy array. Should contain input image of size [M,N] (i.e. M
rows x N cols).
:return featSet: a tuple of float-scalars, each representing one image-quality
measure.
"""
def compute_quality_features(image):
"""Extract a set of image quality-features computed for the input image.
Parameters:
image (:py:class:`numpy.ndarray`): A ``uint8`` array with 2 or 3 dimensions, representing the input image of shape [M,N] (M rows x N cols).
If 2D, image should contain a gray-image of shape [M,N].
If 3D, image should have a shape [3,M,N], and should contain an RGB-image.
image (:py:class:`numpy.ndarray`): A ``uint8`` array with 2 or 3
dimensions, representing the input image of shape [M,N] (M rows x N
cols). If 2D, image should contain a gray-image of shape [M,N]. If 3D,
image should have a shape [3,M,N], and should contain an RGB-image.
Returns:
featSet (:py:class:`numpy.ndarray`): a 1D numpy array of 18 float32 scalars, each representing one image-quality measure.
This function returns a subset of the image-quality features (for face anti-spoofing) that have been
described by Galbally et al. in their paper:
"Image Quality Assessment for Fake Biometric Detection: Application to Iris, Fingerprint, and Face Recognition",
IEEE Trans. on Image Processing Vol 23(2), 2014.
featSet (:py:class:`numpy.ndarray`): a 1D numpy array of 18 float32
scalars, each representing one image-quality measure. This function
returns a subset of the image-quality features (for face anti-spoofing)
that have been described by Galbally et al. in their paper:
"Image Quality Assessment for Fake Biometric Detection: Application to
Iris, Fingerprint, and Face Recognition", IEEE Trans. on Image
Processing Vol 23(2), 2014.
"""
gray_image = None
#print("shape of input image:")
#print(image.shape)
# print(image.shape)
if len(image.shape) == 3:
if(image.shape[0]==3):
gray_image = matlab_rgb2gray(image) #compute gray-level image for input color-frame
if(image.shape[0] == 3):
# compute gray-level image for input color-frame
gray_image = matlab_rgb2gray(image)
# print(gray_image.shape)
else:
print('error. Wrong kind of input image')
......@@ -60,176 +63,229 @@ def compute_quality_features(image):
else:
print('error -- wrong kind of input')
if gray_image is not None:
if gray_image is not None:
gwin = gauss_2d((3,3), 0.5) # set up the smoothing-filter
gwin = gauss_2d((3, 3), 0.5) # set up the smoothing-filter
# print("computing degraded version of image")
smoothed = ssg.convolve2d(gray_image, gwin, boundary='symm', mode='same')
smoothed = ssg.convolve2d(
gray_image, gwin, boundary='symm', mode='same')
"""
Some of the image-quality measures computed here require a reference image.
For these measures, we use the input-image itself as a reference-image, and we compute
the quality-measure of a smoothed version of the input-image. The assumption in this
approach is that smoothing degrades a spoof-image more than it does a genuine image
(see Galbally's paper referenced above).
Some of the image-quality measures computed here require a reference
image. For these measures, we use the input-image itself as a
reference-image, and we compute the quality-measure of a smoothed
version of the input-image. The assumption in this approach is that
smoothing degrades a spoof-image more than it does a genuine image (see
Galbally's paper referenced above).
"""
# print("computing galbally quality features")
featSet = image_quality_measures(gray_image, smoothed)
return featSet
else:
return None
"""
actually computes various measures of similarity between the two input images, but also returns some descriptors of the reference-image that are independent of any other image.
Returns a tuple of 18 values, each of which is a float-scalar.
The quality measures computed in this function correspond to the Image-quality features discussed in Galbally et al., 2014.
actually computes various measures of similarity between the two input images,
but also returns some descriptors of the reference-image that are independent
of any other image. Returns a tuple of 18 values, each of which is a float-
scalar. The quality measures computed in this function correspond to the Image-
quality features discussed in Galbally et al., 2014.
"""
def image_quality_measures(refImage, testImage):
"""Compute image-quality measures for testImage and return a tuple of quality-measures.
Some of the quality-measures require a reference-image, but others are 'no-reference' measures.
:input refImage: 2d numpy array. Should represent input 8-bit gray-level image of size [M,N].
:input testImage: 2d numpy array. Should represent input 8-bit gray-level image of size [M,N]..
:return a tuple of 18 values, each of which is a float-scalar.
The quality measures computed in this function correspond to the Image-quality features discussed in Galbally et al., 2014.
"""Compute image-quality measures for testImage and return a tuple of
quality-measures. Some of the quality-measures require a reference-
image, but others are 'no-reference' measures.
:input refImage: 2d numpy array. Should represent input 8-bit gray-level
image of size [M,N].
:input testImage: 2d numpy array. Should represent input 8-bit gray-
level image of size [M,N]..
:return a tuple of 18 values, each of which is a float-scalar. The
quality measures computed in this function correspond to the Image-
quality features discussed in Galbally et al., 2014.
"""
assert len(refImage.shape)==2, "refImage should be a 2D array"
assert len(testImage.shape)==2, "testImage should be a 2D array"
assert (refImage.shape[0] == testImage.shape[0]), "The two images should have the same width"
assert (refImage.shape[1] == testImage.shape[1]), "The two images should have the same height"
diffImg = refImage.astype(np.float) - testImage.astype(np.float)
assert len(refImage.shape) == 2, "refImage should be a 2D array"
assert len(testImage.shape) == 2, "testImage should be a 2D array"
assert (refImage.shape[0] == testImage.shape[0]
), "The two images should have the same width"
assert (refImage.shape[1] == testImage.shape[1]
), "The two images should have the same height"
diffImg = refImage.astype(np.float) - testImage.astype(np.float)
diffSq = np.square(diffImg)
sumDiffSq = np.sum(diffSq)
absDiffImg = np.absolute(diffImg)
refSq = np.square(refImage.astype(np.float))
sumRefSq = np.sum(refSq)
numPx = refImage.shape[0]*refImage.shape[1] #number of pixels in each image
maxPxVal = 255.0;
#1 MSE
mse00 = float(sumDiffSq)/float(numPx)
#2 PSNR
# number of pixels in each image
numPx = refImage.shape[0] * refImage.shape[1]
maxPxVal = 255.0
# 1 MSE
mse00 = float(sumDiffSq) / float(numPx)
# 2 PSNR
psnr01 = np.inf
if mse00>0:
psnr01 = 10.0*np.log10(maxPxVal*maxPxVal/mse00)
#3 AD: Average difference
ad02 = float(np.sum(diffImg))/float(numPx)
#4 SC: structural content
if mse00 > 0:
psnr01 = 10.0 * np.log10(maxPxVal * maxPxVal / mse00)
# 3 AD: Average difference
ad02 = float(np.sum(diffImg)) / float(numPx)
# 4 SC: structural content
testSq = np.square(testImage.astype(np.float))
sumTestSq = np.sum(testSq)
sc03=np.inf
if sumTestSq>0:
sc03 = float(sumRefSq)/float(sumTestSq)
#5 NK: normalized cross-correlation
imgProd = refImage * testImage # element-wise product
nk04 = float(np.sum(imgProd))/float(sumRefSq)
#6 MD: Maximum difference
sc03 = np.inf
if sumTestSq > 0:
sc03 = float(sumRefSq) / float(sumTestSq)
# 5 NK: normalized cross-correlation
imgProd = refImage * testImage # element-wise product
nk04 = float(np.sum(imgProd)) / float(sumRefSq)
# 6 MD: Maximum difference
md05 = float(np.amax(absDiffImg))
#7 LMSE: Laplacian MSE
#scipy implementation of laplacian is different from Matlab's version, especially at the image-borders
# To significant differences between scipy...laplace and Matlab's del2() are:
# a. Matlab del2() divides the convolution result by 4, so the ratio (scipy.laplace() result)/(del2()-result) is 4
# b. Matlab does a different kind of processing at the boundaries, so the results at the boundaries are different in the 2 calls.
#In Galbally's Matlab code, there is a factor of 4, which I have dropped (no difference in result),
#because this is implicit in scipy.ndimage.filters.laplace()
op = snf.laplace(refImage, mode='reflect') #mode can be 'wrap', 'reflect', 'nearest', 'mirror', or ['constant' with a specified value]
# 7 LMSE: Laplacian MSE scipy implementation of laplacian is different from
# Matlab's version, especially at the image-borders To significant
# differences between scipy...laplace and Matlab's del2() are:
# a. Matlab del2() divides the convolution result by 4, so the ratio
# (scipy.laplace() result)/(del2()-result) is 4
# b. Matlab does a different kind of processing at the boundaries, so
# the results at the boundaries are different in the 2 calls.
# In Galbally's Matlab code, there is a factor of 4, which I have dropped
# (no difference in result),
# because this is implicit in scipy.ndimage.filters.laplace()
# mode can be 'wrap', 'reflect', 'nearest', 'mirror', or ['constant' with
# a specified value]
op = snf.laplace(refImage, mode='reflect')
opSq = np.square(op)
sum_opSq = np.sum(opSq)
tmp1 = (op - (snf.laplace(testImage, mode='reflect')))
num_op = np.square(tmp1)
lmse06 = float(np.sum(num_op))/float(sum_opSq)
#8 NAE: normalized abs. error
lmse06 = float(np.sum(num_op)) / float(sum_opSq)
# 8 NAE: normalized abs. error
sumRef = np.sum(np.absolute(refImage))
nae07 = float(np.sum(absDiffImg))/float(sumRef)
#9 SNRv: SNR in db
snrv08 = 10.0*np.log10(float(sumRefSq)/float(sumDiffSq))
#10 RAMDv: R-averaged max diff (r=10)
#implementation below is same as what Galbally does in Matlab
r=10
sorted = np.sort(diffImg.flatten())[::-1] #the [::-1] flips the sorted vector, so that it is in descending order
nae07 = float(np.sum(absDiffImg)) / float(sumRef)
# 9 SNRv: SNR in db
snrv08 = 10.0 * np.log10(float(sumRefSq) / float(sumDiffSq))
# 10 RAMDv: R-averaged max diff (r=10)
# implementation below is same as what Galbally does in Matlab
r = 10
# the [::-1] flips the sorted vector, so that it is in descending order
sorted = np.sort(diffImg.flatten())[::-1]
topsum = np.sum(sorted[0:r])
ramdv09 = np.sqrt(float(topsum)/float(r))
#11,12: MAS: Mean Angle Similarity, MAMS: Mean Angle-Magnitude Similarity
mas10, mams11 = angle_similarity(refImage, testImage, diffImg)
ramdv09 = np.sqrt(float(topsum) / float(r))
# 11,12: MAS: Mean Angle Similarity, MAMS: Mean Angle-Magnitude Similarity
mas10, mams11 = angle_similarity(refImage, testImage, diffImg)
fftRef = np.fft.fft2(refImage)
# fftTest = np.fft.fft2(testImage)
#13, 14: SME: spectral magnitude error; SPE: spectral phase error
sme12, spe13 = spectral_similarity(refImage, testImage) #spectralSimilarity(fftRef, fftTest, numPx)
#15 TED: Total edge difference
ted14 = edge_similarity(refImage, testImage)
#16 TCD: Total corner difference
tcd15 = corner_similarity(refImage, testImage)
#17, 18: GME: gradient-magnitude error; GPE: gradient phase error
# fftTest = np.fft.fft2(testImage)
# 13, 14: SME: spectral magnitude error; SPE: spectral phase error
# spectralSimilarity(fftRef, fftTest, numPx)
sme12, spe13 = spectral_similarity(refImage, testImage)
# 15 TED: Total edge difference
# ted14 = edge_similarity(refImage, testImage)
# 16 TCD: Total corner difference
# tcd15 = corner_similarity(refImage, testImage)
# 17, 18: GME: gradient-magnitude error; GPE: gradient phase error
gme16, gpe17 = gradient_similarity(refImage, testImage)
#19 SSIM
# 19 SSIM
ssim18, _ = ssim(refImage, testImage)
#20 VIF
# 20 VIF
vif19 = vif(refImage, testImage)
#21,22,23,24,25: RRED, BIQI, JQI, NIQE: these parameters are not computed here.
#26 HLFI: high-low frequency index (implemented as done by Galbally in Matlab).
hlfi25=high_low_freq_index(fftRef, refImage.shape[1])
return np.asarray((mse00, psnr01, ad02, sc03, nk04, md05, lmse06, nae07, snrv08, ramdv09, mas10, mams11, sme12, gme16, gpe17, ssim18, vif19, hlfi25), dtype=np.float32)
# 21,22,23,24,25: RRED, BIQI, JQI, NIQE: these parameters are not computed
# here.
# 26 HLFI: high-low frequency index (implemented as done by Galbally in
# Matlab).
hlfi25 = high_low_freq_index(fftRef, refImage.shape[1])
return np.asarray(
(mse00,
psnr01,
ad02,
sc03,
nk04,
md05,
lmse06,
nae07,
snrv08,
ramdv09,
mas10,
mams11,
sme12,
gme16,
gpe17,
ssim18,
vif19,
hlfi25),
dtype=np.float32)
"""
Matlab-like RGB to gray...
"""
def matlab_rgb2gray(rgbImage):
'''converts color rgbImage to gray to produce exactly the same result as Matlab would.
def matlab_rgb2gray(rgbImage):
'''converts color rgbImage to gray to produce exactly the same result as
Matlab would.
Inputs:
rgbimage: numpy array of shape [3, height, width]
Return:
numpy array of shape [height, width] containing a gray-image with floating-point pixel values, in the range[(16.0/255) .. (235.0/255)]
numpy array of shape [height, width] containing a gray-image with floating-
point pixel values, in the range[(16.0/255) .. (235.0/255)]
'''
#g1 = 0.299*rgbFrame[0,:,:] + 0.587*rgbFrame[1,:,:] + 0.114*rgbFrame[2,:,:] #standard coeffs CCIR601
#this is how it's done in matlab...
# g1 = 0.299*rgbFrame[0,:,:] + 0.587*rgbFrame[1,:,:] +
# 0.114*rgbFrame[2,:,:] #standard coeffs CCIR601
# this is how it's done in matlab...
rgbImage = rgbImage / 255.0
C0 = 65.481/255.0
C1 = 128.553/255.0
C2 = 24.966/255.0
scaleMin = 16.0/255.0
#scaleMax = 235.0/255.0
gray = scaleMin + (C0*rgbImage[0,:,:] + C1*rgbImage[1,:,:] + C2*rgbImage[2,:,:])
C0 = 65.481 / 255.0
C1 = 128.553 / 255.0
C2 = 24.966 / 255.0
scaleMin = 16.0 / 255.0
# scaleMax = 235.0/255.0
gray = scaleMin + \
(C0 * rgbImage[0, :, :] + C1 * rgbImage[1, :, :] +
C2 * rgbImage[2, :, :])
return gray
"""
SSIM: Structural Similarity index between two gray-level images. The dynamic range is assumed to be 0..255.
Ref:Z. Wang, A.C. Bovik, H.R. Sheikh and E.P. Simoncelli:
SSIM: Structural Similarity index between two gray-level images. The dynamic
range is assumed to be 0..255.
Ref:Z. Wang, A.C. Bovik, H.R. Sheikh and E.P. Simoncelli:
"Image Quality Assessment: From error measurement to Structural Similarity"
IEEE Trans. on Image Processing, 13(1), 01/2004
@param refImage: 2D numpy array (reference image)
@param testImage: 2D numpy array (test image)
Both input images should have the same dimensions. This is assumed, and not verified in this function
@return ssim: float-scalar. The mean structural similarity between the 2 input images.
@return ssim_map: the SSIM index map of the test image (this map is smaller than the test image).
Both input images should have the same dimensions. This is assumed, and not
verified in this function
@return ssim: float-scalar. The mean structural similarity between the 2
input images.
@return ssim_map: the SSIM index map of the test image (this map is smaller
than the test image).
"""
def ssim(refImage, testImage):
"""Compute and return SSIM between two images.
......@@ -241,565 +297,593 @@ def ssim(refImage, testImage):
Returns:
Returns ssim and ssim_map
ssim: float-scalar. The mean structural similarity between the 2 input images.
ssim_map: the SSIM index map of the test image (this map is smaller than the test image).
ssim: float-scalar. The mean structural similarity between the 2 input
images.
ssim_map: the SSIM index map of the test image (this map is smaller than
the test image).
"""
M=refImage.shape[0]
N=refImage.shape[1]
winSz=11 #window size for gaussian filter
winSgm = 1.5 # sigma for gaussian filter
#input image should be at least 11x11 in size.
if(M<winSz) or (N<winSz):
M = refImage.shape[0]
N = refImage.shape[1]
winSz = 11 # window size for gaussian filter
winSgm = 1.5 # sigma for gaussian filter
# input image should be at least 11x11 in size.
if(M < winSz) or (N < winSz):
ssim_index = -np.inf
ssim_map = -np.inf
return ssim_index, ssim_map
#construct the gaussian filter
# construct the gaussian filter
gwin = gauss_2d((winSz, winSz), winSgm)
K1 = 0.01 # constants taken from the initial matlab implementation provided by Bovik's lab.
# constants taken from the initial matlab implementation provided by
# Bovik's lab.
K1 = 0.01
K2 = 0.03
L = 255 #dynamic range.
C1 = (K1*L)*(K1*L)
C2 = (K2*L)*(K2*L)
#refImage=refImage.astype(np.float)
#testImage=testImage.astype(np.float)
#ssg is scipy.signal
L = 255 # dynamic range.
C1 = (K1 * L) * (K1 * L)
C2 = (K2 * L) * (K2 * L)
# refImage=refImage.astype(np.float)
# testImage=testImage.astype(np.float)
# ssg is scipy.signal
mu1 = ssg.convolve2d(refImage, gwin, mode='valid')
mu2 = ssg.convolve2d(testImage, gwin, mode='valid')
mu1Sq = mu1*mu1
mu2Sq = mu2*mu2
mu1_mu2 = mu1*mu2
sigma1_sq = ssg.convolve2d((refImage*refImage), gwin, mode='valid') - mu1Sq
sigma2_sq = ssg.convolve2d((testImage*testImage), gwin, mode='valid') - mu1Sq
sigma12 = ssg.convolve2d((refImage*testImage), gwin, mode='valid') - mu1_mu2
assert (C1>0 and C2 > 0), "Conditions for computing ssim with this code are not met. Set the Ks and L to values > 0."
num1 = (2.0*mu1_mu2 + C1) *(2.0*sigma12 + C2)
den1 = (mu1Sq + mu2Sq+C1)*(sigma1_sq + sigma2_sq +C2)
ssim_map = num1/den1
mu1Sq = mu1 * mu1
mu2Sq = mu2 * mu2
mu1_mu2 = mu1 * mu2
sigma1_sq = ssg.convolve2d(
(refImage * refImage),
gwin,
mode='valid') - mu1Sq
sigma2_sq = ssg.convolve2d(
(testImage * testImage),
gwin,
mode='valid') - mu1Sq
sigma12 = ssg.convolve2d(
(refImage * testImage),
gwin,
mode='valid') - mu1_mu2
assert (C1 > 0 and C2 > 0), "Conditions for computing ssim with this "
"code are not met. Set the Ks and L to values > 0."
num1 = (2.0 * mu1_mu2 + C1) * (2.0 * sigma12 + C2)
den1 = (mu1Sq + mu2Sq + C1) * (sigma1_sq + sigma2_sq + C2)
ssim_map = num1 / den1
ssim = np.average(ssim_map)
return ssim, ssim_map
"""
VIF: Visual Information Fidelity measure.
Ref: H.R. Sheikh and A.C. Bovik: "Image Information and Visual Quality", IEEE Trans. Image Processing.
Adapted from Galbally's matlab code, which was provided by Bovik et al's LIVE lab.
@param refImage: 2D numpy array (reference image)
@param testImage: 2D numpy array (test image)
Both input images should have the same dimensions. This is assumed, and not verified in this function
@return vifp: float-scalar. Measure of visual information fidelity between the 2 input images
"""
def vif(refImage, testImage):
"""
VIF: Visual Information Fidelity measure.
Ref: H.R. Sheikh and A.C. Bovik: "Image Information and Visual Quality",
IEEE Trans. Image Processing. Adapted from Galbally's matlab code, which
was provided by Bovik et al's LIVE lab.
@param refImage: 2D numpy array (reference image)
@param testImage: 2D numpy array (test image)
Both input images should have the same dimensions. This is assumed, and
not verified in this function
@return vifp: float-scalar. Measure of visual information fidelity
between the 2 input images
"""
sigma_nsq = 2.0
num=0
den=0
#sc is scale, taking values (1,2,3,4)
for sc in range(1,5):
N=(2**(4-sc+1))+1
#print(N, sc)
win = gauss_2d((N,N), (float(N)/5.0))
#ssg is scipy.signal
if sc > 1 :
num = 0
den = 0
# sc is scale, taking values (1,2,3,4)
for sc in range(1, 5):
N = (2**(4 - sc + 1)) + 1
win = gauss_2d((N, N), (float(N) / 5.0))
if sc > 1:
refImage = ssg.convolve2d(refImage, win, mode='valid')
testImage = ssg.convolve2d(testImage, win, mode='valid')
refImage = refImage[::2, ::2] #downsample by factor 2 in each direction
# downsample by factor 2 in each direction
refImage = refImage[::2, ::2]
testImage = testImage[::2, ::2]
mu1 = ssg.convolve2d(refImage, win, mode='valid')
mu2 = ssg.convolve2d(testImage, win, mode='valid')
mu1Sq = mu1*mu1
mu2Sq = mu2*mu2
mu1_mu2 = mu1*mu2
sigma1_sq = ssg.convolve2d((refImage*refImage), win, mode='valid') - mu1Sq
sigma2_sq = ssg.convolve2d((testImage*testImage), win, mode='valid') - mu2Sq
sigma12 = ssg.convolve2d((refImage*testImage), win, mode='valid') - mu1_mu2
sigma1_sq[sigma1_sq<0]=0 #set negative filter responses to 0.
sigma2_sq[sigma2_sq<0]=0
mu1Sq = mu1 * mu1
mu2Sq = mu2 * mu2
mu1_mu2 = mu1 * mu2
sigma1_sq = ssg.convolve2d(
(refImage * refImage), win, mode='valid') - mu1Sq
sigma2_sq = ssg.convolve2d(
(testImage * testImage), win, mode='valid') - mu2Sq
sigma12 = ssg.convolve2d(
(refImage * testImage),
win,
mode='valid') - mu1_mu2
sigma1_sq[sigma1_sq < 0] = 0 # set negative filter responses to 0.
sigma2_sq[sigma2_sq < 0] = 0
g = sigma12 / (sigma1_sq + 1e-10)
sv_sq = sigma2_sq - g*sigma12;
g[(sigma1_sq < 1e-10)]=0
sv_sq = sigma2_sq - g * sigma12
g[(sigma1_sq < 1e-10)] = 0
sv_sq[sigma1_sq < 1e-10] = sigma2_sq[sigma1_sq < 1e-10]
sigma1_sq[sigma1_sq<1e-10]=0
g[(sigma2_sq < 1e-10)]=0
sv_sq[sigma2_sq < 1e-10] =0
sv_sq[g<0]=sigma2_sq[g<0]
g[g<0]=0
sv_sq[sv_sq <= 1e-10] = 1e-10 #sic. As implemented in the original matlab version...
m1 = g*g*sigma1_sq
m2 = sv_sq+sigma_nsq
m3 = np.log10(1 + m1/m2)
m4 = np.log10(1 + (sigma1_sq/sigma_nsq))
sigma1_sq[sigma1_sq < 1e-10] = 0
g[(sigma2_sq < 1e-10)] = 0
sv_sq[sigma2_sq < 1e-10] = 0
sv_sq[g < 0] = sigma2_sq[g < 0]
g[g < 0] = 0
# sic. As implemented in the original matlab version...
sv_sq[sv_sq <= 1e-10] = 1e-10
m1 = g * g * sigma1_sq
m2 = sv_sq + sigma_nsq
m3 = np.log10(1 + m1 / m2)
m4 = np.log10(1 + (sigma1_sq / sigma_nsq))
num += np.sum(m3)
den += np.sum(m4)
vifp = num/den
vifp = num / den
return vifp
"""
HLFI: relative difference between high- and low-frequency energy in image.
Ref: I. Avcibas, N. Memon, B. Sankur: "Steganalysis using image quality metrics", IEEE Trans. Image Processing, 12, 2003.
@param imgFFT: 2D numpy array of complex numbers, representing Fourier transform of test image.
@param ncols: int. Number of columns in image.
@return float-scalar.
"""
def high_low_freq_index(imgFFT, ncols):
"""
HLFI: relative difference between high- and low-frequency energy in image.
Ref: I. Avcibas, N. Memon, B. Sankur: "Steganalysis using image quality
metrics", IEEE Trans. Image Processing, 12, 2003.
@param imgFFT: 2D numpy array of complex numbers, representing Fourier
transform of test image.
@param ncols: int. Number of columns in image.
@return float-scalar.
"""
N= ncols
colHalf = int(round(N/2)) # (N/2) + (N % 2) #round it up
N = ncols
colHalf = int(round(N /