-
Notifications
You must be signed in to change notification settings - Fork 28
Expand file tree
/
Copy pathcompute.py
More file actions
211 lines (149 loc) · 7.29 KB
/
compute.py
File metadata and controls
211 lines (149 loc) · 7.29 KB
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
207
208
209
210
211
# coding: utf-8
from __future__ import (
absolute_import,
division,
print_function,
unicode_literals
)
from math import atan2, pi
from sys import argv
import numpy as np
from imageio import imread
from skimage.feature import canny
from cpbd.octave import sobel
# threshold to characterize blocks as edge/non-edge blocks
THRESHOLD = 0.002
# fitting parameter
BETA = 3.6
# block size
BLOCK_HEIGHT, BLOCK_WIDTH = (64, 64)
# just noticeable widths based on the perceptual experiments
WIDTH_JNB = np.concatenate([5*np.ones(51), 3*np.ones(205)])
def compute(image):
# type: (numpy.ndarray) -> float
"""Compute the sharpness metric for the given data."""
# convert the image to double for further processing
image = image.astype(np.float64)
# edge detection using canny and sobel canny edge detection is done to
# classify the blocks as edge or non-edge blocks and sobel edge
# detection is done for the purpose of edge width measurement.
canny_edges = canny(image)
sobel_edges = sobel(image)
# edge width calculation
marziliano_widths = marziliano_method(sobel_edges, image)
# sharpness metric calculation
return _calculate_sharpness_metric(image, canny_edges, marziliano_widths)
def marziliano_method(edges, image):
# type: (numpy.ndarray, numpy.ndarray) -> numpy.ndarray
"""
Calculate the widths of the given edges.
:return: A matrix with the same dimensions as the given image with 0's at
non-edge locations and edge-widths at the edge locations.
"""
# `edge_widths` consists of zero and non-zero values. A zero value
# indicates that there is no edge at that position and a non-zero value
# indicates that there is an edge at that position and the value itself
# gives the edge width.
edge_widths = np.zeros(image.shape)
# find the gradient for the image
gradient_y, gradient_x = np.gradient(image)
# dimensions of the image
img_height, img_width = image.shape
# holds the angle information of the edges
edge_angles = np.zeros(image.shape)
# calculate the angle of the edges
for row in range(img_height):
for col in range(img_width):
if gradient_x[row, col] != 0:
edge_angles[row, col] = atan2(gradient_y[row, col], gradient_x[row, col]) * (180 / pi)
elif gradient_x[row, col] == 0 and gradient_y[row, col] == 0:
edge_angles[row,col] = 0
elif gradient_x[row, col] == 0 and gradient_y[row, col] == pi/2:
edge_angles[row, col] = 90
if np.any(edge_angles):
# quantize the angle
quantized_angles = 45 * np.round(edge_angles / 45)
for row in range(1, img_height - 1):
for col in range(1, img_width - 1):
if edges[row, col] == 1:
# gradient angle = 180 or -180
if quantized_angles[row, col] == 180 or quantized_angles[row, col] == -180:
for margin in range(100 + 1):
inner_border = (col - 1) - margin
outer_border = (col - 2) - margin
# outside image or intensity increasing from left to right
if outer_border < 0 or (image[row, outer_border] - image[row, inner_border]) <= 0:
break
width_left = margin + 1
for margin in range(100 + 1):
inner_border = (col + 1) + margin
outer_border = (col + 2) + margin
# outside image or intensity increasing from left to right
if outer_border >= img_width or (image[row, outer_border] - image[row, inner_border]) >= 0:
break
width_right = margin + 1
edge_widths[row, col] = width_left + width_right
# gradient angle = 0
if quantized_angles[row, col] == 0:
for margin in range(100 + 1):
inner_border = (col - 1) - margin
outer_border = (col - 2) - margin
# outside image or intensity decreasing from left to right
if outer_border < 0 or (image[row, outer_border] - image[row, inner_border]) >= 0:
break
width_left = margin + 1
for margin in range(100 + 1):
inner_border = (col + 1) + margin
outer_border = (col + 2) + margin
# outside image or intensity decreasing from left to right
if outer_border >= img_width or (image[row, outer_border] - image[row, inner_border]) <= 0:
break
width_right = margin + 1
edge_widths[row, col] = width_right + width_left
return edge_widths
def _calculate_sharpness_metric(image, edges, edge_widths):
# type: (numpy.array, numpy.array, numpy.array) -> numpy.float64
# get the size of image
img_height, img_width = image.shape
total_num_edges = 0
hist_pblur = np.zeros(101)
# maximum block indices
num_blocks_vertically = int(img_height / BLOCK_HEIGHT)
num_blocks_horizontally = int(img_width / BLOCK_WIDTH)
# loop over the blocks
for i in range(num_blocks_vertically):
for j in range(num_blocks_horizontally):
# get the row and col indices for the block pixel positions
rows = slice(BLOCK_HEIGHT * i, BLOCK_HEIGHT * (i + 1))
cols = slice(BLOCK_WIDTH * j, BLOCK_WIDTH * (j + 1))
if is_edge_block(edges[rows, cols], THRESHOLD):
block_widths = edge_widths[rows, cols]
# rotate block to simulate column-major boolean indexing
block_widths = np.rot90(np.flipud(block_widths), 3)
block_widths = block_widths[block_widths != 0]
block_contrast = get_block_contrast(image[rows, cols])
block_jnb = WIDTH_JNB[block_contrast]
# calculate the probability of blur detection at the edges
# detected in the block
prob_blur_detection = 1 - np.exp(-abs(block_widths/block_jnb) ** BETA)
# update the statistics using the block information
for probability in prob_blur_detection:
bucket = int(round(probability * 100))
hist_pblur[bucket] += 1
total_num_edges += 1
# normalize the pdf
if total_num_edges > 0:
hist_pblur = hist_pblur / total_num_edges
# calculate the sharpness metric
return np.sum(hist_pblur[:64])
def is_edge_block(block, threshold):
# type: (numpy.ndarray, float) -> bool
"""Decide whether the given block is an edge block."""
return np.count_nonzero(block) > (block.size * threshold)
def get_block_contrast(block):
# type: (numpy.ndarray) -> int
return int(np.max(block) - np.min(block))
if __name__ == '__main__':
input_image = imread(argv[1], pilmode = 'L')
sharpness = compute(input_image)
print('CPBD sharpness for %s: %f' % (argv[1], sharpness))