Home | History | Annotate | Download | only in scene5
      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