1 # Copyright 2016 The Android Open Source Project 2 # 3 # Licensed under the Apache License, Version 2.0 (the "License"); 4 # you may not use this file except in compliance with the License. 5 # You may obtain a copy of the License at 6 # 7 # http://www.apache.org/licenses/LICENSE-2.0 8 # 9 # Unless required by applicable law or agreed to in writing, software 10 # distributed under the License is distributed on an "AS IS" BASIS, 11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 # See the License for the specific language governing permissions and 13 # limitations under the License. 14 15 import its.image 16 import its.caps 17 import its.device 18 import its.objects 19 import os.path 20 import numpy 21 import cv2 22 import math 23 24 25 def main(): 26 """ Test that the lens shading correction is applied appropriately, and 27 color of a monochrome uniform scene is evenly distributed, for example, 28 when a diffuser is placed in front of the camera. 29 Perform this test on a yuv frame with auto 3a. Lens shading is evaluated 30 based on the y channel. Measure the average y value for each sample block 31 specified, and then determine pass/fail by comparing with the center y 32 value. 33 The color uniformity test is evaluated in r/g and b/g space. At specified 34 radius of the image, the variance of r/g and b/g value need to be less than 35 a threshold in order to pass the test. 36 """ 37 NAME = os.path.basename(__file__).split(".")[0] 38 # Sample block center location and length 39 Num_radius = 8 40 spb_r = 1/2./(Num_radius*2-1) 41 SPB_CT_LIST = numpy.arange(spb_r, 1/2., spb_r*2) 42 43 # Threshold for pass/fail 44 THRES_LS_CT = 0.9 # len shading allowance for center 45 THRES_LS_CN = 0.6 # len shading allowance for corner 46 THRES_LS_HIGH = 0.2 # max allowed percentage for a patch to be brighter 47 # than center 48 THRES_UFMT = 0.1 # uniformity allowance 49 # Drawing color 50 RED = (1, 0, 0) # blocks failed the test 51 GREEN = (0, 0.7, 0.3) # blocks passed the test 52 53 with its.device.ItsSession() as cam: 54 props = cam.get_camera_properties() 55 its.caps.skip_unless(its.caps.ae_lock(props) and 56 its.caps.awb_lock(props)) 57 if its.caps.read_3a(props): 58 # Converge 3A and get the estimates. 59 sens, exp, gains, xform, focus = cam.do_3a(get_results=True, 60 do_af=False, 61 lock_ae=True, 62 lock_awb=True) 63 print "AE sensitivity %d, exposure %dms" % (sens, exp / 1000000.0) 64 print "AWB gains", gains 65 print "AWB transform", xform 66 print "AF distance", focus 67 req = its.objects.auto_capture_request() 68 img_size = its.objects.get_available_output_sizes("yuv", props) 69 w = img_size[0][0] 70 h = img_size[0][1] 71 out_surface = {"format": "yuv"} 72 cap = cam.do_capture(req, out_surface) 73 print "Captured yuv %dx%d" % (w, h) 74 # rgb image 75 img_rgb = its.image.convert_capture_to_rgb_image(cap) 76 img_g_pos = img_rgb[:, :, 1] + 0.001 # in case g channel is zero. 77 r_g = img_rgb[:, :, 0] / img_g_pos 78 b_g = img_rgb[:, :, 2] / img_g_pos 79 # y channel 80 img_y = its.image.convert_capture_to_planes(cap)[0] 81 its.image.write_image(img_y, "%s_y_plane.png" % NAME, True) 82 83 # Evaluation begins 84 # image with legend 85 img_legend_ls = numpy.copy(img_rgb) 86 img_legend_ufmt = numpy.copy(img_rgb) 87 line_width = max(2, int(max(h, w)/500)) # line width of legend 88 font_scale = line_width / 7.0 # font scale of the basic font size 89 text_height = cv2.getTextSize('gf', cv2.FONT_HERSHEY_SIMPLEX, 90 font_scale, line_width)[0][1] 91 text_offset = int(text_height*1.5) 92 93 # center block average Y value, r/g, and b/g 94 top = int((0.5-spb_r)*h) 95 bottom = int((0.5+spb_r)*h) 96 left = int((0.5-spb_r)*w) 97 right = int((0.5+spb_r)*w) 98 center_y = numpy.mean(img_y[top:bottom, left:right]) 99 center_r_g = numpy.mean(r_g[top:bottom, left:right]) 100 center_b_g = numpy.mean(b_g[top:bottom, left:right]) 101 # add legend to lens Shading figure 102 cv2.rectangle(img_legend_ls, (left, top), (right, bottom), GREEN, 103 line_width) 104 draw_legend(img_legend_ls, ["Y: %.2f" % center_y], 105 [left+text_offset, bottom-text_offset], 106 font_scale, text_offset, GREEN, int(line_width/2)) 107 # add legend to color uniformity figure 108 cv2.rectangle(img_legend_ufmt, (left, top), (right, bottom), GREEN, 109 line_width) 110 texts = ["r/g: %.2f" % center_r_g, 111 "b/g: %.2f" % center_b_g] 112 draw_legend(img_legend_ufmt, texts, 113 [left+text_offset, bottom-text_offset*2], 114 font_scale, text_offset, GREEN, int(line_width/2)) 115 116 # evaluate y and r/g, b/g for each block 117 ls_test_failed = [] 118 cu_test_failed = [] 119 ls_thres_h = center_y * (1 + THRES_LS_HIGH) 120 dist_max = math.sqrt(pow(w, 2)+pow(h, 2))/2 121 for spb_ct in SPB_CT_LIST: 122 # list sample block center location 123 num_sample = int(numpy.asscalar((1-spb_ct*2)/spb_r/2 + 1)) 124 ct_cord_x = numpy.concatenate( 125 (numpy.arange(spb_ct, 1-spb_ct+spb_r, spb_r*2), 126 spb_ct*numpy.ones((num_sample-1)), 127 (1-spb_ct)*numpy.ones((num_sample-1)), 128 numpy.arange(spb_ct, 1-spb_ct+spb_r, spb_r*2))) 129 ct_cord_y = numpy.concatenate( 130 (spb_ct*numpy.ones(num_sample+1), 131 numpy.arange(spb_ct+spb_r*2, 1-spb_ct, spb_r*2), 132 numpy.arange(spb_ct+spb_r*2, 1-spb_ct, spb_r*2), 133 (1-spb_ct)*numpy.ones(num_sample+1))) 134 135 blocks_info = [] 136 max_r_g = 0 137 min_r_g = float("inf") 138 max_b_g = 0 139 min_b_g = float("inf") 140 for spb_ctx, spb_cty in zip(ct_cord_x, ct_cord_y): 141 top = int((spb_cty-spb_r)*h) 142 bottom = int((spb_cty+spb_r)*h) 143 left = int((spb_ctx-spb_r)*w) 144 right = int((spb_ctx+spb_r)*w) 145 dist_to_img_center = math.sqrt(pow(abs(spb_ctx-0.5)*w, 2) 146 + pow(abs(spb_cty-0.5)*h, 2)) 147 ls_thres_l = ((THRES_LS_CT-THRES_LS_CN)*(1-dist_to_img_center 148 /dist_max)+THRES_LS_CN) * center_y 149 150 # compute block average value 151 block_y = numpy.mean(img_y[top:bottom, left:right]) 152 block_r_g = numpy.mean(r_g[top:bottom, left:right]) 153 block_b_g = numpy.mean(b_g[top:bottom, left:right]) 154 max_r_g = max(max_r_g, block_r_g) 155 min_r_g = min(min_r_g, block_r_g) 156 max_b_g = max(max_b_g, block_b_g) 157 min_b_g = min(min_b_g, block_b_g) 158 blocks_info.append({"pos": [top, bottom, left, right], 159 "block_r_g": block_r_g, 160 "block_b_g": block_b_g}) 161 # check lens shading and draw legend 162 if block_y > ls_thres_h or block_y < ls_thres_l: 163 ls_test_failed.append({"pos": [top, bottom, left, 164 right], 165 "val": block_y, 166 "thres_l": ls_thres_l}) 167 legend_color = RED 168 else: 169 legend_color = GREEN 170 text_bottom = bottom - text_offset 171 cv2.rectangle(img_legend_ls, (left, top), (right, bottom), 172 legend_color, line_width) 173 draw_legend(img_legend_ls, ["Y: %.2f" % block_y], 174 [left+text_offset, text_bottom], font_scale, 175 text_offset, legend_color, int(line_width/2)) 176 177 # check color uniformity and draw legend 178 ufmt_r_g = (max_r_g-min_r_g) / center_r_g 179 ufmt_b_g = (max_b_g-min_b_g) / center_b_g 180 if ufmt_r_g > THRES_UFMT or ufmt_b_g > THRES_UFMT: 181 cu_test_failed.append({"pos": spb_ct, 182 "ufmt_r_g": ufmt_r_g, 183 "ufmt_b_g": ufmt_b_g}) 184 legend_color = RED 185 else: 186 legend_color = GREEN 187 for block in blocks_info: 188 top, bottom, left, right = block["pos"] 189 cv2.rectangle(img_legend_ufmt, (left, top), (right, bottom), 190 legend_color, line_width) 191 texts = ["r/g: %.2f" % block["block_r_g"], 192 "b/g: %.2f" % block["block_b_g"]] 193 text_bottom = bottom - text_offset * 2 194 draw_legend(img_legend_ufmt, texts, 195 [left+text_offset, text_bottom], font_scale, 196 text_offset, legend_color, int(line_width/2)) 197 198 # Save images 199 its.image.write_image(img_legend_ufmt, 200 "%s_color_uniformity_result.png" % NAME, True) 201 its.image.write_image(img_legend_ls, 202 "%s_lens_shading_result.png" % NAME, True) 203 204 # print results 205 lens_shading_test_passed = True 206 color_uniformity_test_passed = True 207 if len(ls_test_failed) > 0: 208 lens_shading_test_passed = False 209 print "\nLens shading test summary" 210 print "Center block average Y value: %.3f" % center_y 211 print "Blocks failed in the lens shading test:" 212 for block in ls_test_failed: 213 top, bottom, left, right = block["pos"] 214 print "Block position: [top: %d, bottom: %d, left: %d, right: "\ 215 "%d]; average Y value: %.3f; valid value range: %.3f ~ " \ 216 "%.3f" % (top, bottom, left, right, block["val"], 217 block["thres_l"], ls_thres_h) 218 if len(cu_test_failed) > 0: 219 color_uniformity_test_passed = False 220 print "\nColor uniformity test summary" 221 print "Valid color uniformity value range: 0 ~ ", THRES_UFMT 222 print "Areas that failed the color uniformity test:" 223 for rd in cu_test_failed: 224 print "Radius position: %.3f; r/g uniformity: %.3f; b/g " \ 225 "uniformity: %.3f" % (rd["pos"], rd["ufmt_r_g"], 226 rd["ufmt_b_g"]) 227 assert lens_shading_test_passed 228 assert color_uniformity_test_passed 229 230 231 def draw_legend(img, texts, text_org, font_scale, text_offset, legend_color, 232 line_width): 233 """ Draw legend on an image. 234 235 Args: 236 img: Numpy float image array in RGB, with pixel values in [0,1]. 237 texts: list of legends. Each element in the list is a line of legend. 238 text_org: tuple of the bottom left corner of the text position in 239 pixels, horizontal and vertical. 240 font_scale: float number. Font scale of the basic font size. 241 text_offset: text line width in pixels. 242 legend_color: text color in rgb value. 243 line_width: strokes width in pixels. 244 """ 245 for text in texts: 246 cv2.putText(img, text, (text_org[0], text_org[1]), 247 cv2.FONT_HERSHEY_SIMPLEX, font_scale, 248 legend_color, line_width) 249 text_org[1] += text_offset 250 251 252 if __name__ == '__main__': 253 main() 254