1 # Copyright 2015 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.image 16 import its.caps 17 import its.device 18 import its.objects 19 import its.target 20 import matplotlib 21 import matplotlib.pyplot 22 import numpy 23 import os.path 24 from matplotlib import pylab 25 26 NR_MODES = [0, 1, 2, 3, 4] 27 28 29 def main(): 30 """Test that the android.noiseReduction.mode param is applied when set for 31 reprocessing requests. 32 33 Capture reprocessed images with the camera dimly lit. Uses a high analog 34 gain to ensure the captured image is noisy. 35 36 Captures three reprocessed images, for NR off, "fast", and "high quality". 37 Also captures a reprocessed image with low gain and NR off, and uses the 38 variance of this as the baseline. 39 """ 40 41 NAME = os.path.basename(__file__).split(".")[0] 42 43 NUM_SAMPLES_PER_MODE = 4 44 SNR_TOLERANCE = 3 # unit in db 45 46 with its.device.ItsSession() as cam: 47 props = cam.get_camera_properties() 48 49 its.caps.skip_unless(its.caps.compute_target_exposure(props) and 50 its.caps.per_frame_control(props) and 51 its.caps.noise_reduction_mode(props, 0) and 52 (its.caps.yuv_reprocess(props) or 53 its.caps.private_reprocess(props))) 54 55 # If reprocessing is supported, ZSL NR mode must be avaiable. 56 assert(its.caps.noise_reduction_mode(props, 4)) 57 58 reprocess_formats = [] 59 if (its.caps.yuv_reprocess(props)): 60 reprocess_formats.append("yuv") 61 if (its.caps.private_reprocess(props)): 62 reprocess_formats.append("private") 63 64 for reprocess_format in reprocess_formats: 65 # List of variances for R, G, B. 66 snrs = [[], [], []] 67 nr_modes_reported = [] 68 69 # NR mode 0 with low gain 70 e, s = its.target.get_target_exposure_combos(cam)["minSensitivity"] 71 req = its.objects.manual_capture_request(s, e) 72 req["android.noiseReduction.mode"] = 0 73 74 # Test reprocess_format->JPEG reprocessing 75 # TODO: Switch to reprocess_format->YUV when YUV reprocessing is 76 # supported. 77 size = its.objects.get_available_output_sizes("jpg", props)[0] 78 out_surface = {"width":size[0], "height":size[1], "format":"jpg"} 79 cap = cam.do_capture(req, out_surface, reprocess_format) 80 img = its.image.decompress_jpeg_to_rgb_image(cap["data"]) 81 its.image.write_image(img, "%s_low_gain_fmt=jpg.jpg" % (NAME)) 82 tile = its.image.get_image_patch(img, 0.45, 0.45, 0.1, 0.1) 83 ref_snr = its.image.compute_image_snrs(tile) 84 print "Ref SNRs:", ref_snr 85 86 e, s = its.target.get_target_exposure_combos(cam)["maxSensitivity"] 87 for nr_mode in NR_MODES: 88 # Skip unavailable modes 89 if not its.caps.noise_reduction_mode(props, nr_mode): 90 nr_modes_reported.append(nr_mode) 91 for channel in range(3): 92 snrs[channel].append(0) 93 continue 94 95 rgb_snr_list = [] 96 # Capture several images to account for per frame noise 97 # variations 98 for n in range(NUM_SAMPLES_PER_MODE): 99 req = its.objects.manual_capture_request(s, e) 100 req["android.noiseReduction.mode"] = nr_mode 101 cap = cam.do_capture(req, out_surface, reprocess_format) 102 103 img = its.image.decompress_jpeg_to_rgb_image(cap["data"]) 104 if n == 0: 105 its.image.write_image( 106 img, 107 "%s_high_gain_nr=%d_fmt=jpg.jpg" 108 %(NAME, nr_mode)) 109 nr_modes_reported.append( 110 cap["metadata"]["android.noiseReduction.mode"]) 111 112 tile = its.image.get_image_patch(img, 0.45, 0.45, 0.1, 0.1) 113 # Get the variances for R, G, and B channels 114 rgb_snrs = its.image.compute_image_snrs(tile) 115 rgb_snr_list.append(rgb_snrs) 116 117 r_snrs = [rgb[0] for rgb in rgb_snr_list] 118 g_snrs = [rgb[1] for rgb in rgb_snr_list] 119 b_snrs = [rgb[2] for rgb in rgb_snr_list] 120 rgb_snrs = [numpy.mean(r_snrs), 121 numpy.mean(g_snrs), 122 numpy.mean(b_snrs)] 123 print "NR mode", nr_mode, "SNRs:" 124 print " R SNR:", rgb_snrs[0],\ 125 "Min:", min(r_snrs), "Max:", max(r_snrs) 126 print " G SNR:", rgb_snrs[1],\ 127 "Min:", min(g_snrs), "Max:", max(g_snrs) 128 print " B SNR:", rgb_snrs[2],\ 129 "Min:", min(b_snrs), "Max:", max(b_snrs) 130 131 for chan in range(3): 132 snrs[chan].append(rgb_snrs[chan]) 133 134 # Draw a plot. 135 pylab.figure(reprocess_format) 136 for channel in range(3): 137 pylab.plot(NR_MODES, snrs[channel], "-"+"rgb"[channel]+"o") 138 139 pylab.xlabel("Noise Reduction Mode") 140 pylab.ylabel("SNR (dB)") 141 pylab.xticks(NR_MODES) 142 matplotlib.pyplot.savefig("%s_plot_%s_SNRs.png" % 143 (NAME, reprocess_format)) 144 145 assert nr_modes_reported == NR_MODES 146 147 for j in range(3): 148 # Larger is better 149 # Verify OFF(0) is not better than FAST(1) 150 assert(snrs[j][0] < 151 snrs[j][1] + SNR_TOLERANCE) 152 # Verify FAST(1) is not better than HQ(2) 153 assert(snrs[j][1] < 154 snrs[j][2] + SNR_TOLERANCE) 155 # Verify HQ(2) is better than OFF(0) 156 assert(snrs[j][0] < snrs[j][2]) 157 if its.caps.noise_reduction_mode(props, 3): 158 # Verify OFF(0) is not better than MINIMAL(3) 159 assert(snrs[j][0] < 160 snrs[j][3] + SNR_TOLERANCE) 161 # Verify MINIMAL(3) is not better than HQ(2) 162 assert(snrs[j][3] < 163 snrs[j][2] + SNR_TOLERANCE) 164 # Verify ZSL(4) is close to MINIMAL(3) 165 assert(numpy.isclose(snrs[j][4], snrs[j][3], 166 atol=SNR_TOLERANCE)) 167 else: 168 # Verify ZSL(4) is close to OFF(0) 169 assert(numpy.isclose(snrs[j][4], snrs[j][0], 170 atol=SNR_TOLERANCE)) 171 172 if __name__ == '__main__': 173 main() 174 175