aboutsummaryrefslogtreecommitdiff
path: root/apps/CameraITS/tools
diff options
context:
space:
mode:
Diffstat (limited to 'apps/CameraITS/tools')
-rw-r--r--apps/CameraITS/tools/compute_dng_noise_model.py175
-rw-r--r--apps/CameraITS/tools/config.py66
-rw-r--r--apps/CameraITS/tools/run_all_tests.py66
3 files changed, 0 insertions, 307 deletions
diff --git a/apps/CameraITS/tools/compute_dng_noise_model.py b/apps/CameraITS/tools/compute_dng_noise_model.py
deleted file mode 100644
index e089ffc..0000000
--- a/apps/CameraITS/tools/compute_dng_noise_model.py
+++ /dev/null
@@ -1,175 +0,0 @@
-# Copyright 2014 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-import its.device
-import its.objects
-import its.image
-import pprint
-import pylab
-import os.path
-import matplotlib
-import matplotlib.pyplot
-import numpy
-import math
-
-def main():
- """Compute the DNG noise model from a color checker chart.
-
- TODO: Make this more robust; some manual futzing may be needed.
- """
- NAME = os.path.basename(__file__).split(".")[0]
-
- with its.device.ItsSession() as cam:
-
- props = cam.get_camera_properties()
-
- white_level = float(props['android.sensor.info.whiteLevel'])
- black_levels = props['android.sensor.blackLevelPattern']
- idxs = its.image.get_canonical_cfa_order(props)
- black_levels = [black_levels[i] for i in idxs]
-
- # Expose for the scene with min sensitivity
- sens_min, sens_max = props['android.sensor.info.sensitivityRange']
- s_ae,e_ae,awb_gains,awb_ccm,_ = cam.do_3a(get_results=True)
- s_e_prod = s_ae * e_ae
-
- # Make the image brighter since the script looks at linear Bayer
- # raw patches rather than gamma-encoded YUV patches (and the AE
- # probably under-exposes a little for this use-case).
- s_e_prod *= 2
-
- # Capture raw frames across the full sensitivity range.
- NUM_SENS_STEPS = 15
- sens_step = int((sens_max - sens_min - 1) / float(NUM_SENS_STEPS))
- reqs = []
- sens = []
- for s in range(sens_min, sens_max, sens_step):
- e = int(s_e_prod / float(s))
- req = its.objects.manual_capture_request(s, e)
- req["android.colorCorrection.transform"] = \
- its.objects.float_to_rational(awb_ccm)
- req["android.colorCorrection.gains"] = awb_gains
- reqs.append(req)
- sens.append(s)
-
- caps = cam.do_capture(reqs, cam.CAP_RAW)
-
- # A list of the (x,y) coords of the center pixel of a collection of
- # patches of a color checker chart. Each patch should be uniform,
- # however the actual color doesn't matter. Note that the coords are
- # relative to the *converted* RGB image, which is 1/2 x 1/2 of the
- # full size; convert back to full.
- img = its.image.convert_capture_to_rgb_image(caps[0], props=props)
- patches = its.image.get_color_checker_chart_patches(img, NAME+"_debug")
- patches = [(2*x,2*y) for (x,y) in sum(patches,[])]
-
- lines = []
- for (s,cap) in zip(sens,caps):
- # For each capture, compute the mean value in each patch, for each
- # Bayer plane; discard patches where pixels are close to clamped.
- # Also compute the variance.
- CLAMP_THRESH = 0.2
- planes = its.image.convert_capture_to_planes(cap, props)
- points = []
- for i,plane in enumerate(planes):
- plane = (plane * white_level - black_levels[i]) / (
- white_level - black_levels[i])
- for j,(x,y) in enumerate(patches):
- tile = plane[y/2-16:y/2+16:,x/2-16:x/2+16:,::]
- mean = its.image.compute_image_means(tile)[0]
- var = its.image.compute_image_variances(tile)[0]
- if (mean > CLAMP_THRESH and mean < 1.0-CLAMP_THRESH):
- # Each point is a (mean,variance) tuple for a patch;
- # for a given ISO, there should be a linear
- # relationship between these values.
- points.append((mean,var))
-
- # Fit a line to the points, with a line equation: y = mx + b.
- # This line is the relationship between mean and variance (i.e.)
- # between signal level and noise, for this particular sensor.
- # In the DNG noise model, the gradient (m) is "S", and the offset
- # (b) is "O".
- points.sort()
- xs = [x for (x,y) in points]
- ys = [y for (x,y) in points]
- m,b = numpy.polyfit(xs, ys, 1)
- lines.append((s,m,b))
- print s, "->", m, b
-
- # TODO: Clean up these checks (which currently fail in some cases).
- # Some sanity checks:
- # * Noise levels should increase with brightness.
- # * Extrapolating to a black image, the noise should be positive.
- # Basically, the "b" value should correspnd to the read noise,
- # which is the noise level if the sensor was operating in zero
- # light.
- #assert(m > 0)
- #assert(b >= 0)
-
- # Draw a plot.
- pylab.plot(xs, ys, 'r')
- pylab.plot([0,xs[-1]],[b,m*xs[-1]+b],'b')
- matplotlib.pyplot.savefig("%s_plot_mean_vs_variance.png" % (NAME))
-
- # Now fit a line across the (m,b) line parameters for each sensitivity.
- # The gradient (m) params are fit to the "S" line, and the offset (b)
- # params are fit to the "O" line, both as a function of sensitivity.
- gains = [d[0] for d in lines]
- Ss = [d[1] for d in lines]
- Os = [d[2] for d in lines]
- mS,bS = numpy.polyfit(gains, Ss, 1)
- mO,bO = numpy.polyfit(gains, Os, 1)
-
- # Plot curve "O" as 10x, so it fits in the same scale as curve "S".
- pylab.plot(gains, [10*o for o in Os], 'r')
- pylab.plot([gains[0],gains[-1]],
- [10*mO*gains[0]+10*bO, 10*mO*gains[-1]+10*bO], 'b')
- pylab.plot(gains, Ss, 'r')
- pylab.plot([gains[0],gains[-1]], [mS*gains[0]+bS, mS*gains[-1]+bS], 'b')
- matplotlib.pyplot.savefig("%s_plot_S_O.png" % (NAME))
-
- print """
- /* Generated test code to dump a table of data for external validation
- * of the noise model parameters.
- */
- #include <stdio.h>
- #include <assert.h>
- double compute_noise_model_entry_S(int sens);
- double compute_noise_model_entry_O(int sens);
- int main(void) {
- int sens;
- for (sens = %d; sens <= %d; sens += 100) {
- double o = compute_noise_model_entry_O(sens);
- double s = compute_noise_model_entry_S(sens);
- printf("%%d,%%lf,%%lf\\n", sens, o, s);
- }
- return 0;
- }
-
- /* Generated functions to map a given sensitivity to the O and S noise
- * model parameters in the DNG noise model.
- */
- double compute_noise_model_entry_S(int sens) {
- double s = %e * sens + %e;
- return s < 0.0 ? 0.0 : s;
- }
- double compute_noise_model_entry_O(int sens) {
- double o = %e * sens + %e;
- return o < 0.0 ? 0.0 : o;
- }
- """%(sens_min,sens_max,mS,bS,mO,bO)
-
-if __name__ == '__main__':
- main()
-
diff --git a/apps/CameraITS/tools/config.py b/apps/CameraITS/tools/config.py
deleted file mode 100644
index 6e83412..0000000
--- a/apps/CameraITS/tools/config.py
+++ /dev/null
@@ -1,66 +0,0 @@
-# Copyright 2013 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-import its.device
-import its.target
-import sys
-
-def main():
- """Set the target exposure.
-
- This program is just a wrapper around the its.target module, to allow the
- functions in it to be invoked from the command line.
-
- Usage:
- python config.py - Measure the target exposure, and cache it.
- python config.py EXP - Hard-code (and cache) the target exposure.
-
- The "reboot" or "reboot=<N>" and "camera=<N>" arguments may also be
- provided, just as with all the test scripts. The "target" argument is
- may also be provided but it has no effect on this script since the cached
- exposure value is cleared regardless.
-
- If no exposure value is provided, the camera will be used to measure
- the scene and set a level that will result in the luma (with linear
- tonemap) being at the 0.5 level. This requires camera 3A and capture
- to be functioning.
-
- For bring-up purposes, the exposure value may be manually set to a hard-
- coded value, without the camera having to be able to perform 3A (or even
- capture a shot reliably).
- """
-
- # Command line args, ignoring any args that will be passed down to the
- # ItsSession constructor.
- args = [s for s in sys.argv if s[:6] not in \
- ["reboot", "camera", "target", "noinit"]]
-
- if len(args) == 1:
- with its.device.ItsSession() as cam:
- # Automatically measure target exposure.
- its.target.clear_cached_target_exposure()
- exposure = its.target.get_target_exposure(cam)
- elif len(args) == 2:
- # Hard-code the target exposure.
- exposure = int(args[1])
- its.target.set_hardcoded_exposure(exposure)
- else:
- print "Usage: python %s [EXPOSURE]"
- sys.exit(0)
- print "New target exposure set to", exposure
- print "This corresponds to %dms at ISO 100" % int(exposure/100/1000000.0)
-
-if __name__ == '__main__':
- main()
-
diff --git a/apps/CameraITS/tools/run_all_tests.py b/apps/CameraITS/tools/run_all_tests.py
deleted file mode 100644
index 4677331..0000000
--- a/apps/CameraITS/tools/run_all_tests.py
+++ /dev/null
@@ -1,66 +0,0 @@
-# Copyright 2014 The Android Open Source Project
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-import os
-import os.path
-import tempfile
-import subprocess
-import time
-import sys
-
-def main():
- """Run all the automated tests, saving intermediate files, and producing
- a summary/report of the results.
-
- Script should be run from the top-level CameraITS directory.
- """
-
- # Get all the scene0 and scene1 tests, which can be run using the same
- # physical setup.
- scenes = ["scene0", "scene1"]
- tests = []
- for d in scenes:
- tests += [(d,s[:-3],os.path.join("tests", d, s))
- for s in os.listdir(os.path.join("tests",d))
- if s[-3:] == ".py"]
- tests.sort()
-
- # Make output directories to hold the generated files.
- topdir = tempfile.mkdtemp()
- for d in scenes:
- os.mkdir(os.path.join(topdir, d))
- print "Saving output files to:", topdir, "\n"
-
- # Run each test, capturing stdout and stderr.
- numpass = 0
- for (scene,testname,testpath) in tests:
- cmd = ['python', os.path.join(os.getcwd(),testpath)] + sys.argv[1:]
- outdir = os.path.join(topdir,scene)
- outpath = os.path.join(outdir,testname+"_stdout.txt")
- errpath = os.path.join(outdir,testname+"_stderr.txt")
- t0 = time.time()
- with open(outpath,"w") as fout, open(errpath,"w") as ferr:
- retcode = subprocess.call(cmd,stderr=ferr,stdout=fout,cwd=outdir)
- t1 = time.time()
- print "%s %s/%s [%.1fs]" % (
- "PASS" if retcode==0 else "FAIL", scene, testname, t1-t0)
- if retcode == 0:
- numpass += 1
-
- print "\n%d / %d tests passed (%.1f%%)" % (
- numpass, len(tests), 100.0*float(numpass)/len(tests))
-
-if __name__ == '__main__':
- main()
-