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