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.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