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