import numpy def bilinear(cfa, pattern='GBRG', maxCount=65535): """ title:: bilinear description:: This method will perform color filter array interpolation on a generic document mode (Bayer) image. A simple neighbor averaging approach will be taken for each of the three patterned color channels. The green channel interpolation will be performed, at each "x" location, G x G x G x G x G x G x G x G x G x G x G x G x G as the average of the surrounding four G values. For the red channel x R x R x y y y y y x R x R x y y y y y x R x R x the interpolation at each "x" location will be the average of the horizontal neighbors. Subsequently, the interpolation at each "y" location will be the average of the vertical neighbors (half of which are original red values and half of which are interpolated red values). Similarly for the blue channel y y y y y B x B x B y y y y y B x B x B y y y y y the interpolation at each "x" location will be the average of the horizontal neighbors. Subsequently, the interpolation at each "y" location will be the average of the vertical neighbors (half of which are original blue values and half of which are interpolated blue values). attributes:: cfa A 2-dimensional ndarray containing the RAW document mode image to be interpolated. The image can be any bit depth and the output will match the input data type. pattern A string defining the CFA layout [default is 'GBRG']: 'GBRG' - G B Raspberry Pi Camera (OmniVision OV5647) R G 'GRBG' - G R B G 'BGGR' - B G G R 'RGGB' - R G G B maxCount The upper limit on digital count that will be permitted in the interpolated image. Values that exceed this count will be truncated to this value. [default is 65535] author:: Carl Salvaggio copyright:: Copyright (C) 2015, Rochester Institute of Technology license:: GPL version:: 1.0.0 disclaimer:: This source code is provided "as is" and without warranties as to performance or merchantability. The author and/or distributors of this source code may have made statements about this source code. Any such statements do not constitute warranties and shall not be relied on by the user in deciding whether to use this source code. This source code is provided without any express or implied warranties whatsoever. Because of the diversity of conditions and hardware under which this source code may be used, no warranty of fitness for a particular purpose is offered. The user is advised to test the source code thoroughly before relying on it. The user must assume the entire risk of using the source code. """ # Determine the dimensions of the source image dimensions = cfa.shape numberRows = dimensions[0] numberColumns = dimensions[1] if len(dimensions) == 3: numberBands = dimensions[2] else: numberBands = 1 dataType = cfa.dtype # If the provided source image is not greyscale, raise an error if numberBands != 1: msg = 'Provide CFA image must have only 1 band: %s' % numberBands raise ValueError(msg) # Construct color filter masks for each of the individual colors using # the provided mask pattern rMask = numpy.zeros((numberRows, numberColumns), dtype=int) bMask = numpy.zeros((numberRows, numberColumns), dtype=int) if pattern == 'GBRG': rMask[1:rMask.shape[0]:2,0:rMask.shape[1]:2] = 1 bMask[0:bMask.shape[0]:2,1:bMask.shape[1]:2] = 1 elif pattern == 'GRBG': rMask[0:rMask.shape[0]:2,1:rMask.shape[1]:2] = 1 bMask[1:bMask.shape[0]:2,0:bMask.shape[1]:2] = 1 elif pattern == 'BGGR': rMask[1:rMask.shape[0]:2,1:rMask.shape[1]:2] = 1 bMask[0:bMask.shape[0]:2,0:bMask.shape[1]:2] = 1 elif pattern == 'RGGB': rMask[0:rMask.shape[0]:2,0:rMask.shape[1]:2] = 1 bMask[1:bMask.shape[0]:2,1:bMask.shape[1]:2] = 1 else: msg = 'Invalid Bayer pattern provided: %s' % pattern raise ValueError(msg) gMask = numpy.ones((numberRows, numberColumns), dtype=int) - rMask - bMask # Create a floating-point array for each of the interpolated channels fCFA = cfa.astype(float) r = rMask * fCFA g = gMask * fCFA b = bMask * fCFA # Interpolate missing green values as the average of the surrounding # four (4) green filtered pixels interpolant = (numpy.roll(g, 1, axis=1) + \ numpy.roll(g, -1, axis=1) + \ numpy.roll(g, 1, axis=0) + \ numpy.roll(g, -1, axis=0)) / 4 index = numpy.where(gMask == 0) if len(index[0]) > 0: g[index] = interpolant[index] # Interpolate missing red values as the average of their horizontal # neighbors in the red/green rows, and as the average between these # interpolated rows interpolant = (numpy.roll(r, 1, axis=1) + \ numpy.roll(r, -1, axis=1)) / 2 index = numpy.where(rMask == 0) if len(index[0]) > 0: r[index] = interpolant[index] rMask += numpy.roll(rMask, 1, axis=1) interpolant = (numpy.roll(r, 1, axis=0) + \ numpy.roll(r, -1, axis=0)) / 2 index = numpy.where(rMask == 0) if len(index[0]) > 0: r[index] = interpolant[index] # Interpolate missing blue values as the average of their horizontal # neighbors in the blue/green rows, and as the average between these # interpolated rows interpolant = (numpy.roll(b, 1, axis=1) + \ numpy.roll(b, -1, axis=1)) / 2 index = numpy.where(bMask == 0) if len(index[0]) > 0: b[index] = interpolant[index] bMask += numpy.roll(bMask, 1, axis=1) interpolant = (numpy.roll(b, 1, axis=0) + \ numpy.roll(b, -1, axis=0)) / 2 index = numpy.where(bMask == 0) if len(index[0]) > 0: b[index] = interpolant[index] # Convert the floating-point interpolated values to match the input # CFA data type r = r.astype(dataType) g = g.astype(dataType) b = b.astype(dataType) # Return the interpolated color channels as a tuple return r, g, b