Home | History | Annotate | Download | only in scene1
      1 # Copyright 2018 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.device
     16 import its.caps
     17 import its.objects
     18 import its.image
     19 import os.path
     20 import numpy as np
     21 from matplotlib import pylab
     22 import matplotlib.pyplot
     23 
     24 IMG_STATS_GRID = 9  # find used to find the center 11.11%
     25 NAME = os.path.basename(__file__).split(".")[0]
     26 NUM_ISO_STEPS = 5
     27 SATURATION_TOL = 0.01
     28 BLK_LVL_TOL = 0.1
     29 # Test 3 steps per 2x exposure
     30 EXP_MULT = pow(2, 1.0/3)
     31 INCREASING_THR = 0.99
     32 # slice captures into burst of SLICE_LEN requests
     33 SLICE_LEN = 10
     34 
     35 def main():
     36     """Capture a set of raw images with increasing exposure time and measure the pixel values.
     37     """
     38 
     39     with its.device.ItsSession() as cam:
     40 
     41         props = cam.get_camera_properties()
     42         its.caps.skip_unless(its.caps.raw16(props) and
     43                              its.caps.manual_sensor(props) and
     44                              its.caps.read_3a(props) and
     45                              its.caps.per_frame_control(props) and
     46                              not its.caps.mono_camera(props))
     47         debug = its.caps.debug_mode()
     48 
     49         # Expose for the scene with min sensitivity
     50         exp_min, exp_max = props["android.sensor.info.exposureTimeRange"]
     51         sens_min, _ = props["android.sensor.info.sensitivityRange"]
     52         # Digital gains might not be visible on RAW data
     53         sens_max = props["android.sensor.maxAnalogSensitivity"]
     54         sens_step = (sens_max - sens_min) / NUM_ISO_STEPS
     55         white_level = float(props["android.sensor.info.whiteLevel"])
     56         black_levels = [its.image.get_black_level(i,props) for i in range(4)]
     57         # Get the active array width and height.
     58         aax = props["android.sensor.info.activeArraySize"]["left"]
     59         aay = props["android.sensor.info.activeArraySize"]["top"]
     60         aaw = props["android.sensor.info.activeArraySize"]["right"]-aax
     61         aah = props["android.sensor.info.activeArraySize"]["bottom"]-aay
     62         raw_stat_fmt = {"format": "rawStats",
     63                         "gridWidth": aaw/IMG_STATS_GRID,
     64                         "gridHeight": aah/IMG_STATS_GRID}
     65 
     66         e_test = []
     67         mult = 1.0
     68         while exp_min*mult < exp_max:
     69             e_test.append(int(exp_min*mult))
     70             mult *= EXP_MULT
     71         if e_test[-1] < exp_max * INCREASING_THR:
     72             e_test.append(int(exp_max))
     73         e_test_ms = [e / 1000000.0 for e in e_test]
     74 
     75         for s in range(sens_min, sens_max, sens_step):
     76             means = []
     77             means.append(black_levels)
     78             reqs = [its.objects.manual_capture_request(s, e, 0) for e in e_test]
     79             # Capture raw in debug mode, rawStats otherwise
     80             caps = []
     81             for i in range(len(reqs) / SLICE_LEN):
     82                 if debug:
     83                     caps += cam.do_capture(reqs[i*SLICE_LEN:(i+1)*SLICE_LEN], cam.CAP_RAW)
     84                 else:
     85                     caps += cam.do_capture(reqs[i*SLICE_LEN:(i+1)*SLICE_LEN], raw_stat_fmt)
     86             last_n = len(reqs) % SLICE_LEN
     87             if last_n == 1:
     88                 if debug:
     89                     caps += [cam.do_capture(reqs[-last_n:], cam.CAP_RAW)]
     90                 else:
     91                     caps += [cam.do_capture(reqs[-last_n:], raw_stat_fmt)]
     92             elif last_n > 0:
     93                 if debug:
     94                     caps += cam.do_capture(reqs[-last_n:], cam.CAP_RAW)
     95                 else:
     96                     caps += cam.do_capture(reqs[-last_n:], raw_stat_fmt)
     97 
     98             # Measure the mean of each channel.
     99             # Each shot should be brighter (except underexposed/overexposed scene)
    100             for i,cap in enumerate(caps):
    101                 if debug:
    102                     planes = its.image.convert_capture_to_planes(cap, props)
    103                     tiles = [its.image.get_image_patch(p, 0.445, 0.445, 0.11, 0.11) for p in planes]
    104                     mean = [m * white_level for tile in tiles
    105                             for m in its.image.compute_image_means(tile)]
    106                     img = its.image.convert_capture_to_rgb_image(cap, props=props)
    107                     its.image.write_image(img, "%s_s=%d_e=%05d.jpg" % (NAME, s, e_test))
    108                 else:
    109                     mean_image, _ = its.image.unpack_rawstats_capture(cap)
    110                     mean = mean_image[IMG_STATS_GRID/2, IMG_STATS_GRID/2]
    111 
    112                 print "ISO=%d, exposure time=%.3fms, mean=%s" % (
    113                         s, e_test[i] / 1000000.0, str(mean))
    114                 means.append(mean)
    115 
    116 
    117             # means[0] is black level value
    118             r = [m[0] for m in means[1:]]
    119             gr = [m[1] for m in means[1:]]
    120             gb = [m[2] for m in means[1:]]
    121             b = [m[3] for m in means[1:]]
    122 
    123             pylab.plot(e_test_ms, r, "r.-")
    124             pylab.plot(e_test_ms, b, "b.-")
    125             pylab.plot(e_test_ms, gr, "g.-")
    126             pylab.plot(e_test_ms, gb, "k.-")
    127             pylab.xscale('log')
    128             pylab.yscale('log')
    129             pylab.title("%s ISO=%d" % (NAME, s))
    130             pylab.xlabel("Exposure time (ms)")
    131             pylab.ylabel("Center patch pixel mean")
    132             matplotlib.pyplot.savefig("%s_s=%d.png" % (NAME, s))
    133             pylab.clf()
    134 
    135             allow_under_saturated = True
    136             for i in xrange(1, len(means)):
    137                 prev_mean = means[i-1]
    138                 mean = means[i]
    139 
    140                 if np.isclose(max(mean), white_level, rtol=SATURATION_TOL):
    141                     print "Saturated: white_level %f, max_mean %f"% (white_level, max(mean))
    142                     break;
    143 
    144                 if allow_under_saturated and np.allclose(mean, black_levels, rtol=BLK_LVL_TOL):
    145                     # All channel means are close to black level
    146                     continue
    147 
    148                 allow_under_saturated = False
    149                 # Check pixel means are increasing (with small tolerance)
    150                 channels = ["Red", "Gr", "Gb", "Blue"]
    151                 for chan in range(4):
    152                     err_msg = "ISO=%d, %s, exptime %3fms mean: %.2f, %s mean: %.2f, TOL=%.f%%" % (
    153                             s, channels[chan],
    154                             e_test_ms[i-1], mean[chan],
    155                             "black level" if i == 1 else "exptime %3fms"%e_test_ms[i-2],
    156                             prev_mean[chan],
    157                             INCREASING_THR*100)
    158                     assert mean[chan] > prev_mean[chan] * INCREASING_THR, err_msg
    159 
    160 if __name__ == "__main__":
    161     main()
    162