1 # Copyright 2014 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 from matplotlib import pylab 21 import matplotlib 22 23 GR_PLANE = 1 # GR plane index in RGGB data 24 IMG_STATS_GRID = 9 # find used to find the center 11.11% 25 NAME = os.path.basename(__file__).split(".")[0] 26 NUM_STEPS = 5 27 VAR_THRESH = 1.01 # each shot must be 1% noisier than previous 28 29 30 def main(): 31 """Capture a set of raw images with increasing gains and measure the noise. 32 33 Capture raw-only, in a burst. 34 """ 35 36 with its.device.ItsSession() as cam: 37 38 props = cam.get_camera_properties() 39 its.caps.skip_unless(its.caps.raw16(props) and 40 its.caps.manual_sensor(props) and 41 its.caps.read_3a(props) and 42 its.caps.per_frame_control(props) and 43 not its.caps.mono_camera(props)) 44 debug = its.caps.debug_mode() 45 46 # Expose for the scene with min sensitivity 47 sens_min, _ = props["android.sensor.info.sensitivityRange"] 48 # Digital gains might not be visible on RAW data 49 sens_max = props["android.sensor.maxAnalogSensitivity"] 50 sens_step = (sens_max - sens_min) / NUM_STEPS 51 s_ae, e_ae, _, _, f_dist = cam.do_3a(get_results=True) 52 s_e_prod = s_ae * e_ae 53 54 reqs = [] 55 settings = [] 56 for s in range(sens_min, sens_max, sens_step): 57 e = int(s_e_prod / float(s)) 58 req = its.objects.manual_capture_request(s, e, f_dist) 59 reqs.append(req) 60 settings.append((s, e)) 61 62 if debug: 63 caps = cam.do_capture(reqs, cam.CAP_RAW) 64 else: 65 # Get the active array width and height. 66 aax = props["android.sensor.info.activeArraySize"]["left"] 67 aay = props["android.sensor.info.activeArraySize"]["top"] 68 aaw = props["android.sensor.info.activeArraySize"]["right"]-aax 69 aah = props["android.sensor.info.activeArraySize"]["bottom"]-aay 70 # Compute stats on a grid across each image. 71 caps = cam.do_capture(reqs, 72 {"format": "rawStats", 73 "gridWidth": aaw/IMG_STATS_GRID, 74 "gridHeight": aah/IMG_STATS_GRID}) 75 76 variances = [] 77 for i, cap in enumerate(caps): 78 (s, e) = settings[i] 79 80 # Each shot should be noisier than the previous shot (as the gain 81 # is increasing). Use the variance of the center stats grid cell. 82 if debug: 83 gr = its.image.convert_capture_to_planes(cap, props)[1] 84 tile = its.image.get_image_patch(gr, 0.445, 0.445, 0.11, 0.11) 85 var = its.image.compute_image_variances(tile)[0] 86 img = its.image.convert_capture_to_rgb_image(cap, props=props) 87 its.image.write_image(img, 88 "%s_s=%05d_var=%f.jpg" % (NAME, s, var)) 89 else: 90 # find white level 91 white_level = float(props["android.sensor.info.whiteLevel"]) 92 _, var_image = its.image.unpack_rawstats_capture(cap) 93 cfa_idxs = its.image.get_canonical_cfa_order(props) 94 var = var_image[IMG_STATS_GRID/2, IMG_STATS_GRID/2, 95 cfa_idxs[GR_PLANE]]/white_level**2 96 variances.append(var) 97 print "s=%d, e=%d, var=%e" % (s, e, var) 98 99 x = range(len(variances)) 100 pylab.plot(x, variances, "-ro") 101 pylab.xticks(x) 102 pylab.xlabel("Setting Combination") 103 pylab.ylabel("Image Center Patch Variance") 104 matplotlib.pyplot.savefig("%s_variances.png" % NAME) 105 106 # Test that each shot is noisier than the previous one. 107 x.pop() # remove last element in x index 108 for i in x: 109 assert variances[i] < variances[i+1] / VAR_THRESH 110 111 if __name__ == "__main__": 112 main() 113 114