-
Notifications
You must be signed in to change notification settings - Fork 11.7k
/
Copy pathbrisquequality.py
206 lines (165 loc) · 7.67 KB
/
brisquequality.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
import cv2
import numpy as np
import math as m
import sys
# for gamma function, called
from scipy.special import gamma as tgamma
import os
# import svm functions (from libsvm library)
# if python2.x version : import svm from libsvm (sudo apt-get install python-libsvm)
if sys.version_info[0] < 3:
import svm
import svmutil
from svmutil import *
from svm import *
else:
# if python 3.x version
# make sure the file is in libsvm/python folder
import svm
import svmutil
from svm import *
from svmutil import *
# AGGD fit model, takes input as the MSCN Image / Pair-wise Product
def AGGDfit(structdis):
# variables to count positive pixels / negative pixels and their squared sum
poscount = 0
negcount = 0
possqsum = 0
negsqsum = 0
abssum = 0
poscount = len(structdis[structdis > 0]) # number of positive pixels
negcount = len(structdis[structdis < 0]) # number of negative pixels
# calculate squared sum of positive pixels and negative pixels
possqsum = np.sum(np.power(structdis[structdis > 0], 2))
negsqsum = np.sum(np.power(structdis[structdis < 0], 2))
# absolute squared sum
abssum = np.sum(structdis[structdis > 0]) + np.sum(-1 * structdis[structdis < 0])
# calculate left sigma variance and right sigma variance
lsigma_best = np.sqrt((negsqsum/negcount))
rsigma_best = np.sqrt((possqsum/poscount))
gammahat = lsigma_best/rsigma_best
# total number of pixels - totalcount
totalcount = structdis.shape[1] * structdis.shape[0]
rhat = m.pow(abssum/totalcount, 2)/((negsqsum + possqsum)/totalcount)
rhatnorm = rhat * (m.pow(gammahat, 3) + 1) * (gammahat + 1)/(m.pow(m.pow(gammahat, 2) + 1, 2))
prevgamma = 0
prevdiff = 1e10
sampling = 0.001
gam = 0.2
# vectorized function call for best fitting parameters
vectfunc = np.vectorize(func, otypes = [np.float], cache = False)
# calculate best fit params
gamma_best = vectfunc(gam, prevgamma, prevdiff, sampling, rhatnorm)
return [lsigma_best, rsigma_best, gamma_best]
def func(gam, prevgamma, prevdiff, sampling, rhatnorm):
while(gam < 10):
r_gam = tgamma(2/gam) * tgamma(2/gam) / (tgamma(1/gam) * tgamma(3/gam))
diff = abs(r_gam - rhatnorm)
if(diff > prevdiff): break
prevdiff = diff
prevgamma = gam
gam += sampling
gamma_best = prevgamma
return gamma_best
def compute_features(img):
scalenum = 2
feat = []
# make a copy of the image
im_original = img.copy()
# scale the images twice
for itr_scale in range(scalenum):
im = im_original.copy()
# normalize the image
im = im / 255.0
# calculating MSCN coefficients
mu = cv2.GaussianBlur(im, (7, 7), 1.166)
mu_sq = mu * mu
sigma = cv2.GaussianBlur(im*im, (7, 7), 1.166)
sigma = (sigma - mu_sq)**0.5
# structdis is the MSCN image
structdis = im - mu
structdis /= (sigma + 1.0/255)
# calculate best fitted parameters from MSCN image
best_fit_params = AGGDfit(structdis)
# unwrap the best fit parameters
lsigma_best = best_fit_params[0]
rsigma_best = best_fit_params[1]
gamma_best = best_fit_params[2]
# append the best fit parameters for MSCN image
feat.append(gamma_best)
feat.append((lsigma_best*lsigma_best + rsigma_best*rsigma_best)/2)
# shifting indices for creating pair-wise products
shifts = [[0,1], [1,0], [1,1], [-1,1]] # H V D1 D2
for itr_shift in range(1, len(shifts) + 1):
OrigArr = structdis
reqshift = shifts[itr_shift-1] # shifting index
# create transformation matrix for warpAffine function
M = np.float32([[1, 0, reqshift[1]], [0, 1, reqshift[0]]])
ShiftArr = cv2.warpAffine(OrigArr, M, (structdis.shape[1], structdis.shape[0]))
Shifted_new_structdis = ShiftArr
Shifted_new_structdis = Shifted_new_structdis * structdis
# shifted_new_structdis is the pairwise product
# best fit the pairwise product
best_fit_params = AGGDfit(Shifted_new_structdis)
lsigma_best = best_fit_params[0]
rsigma_best = best_fit_params[1]
gamma_best = best_fit_params[2]
constant = m.pow(tgamma(1/gamma_best), 0.5)/m.pow(tgamma(3/gamma_best), 0.5)
meanparam = (rsigma_best - lsigma_best) * (tgamma(2/gamma_best)/tgamma(1/gamma_best)) * constant
# append the best fit calculated parameters
feat.append(gamma_best) # gamma best
feat.append(meanparam) # mean shape
feat.append(m.pow(lsigma_best, 2)) # left variance square
feat.append(m.pow(rsigma_best, 2)) # right variance square
# resize the image on next iteration
im_original = cv2.resize(im_original, (0,0), fx=0.5, fy=0.5, interpolation=cv2.INTER_CUBIC)
return feat
# function to calculate BRISQUE quality score
# takes input of the image path
def test_measure_BRISQUE(imgPath):
# read image from given path
dis = cv2.imread(imgPath, 1)
if(dis is None):
print("Wrong image path given")
print("Exiting...")
sys.exit(0)
# convert to gray scale
dis = cv2.cvtColor(dis, cv2.COLOR_BGR2GRAY)
# compute feature vectors of the image
features = compute_features(dis)
# rescale the brisqueFeatures vector from -1 to 1
x = [0]
# pre loaded lists from C++ Module to rescale brisquefeatures vector to [-1, 1]
min_= [0.336999 ,0.019667 ,0.230000 ,-0.125959 ,0.000167 ,0.000616 ,0.231000 ,-0.125873 ,0.000165 ,0.000600 ,0.241000 ,-0.128814 ,0.000179 ,0.000386 ,0.243000 ,-0.133080 ,0.000182 ,0.000421 ,0.436998 ,0.016929 ,0.247000 ,-0.200231 ,0.000104 ,0.000834 ,0.257000 ,-0.200017 ,0.000112 ,0.000876 ,0.257000 ,-0.155072 ,0.000112 ,0.000356 ,0.258000 ,-0.154374 ,0.000117 ,0.000351]
max_= [9.999411, 0.807472, 1.644021, 0.202917, 0.712384, 0.468672, 1.644021, 0.169548, 0.713132, 0.467896, 1.553016, 0.101368, 0.687324, 0.533087, 1.554016, 0.101000, 0.689177, 0.533133, 3.639918, 0.800955, 1.096995, 0.175286, 0.755547, 0.399270, 1.095995, 0.155928, 0.751488, 0.402398, 1.041992, 0.093209, 0.623516, 0.532925, 1.042992, 0.093714, 0.621958, 0.534484]
# append the rescaled vector to x
for i in range(0, 36):
min = min_[i]
max = max_[i]
x.append(-1 + (2.0/(max - min) * (features[i] - min)))
# load model
model = svmutil.svm_load_model("allmodel")
# create svm node array from python list
x, idx = gen_svm_nodearray(x[1:], isKernel=(model.param.kernel_type == PRECOMPUTED))
x[36].index = -1 # set last index to -1 to indicate the end.
# get important parameters from model
svm_type = model.get_svm_type()
is_prob_model = model.is_probability_model()
nr_class = model.get_nr_class()
if svm_type in (ONE_CLASS, EPSILON_SVR, NU_SVC):
# here svm_type is EPSILON_SVR as it's regression problem
nr_classifier = 1
dec_values = (c_double * nr_classifier)()
# calculate the quality score of the image using the model and svm_node_array
qualityscore = svmutil.libsvm.svm_predict_probability(model, x, dec_values)
return qualityscore
# exit if input argument not given
if(len(sys.argv) != 2):
print("Please give input argument of the image path.")
print("Arguments expected: <image_path>")
print("--------------------------------")
print("Exiting")
sys.exit(0)
# calculate quality score
qualityscore = test_measure_BRISQUE(sys.argv[1])
print ("Score of the given image: {}".format(qualityscore))