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