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.device 16 import its.caps 17 import its.image 18 import its.objects 19 import its.target 20 import os.path 21 import pylab 22 import matplotlib 23 import matplotlib.pyplot 24 25 def main(): 26 """Capture a set of raw/yuv images with different 27 sensitivity/post Raw sensitivity boost combination 28 and check if the output pixel mean matches request settings 29 """ 30 NAME = os.path.basename(__file__).split(".")[0] 31 32 # Each raw image 33 RATIO_THRESHOLD = 0.1 34 # Waive the check if raw pixel value is below this level (signal too small 35 # that small black level error converts to huge error in percentage) 36 RAW_PIXEL_VAL_THRESHOLD = 0.03 37 38 with its.device.ItsSession() as cam: 39 props = cam.get_camera_properties() 40 its.caps.skip_unless(its.caps.raw_output(props) and 41 its.caps.post_raw_sensitivity_boost(props) and 42 its.caps.compute_target_exposure(props) and 43 its.caps.per_frame_control(props)) 44 45 w,h = its.objects.get_available_output_sizes( 46 "yuv", props, (1920, 1080))[0] 47 48 if its.caps.raw16(props): 49 raw_format = 'raw' 50 elif its.caps.raw10(props): 51 raw_format = 'raw10' 52 elif its.caps.raw12(props): 53 raw_format = 'raw12' 54 else: # should not reach here 55 raise its.error.Error('Cannot find available RAW output format') 56 57 out_surfaces = [{"format": raw_format}, 58 {"format": "yuv", "width": w, "height": h}] 59 60 sens_min, sens_max = props['android.sensor.info.sensitivityRange'] 61 sens_boost_min, sens_boost_max = \ 62 props['android.control.postRawSensitivityBoostRange'] 63 64 65 e_target, s_target = \ 66 its.target.get_target_exposure_combos(cam)["midSensitivity"] 67 68 reqs = [] 69 settings = [] 70 s_boost = sens_boost_min 71 while s_boost <= sens_boost_max: 72 s_raw = int(round(s_target * 100.0 / s_boost)) 73 if s_raw < sens_min or s_raw > sens_max: 74 break 75 req = its.objects.manual_capture_request(s_raw, e_target) 76 req['android.control.postRawSensitivityBoost'] = s_boost 77 reqs.append(req) 78 settings.append((s_raw, s_boost)) 79 if s_boost == sens_boost_max: 80 break 81 s_boost *= 2 82 # Always try to test maximum sensitivity boost value 83 if s_boost > sens_boost_max: 84 s_boost = sens_boost_max 85 86 caps = cam.do_capture(reqs, out_surfaces) 87 88 raw_rgb_means = [] 89 yuv_rgb_means = [] 90 raw_caps, yuv_caps = caps 91 if not isinstance(raw_caps, list): 92 raw_caps = [raw_caps] 93 if not isinstance(yuv_caps, list): 94 yuv_caps = [yuv_caps] 95 for i in xrange(len(reqs)): 96 (s, s_boost) = settings[i] 97 raw_cap = raw_caps[i] 98 yuv_cap = yuv_caps[i] 99 raw_rgb = its.image.convert_capture_to_rgb_image(raw_cap, props=props) 100 yuv_rgb = its.image.convert_capture_to_rgb_image(yuv_cap) 101 raw_tile = its.image.get_image_patch(raw_rgb, 0.45,0.45,0.1,0.1) 102 yuv_tile = its.image.get_image_patch(yuv_rgb, 0.45,0.45,0.1,0.1) 103 raw_rgb_means.append(its.image.compute_image_means(raw_tile)) 104 yuv_rgb_means.append(its.image.compute_image_means(yuv_tile)) 105 its.image.write_image(raw_tile, 106 "%s_raw_s=%04d_boost=%04d.jpg" % (NAME,s,s_boost)) 107 its.image.write_image(yuv_tile, 108 "%s_yuv_s=%04d_boost=%04d.jpg" % (NAME,s,s_boost)) 109 print "s=%d, s_boost=%d: raw_means %s, yuv_means %s"%( 110 s,s_boost,raw_rgb_means[-1], yuv_rgb_means[-1]) 111 112 xs = range(len(reqs)) 113 pylab.plot(xs, [rgb[0] for rgb in raw_rgb_means], 'r') 114 pylab.plot(xs, [rgb[1] for rgb in raw_rgb_means], 'g') 115 pylab.plot(xs, [rgb[2] for rgb in raw_rgb_means], 'b') 116 pylab.ylim([0,1]) 117 matplotlib.pyplot.savefig("%s_raw_plot_means.png" % (NAME)) 118 pylab.clf() 119 pylab.plot(xs, [rgb[0] for rgb in yuv_rgb_means], 'r') 120 pylab.plot(xs, [rgb[1] for rgb in yuv_rgb_means], 'g') 121 pylab.plot(xs, [rgb[2] for rgb in yuv_rgb_means], 'b') 122 pylab.ylim([0,1]) 123 matplotlib.pyplot.savefig("%s_yuv_plot_means.png" % (NAME)) 124 125 rgb_str = ["R", "G", "B"] 126 # Test that raw means is about 2x brighter than next step 127 for step in range(1, len(reqs)): 128 (s_prev, s_boost_prev) = settings[step - 1] 129 (s, s_boost) = settings[step] 130 expect_raw_ratio = s_prev / float(s) 131 raw_thres_min = expect_raw_ratio * (1 - RATIO_THRESHOLD) 132 raw_thres_max = expect_raw_ratio * (1 + RATIO_THRESHOLD) 133 for rgb in range(3): 134 ratio = raw_rgb_means[step - 1][rgb] / raw_rgb_means[step][rgb] 135 print ("Step (%d,%d) %s channel: %f, %f, ratio %f," + 136 " threshold_min %f, threshold_max %f") % ( 137 step-1, step, rgb_str[rgb], 138 raw_rgb_means[step - 1][rgb], 139 raw_rgb_means[step][rgb], 140 ratio, raw_thres_min, raw_thres_max) 141 if (raw_rgb_means[step][rgb] <= RAW_PIXEL_VAL_THRESHOLD): 142 continue 143 assert(raw_thres_min < ratio < raw_thres_max) 144 145 # Test that each yuv step is about the same bright as their mean 146 yuv_thres_min = 1 - RATIO_THRESHOLD 147 yuv_thres_max = 1 + RATIO_THRESHOLD 148 for rgb in range(3): 149 vals = [val[rgb] for val in yuv_rgb_means] 150 for step in range(len(reqs)): 151 if (raw_rgb_means[step][rgb] <= RAW_PIXEL_VAL_THRESHOLD): 152 vals = vals[:step] 153 mean = sum(vals) / len(vals) 154 print "%s channel vals %s mean %f"%(rgb_str[rgb], vals, mean) 155 for step in range(len(vals)): 156 ratio = vals[step] / mean 157 assert(yuv_thres_min < ratio < yuv_thres_max) 158 159 if __name__ == '__main__': 160 main() 161