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 if its.caps.read_3a(props): 56 # Converge 3A and get the estimates. 57 sens, exp, gains, xform, focus = cam.do_3a(get_results=True, 58 do_af=False, 59 lock_ae=True, 60 lock_awb=True) 61 print "AE sensitivity %d, exposure %dms" % (sens, exp / 1000000.0) 62 print "AWB gains", gains 63 print "AWB transform", xform 64 print "AF distance", focus 65 req = its.objects.auto_capture_request() 66 img_size = its.objects.get_available_output_sizes("yuv", props) 67 w = img_size[0][0] 68 h = img_size[0][1] 69 out_surface = {"format": "yuv"} 70 cap = cam.do_capture(req, out_surface) 71 print "Captured yuv %dx%d" % (w, h) 72 # rgb image 73 img_rgb = its.image.convert_capture_to_rgb_image(cap) 74 img_g_pos = img_rgb[:, :, 1] + 0.001 # in case g channel is zero. 75 r_g = img_rgb[:, :, 0] / img_g_pos 76 b_g = img_rgb[:, :, 2] / img_g_pos 77 # y channel 78 img_y = its.image.convert_capture_to_planes(cap)[0] 79 its.image.write_image(img_y, "%s_y_plane.png" % NAME, True) 80 81 # Evaluation begins 82 # image with legend 83 img_legend_ls = numpy.copy(img_rgb) 84 img_legend_ufmt = numpy.copy(img_rgb) 85 line_width = max(2, int(max(h, w)/500)) # line width of legend 86 font_scale = line_width / 7.0 # font scale of the basic font size 87 text_height = cv2.getTextSize('gf', cv2.FONT_HERSHEY_SIMPLEX, 88 font_scale, line_width)[0][1] 89 text_offset = int(text_height*1.5) 90 91 # center block average Y value, r/g, and b/g 92 top = int((0.5-spb_r)*h) 93 bottom = int((0.5+spb_r)*h) 94 left = int((0.5-spb_r)*w) 95 right = int((0.5+spb_r)*w) 96 center_y = numpy.mean(img_y[top:bottom, left:right]) 97 center_r_g = numpy.mean(r_g[top:bottom, left:right]) 98 center_b_g = numpy.mean(b_g[top:bottom, left:right]) 99 # add legend to lens Shading figure 100 cv2.rectangle(img_legend_ls, (left, top), (right, bottom), GREEN, 101 line_width) 102 draw_legend(img_legend_ls, ["Y: %.2f" % center_y], 103 [left+text_offset, bottom-text_offset], 104 font_scale, text_offset, GREEN, int(line_width/2)) 105 # add legend to color uniformity figure 106 cv2.rectangle(img_legend_ufmt, (left, top), (right, bottom), GREEN, 107 line_width) 108 texts = ["r/g: %.2f" % center_r_g, 109 "b/g: %.2f" % center_b_g] 110 draw_legend(img_legend_ufmt, texts, 111 [left+text_offset, bottom-text_offset*2], 112 font_scale, text_offset, GREEN, int(line_width/2)) 113 114 # evaluate y and r/g, b/g for each block 115 ls_test_failed = [] 116 cu_test_failed = [] 117 ls_thres_h = center_y * (1 + THRES_LS_HIGH) 118 dist_max = math.sqrt(pow(w, 2)+pow(h, 2))/2 119 for spb_ct in SPB_CT_LIST: 120 # list sample block center location 121 num_sample = int(numpy.asscalar((1-spb_ct*2)/spb_r/2 + 1)) 122 ct_cord_x = numpy.concatenate( 123 (numpy.arange(spb_ct, 1-spb_ct+spb_r, spb_r*2), 124 spb_ct*numpy.ones((num_sample-1)), 125 (1-spb_ct)*numpy.ones((num_sample-1)), 126 numpy.arange(spb_ct, 1-spb_ct+spb_r, spb_r*2))) 127 ct_cord_y = numpy.concatenate( 128 (spb_ct*numpy.ones(num_sample+1), 129 numpy.arange(spb_ct+spb_r*2, 1-spb_ct, spb_r*2), 130 numpy.arange(spb_ct+spb_r*2, 1-spb_ct, spb_r*2), 131 (1-spb_ct)*numpy.ones(num_sample+1))) 132 133 blocks_info = [] 134 max_r_g = 0 135 min_r_g = float("inf") 136 max_b_g = 0 137 min_b_g = float("inf") 138 for spb_ctx, spb_cty in zip(ct_cord_x, ct_cord_y): 139 top = int((spb_cty-spb_r)*h) 140 bottom = int((spb_cty+spb_r)*h) 141 left = int((spb_ctx-spb_r)*w) 142 right = int((spb_ctx+spb_r)*w) 143 dist_to_img_center = math.sqrt(pow(abs(spb_ctx-0.5)*w, 2) 144 + pow(abs(spb_cty-0.5)*h, 2)) 145 ls_thres_l = ((THRES_LS_CT-THRES_LS_CN)*(1-dist_to_img_center 146 /dist_max)+THRES_LS_CN) * center_y 147 148 # compute block average value 149 block_y = numpy.mean(img_y[top:bottom, left:right]) 150 block_r_g = numpy.mean(r_g[top:bottom, left:right]) 151 block_b_g = numpy.mean(b_g[top:bottom, left:right]) 152 max_r_g = max(max_r_g, block_r_g) 153 min_r_g = min(min_r_g, block_r_g) 154 max_b_g = max(max_b_g, block_b_g) 155 min_b_g = min(min_b_g, block_b_g) 156 blocks_info.append({"pos": [top, bottom, left, right], 157 "block_r_g": block_r_g, 158 "block_b_g": block_b_g}) 159 # check lens shading and draw legend 160 if block_y > ls_thres_h or block_y < ls_thres_l: 161 ls_test_failed.append({"pos": [top, bottom, left, 162 right], 163 "val": block_y, 164 "thres_l": ls_thres_l}) 165 legend_color = RED 166 else: 167 legend_color = GREEN 168 text_bottom = bottom - text_offset 169 cv2.rectangle(img_legend_ls, (left, top), (right, bottom), 170 legend_color, line_width) 171 draw_legend(img_legend_ls, ["Y: %.2f" % block_y], 172 [left+text_offset, text_bottom], font_scale, 173 text_offset, legend_color, int(line_width/2)) 174 175 # check color uniformity and draw legend 176 ufmt_r_g = (max_r_g-min_r_g) / center_r_g 177 ufmt_b_g = (max_b_g-min_b_g) / center_b_g 178 if ufmt_r_g > THRES_UFMT or ufmt_b_g > THRES_UFMT: 179 cu_test_failed.append({"pos": spb_ct, 180 "ufmt_r_g": ufmt_r_g, 181 "ufmt_b_g": ufmt_b_g}) 182 legend_color = RED 183 else: 184 legend_color = GREEN 185 for block in blocks_info: 186 top, bottom, left, right = block["pos"] 187 cv2.rectangle(img_legend_ufmt, (left, top), (right, bottom), 188 legend_color, line_width) 189 texts = ["r/g: %.2f" % block["block_r_g"], 190 "b/g: %.2f" % block["block_b_g"]] 191 text_bottom = bottom - text_offset * 2 192 draw_legend(img_legend_ufmt, texts, 193 [left+text_offset, text_bottom], font_scale, 194 text_offset, legend_color, int(line_width/2)) 195 196 # Save images 197 its.image.write_image(img_legend_ufmt, 198 "%s_color_uniformity_result.png" % NAME, True) 199 its.image.write_image(img_legend_ls, 200 "%s_lens_shading_result.png" % NAME, True) 201 202 # print results 203 lens_shading_test_passed = True 204 color_uniformity_test_passed = True 205 if len(ls_test_failed) > 0: 206 lens_shading_test_passed = False 207 print "\nLens shading test summary" 208 print "Center block average Y value: %.3f" % center_y 209 print "Blocks failed in the lens shading test:" 210 for block in ls_test_failed: 211 top, bottom, left, right = block["pos"] 212 print "Block position: [top: %d, bottom: %d, left: %d, right: "\ 213 "%d]; average Y value: %.3f; valid value range: %.3f ~ " \ 214 "%.3f" % (top, bottom, left, right, block["val"], 215 block["thres_l"], ls_thres_h) 216 if len(cu_test_failed) > 0: 217 color_uniformity_test_passed = False 218 print "\nColor uniformity test summary" 219 print "Valid color uniformity value range: 0 ~ ", THRES_UFMT 220 print "Areas that failed the color uniformity test:" 221 for rd in cu_test_failed: 222 print "Radius position: %.3f; r/g uniformity: %.3f; b/g " \ 223 "uniformity: %.3f" % (rd["pos"], rd["ufmt_r_g"], 224 rd["ufmt_b_g"]) 225 assert lens_shading_test_passed 226 assert color_uniformity_test_passed 227 228 229 def draw_legend(img, texts, text_org, font_scale, text_offset, legend_color, 230 line_width): 231 """ Draw legend on an image. 232 233 Args: 234 img: Numpy float image array in RGB, with pixel values in [0,1]. 235 texts: list of legends. Each element in the list is a line of legend. 236 text_org: tuple of the bottom left corner of the text position in 237 pixels, horizontal and vertical. 238 font_scale: float number. Font scale of the basic font size. 239 text_offset: text line width in pixels. 240 legend_color: text color in rgb value. 241 line_width: strokes width in pixels. 242 """ 243 for text in texts: 244 cv2.putText(img, text, (text_org[0], text_org[1]), 245 cv2.FONT_HERSHEY_SIMPLEX, font_scale, 246 legend_color, line_width) 247 text_org[1] += text_offset 248 249 250 if __name__ == '__main__': 251 main() 252