Timothy Knight | 64f33f9 | 2014-11-16 14:26:57 -0800 | [diff] [blame] | 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 |
Yin-Chia Yeh | 2970012 | 2016-10-10 10:54:07 -0700 | [diff] [blame^] | 20 | from matplotlib import pylab |
Timothy Knight | 64f33f9 | 2014-11-16 14:26:57 -0800 | [diff] [blame] | 21 | import matplotlib |
| 22 | import matplotlib.pyplot |
| 23 | |
| 24 | def main(): |
| 25 | """Verify that the DNG raw model parameters are correct. |
| 26 | """ |
| 27 | NAME = os.path.basename(__file__).split(".")[0] |
| 28 | |
| 29 | NUM_STEPS = 4 |
| 30 | |
| 31 | # Pass if the difference between expected and computed variances is small, |
| 32 | # defined as being within an absolute variance delta of 0.0005, or within |
| 33 | # 20% of the expected variance, whichever is larger; this is to allow the |
| 34 | # test to pass in the presence of some randomness (since this test is |
| 35 | # measuring noise of a small patch) and some imperfect scene conditions |
| 36 | # (since ITS doesn't require a perfectly uniformly lit scene). |
| 37 | DIFF_THRESH = 0.0005 |
| 38 | FRAC_THRESH = 0.2 |
| 39 | |
| 40 | with its.device.ItsSession() as cam: |
| 41 | |
| 42 | props = cam.get_camera_properties() |
| 43 | its.caps.skip_unless(its.caps.raw(props) and |
| 44 | its.caps.raw16(props) and |
| 45 | its.caps.manual_sensor(props) and |
| 46 | its.caps.read_3a(props) and |
| 47 | its.caps.per_frame_control(props)) |
| 48 | |
| 49 | white_level = float(props['android.sensor.info.whiteLevel']) |
Timothy Knight | 64f33f9 | 2014-11-16 14:26:57 -0800 | [diff] [blame] | 50 | cfa_idxs = its.image.get_canonical_cfa_order(props) |
Timothy Knight | 64f33f9 | 2014-11-16 14:26:57 -0800 | [diff] [blame] | 51 | |
| 52 | # Expose for the scene with min sensitivity |
| 53 | sens_min, sens_max = props['android.sensor.info.sensitivityRange'] |
| 54 | sens_step = (sens_max - sens_min) / NUM_STEPS |
Shuzhen Wang | 18b330d | 2016-06-27 11:49:38 -0700 | [diff] [blame] | 55 | s_ae,e_ae,_,_,f_dist = cam.do_3a(get_results=True) |
Timothy Knight | 64f33f9 | 2014-11-16 14:26:57 -0800 | [diff] [blame] | 56 | s_e_prod = s_ae * e_ae |
| 57 | sensitivities = range(sens_min, sens_max, sens_step) |
| 58 | |
| 59 | var_expected = [[],[],[],[]] |
| 60 | var_measured = [[],[],[],[]] |
| 61 | for sens in sensitivities: |
| 62 | |
| 63 | # Capture a raw frame with the desired sensitivity. |
| 64 | exp = int(s_e_prod / float(sens)) |
Shuzhen Wang | 18b330d | 2016-06-27 11:49:38 -0700 | [diff] [blame] | 65 | req = its.objects.manual_capture_request(sens, exp, f_dist) |
Timothy Knight | 64f33f9 | 2014-11-16 14:26:57 -0800 | [diff] [blame] | 66 | cap = cam.do_capture(req, cam.CAP_RAW) |
| 67 | |
| 68 | # Test each raw color channel (R, GR, GB, B): |
| 69 | noise_profile = cap["metadata"]["android.sensor.noiseProfile"] |
| 70 | assert((len(noise_profile)) == 4) |
| 71 | for ch in range(4): |
| 72 | # Get the noise model parameters for this channel of this shot. |
| 73 | s,o = noise_profile[cfa_idxs[ch]] |
| 74 | |
| 75 | # Get a center tile of the raw channel, and compute the mean. |
| 76 | # Use a very small patch to ensure gross uniformity (i.e. so |
| 77 | # non-uniform lighting or vignetting doesn't affect the variance |
| 78 | # calculation). |
| 79 | plane = its.image.convert_capture_to_planes(cap, props)[ch] |
Timothy Knight | fa78587 | 2016-07-12 16:49:47 -0700 | [diff] [blame] | 80 | black_level = its.image.get_black_level( |
| 81 | ch, props, cap["metadata"]) |
| 82 | plane = (plane * white_level - black_level) / ( |
| 83 | white_level - black_level) |
Timothy Knight | 64f33f9 | 2014-11-16 14:26:57 -0800 | [diff] [blame] | 84 | tile = its.image.get_image_patch(plane, 0.49,0.49,0.02,0.02) |
| 85 | mean = tile.mean() |
| 86 | |
| 87 | # Calculate the expected variance based on the model, and the |
| 88 | # measured variance from the tile. |
| 89 | var_measured[ch].append( |
| 90 | its.image.compute_image_variances(tile)[0]) |
| 91 | var_expected[ch].append(s * mean + o) |
| 92 | |
| 93 | for ch in range(4): |
| 94 | pylab.plot(sensitivities, var_expected[ch], "rgkb"[ch], |
| 95 | label=["R","GR","GB","B"][ch]+" expected") |
| 96 | pylab.plot(sensitivities, var_measured[ch], "rgkb"[ch]+"--", |
| 97 | label=["R", "GR", "GB", "B"][ch]+" measured") |
| 98 | pylab.xlabel("Sensitivity") |
| 99 | pylab.ylabel("Center patch variance") |
| 100 | pylab.legend(loc=2) |
| 101 | matplotlib.pyplot.savefig("%s_plot.png" % (NAME)) |
| 102 | |
| 103 | # Pass/fail check. |
| 104 | for ch in range(4): |
| 105 | diffs = [var_measured[ch][i] - var_expected[ch][i] |
| 106 | for i in range(NUM_STEPS)] |
| 107 | print "Diffs (%s):"%(["R","GR","GB","B"][ch]), diffs |
| 108 | for i,diff in enumerate(diffs): |
| 109 | thresh = max(DIFF_THRESH, FRAC_THRESH * var_expected[ch][i]) |
| 110 | assert(diff <= thresh) |
| 111 | |
| 112 | if __name__ == '__main__': |
| 113 | main() |
| 114 | |