def imshow(im, windowName='Image', histogram=False, bitDepth=8, dpi=128.5): # Determine the numnber of grey levels numberLevels = 2**bitDepth # Normalize the image so that it falls in the range [0, 1] normalizedImage = (im / float(numberLevels-1)).astype(numpy.float32) # Reverse the channel order (BGR to RGB) for color images (assumption is # that OpenCV-style color images are provided) if len(im.shape) == 3: normalizedImage = cv2.cvtColor(normalizedImage, cv2.COLOR_BGR2RGB) # Determine the current screen size and set the figure size appropriately scale = 0.85 screenSize = hardware.get_screen_size(dpi=dpi) if histogram: figureSize = [screenSize[3]*scale, screenSize[2]*scale] else: figureSize = [screenSize[2]*scale, screenSize[2]*scale] # Create a Matplotlib figure with the given title and a canvas to contain # this figure figure = matplotlib.pyplot.figure(windowName, figsize=[figureSize[0], figureSize[1]]) canvas = matplotlib.backends.backend_agg.FigureCanvasAgg(figure) # Add one or two subplot axes to the figure depending on whether or not # the histogram is to be displayed if histogram: figureRows = 2 figureCols = 1 axes1 = figure.add_subplot(figureRows, figureCols, 1) axes2 = figure.add_subplot(figureRows, figureCols, 2) else: figureRows = 1 figureCols = 1 axes1 = figure.add_subplot(figureRows, figureCols, 1) # Add the image and the histogram (if requested) to the figure if len(im.shape) == 2: axes1.imshow(normalizedImage, cmap=matplotlib.cm.Greys_r) else: axes1.imshow(normalizedImage) axes1.axis('off') if histogram: h, pdf, cdf = ipcv.histogram(im, maxCount=numberLevels-1) axes2.set_xlim([0, numberLevels-1]) axes2.set_xlabel('DC') axes2.set_ylabel('PDF') if len(h) == 1: axes2.plot(range(numberLevels), pdf[0], 'k') else: for band in range(len(h)): axes2.plot(range(numberLevels), pdf[band]) # Display the figure to the screen matplotlib.pyplot.show()
def histogram_enhancement(im, etype='linear2', target=None, maxCount=255): """ Title: histogram_enhancement Author: Molly Hill, [email protected] Description: Returns given image quantized at set number of levels, using either a uniform or IGS method. Attributes: im - ndarray, can be grayscale or color of any size etype - enhancement type, of the following options: - 'linearX' where X is an integer percent of the area to be clipped/crushed with contrast increas - 'match' where the image is to be matched to a provided target image or PDF - 'equalize' where the image's histogram is to be spread equally across digital count target - if etype = 'match', then target must be provided as either an image (3D array) or PDF (1D array) maxCount - maximum code value of pixel; must be positive integer Requires: histogram.py, author: Carl Salvaggio dimensions.py, author: Carl Salvaggio """ if maxCount <= 0 or type(maxCount) is not int: msg = "Specified maximum digital count must be a positive integer." raise ValueError(msg) if type(im) is not numpy.ndarray: msg = "Specified image type must be ndarray." raise TypeError(msg) if etype[:6] != 'linear' and etype != 'match' and etype != 'equalize': msg = "Enhancement types available are linear, match, and equalize. Defaulting to linear2." print(msg) if etype == 'match': if type(target) == None: etype = 'equalize' msg = "If using match, target must be provided." print(msg) elif target.ndim !=1 and target.ndim !=2: print(target.ndim) msg = "Provided target must be PDF (1-D array) or image (3-D array)" raise TypeError(msg) enhIm = numpy.copy(im) srcCDF = ipcv.histogram(enhIm)[2] DCout = [] tgtCDF = [] tgtPDF = target if etype == 'match' or etype == 'equalize': if etype == 'match' and target.ndim != 1: #is image tgtCDF = ipcv.histogram(target)[2][0] #create CDF of target, currently does red channel if color else: #equalize or PDF passed in as target for matching if etype == 'equalize': tgtPDF = numpy.ones(maxCount+1)/(maxCount+1) tgtCDF = numpy.cumsum(tgtPDF) #convert PDF to CDF for i in range(ipcv.dimensions(srcCDF)[1]): #createLUT difference = numpy.fabs(numpy.subtract(tgtCDF,srcCDF[0][i])) #red channel only DCout.extend([int(maxCount*tgtCDF[numpy.argmin(difference)])]) for j in range(im.size): #apply LUT enhIm.flat[j] = DCout[enhIm.flat[j]] #uses original code value to assign new output from LUT else: #linear pct = (int(etype[6:])/2) / 100 #extract percent from etype and halve difference = numpy.fabs(numpy.subtract(srcCDF[0],pct)) DCmin = numpy.argmin(difference) difference = numpy.fabs(numpy.subtract(srcCDF[0],(1-pct))) DCmax = numpy.argmin(difference) slope = (maxCount+1)/(DCmax-DCmin) intercept = -slope * DCmin for j in range(enhIm.size): px = enhIm.flat[j] if px >= DCmax: px = maxCount elif px <= DCmin: px = 0 else: px = slope * px + intercept enhIm.flat[j] = int(px) return enhIm
# A greyscale test image filename = 'crowd.jpg' # A 3-channel color test image filename = 'lenna.tif' im = cv2.imread(filename, cv2.IMREAD_UNCHANGED) print('Data type = {0}'.format(type(im))) print('Image shape = {0}'.format(im.shape)) print('Image size = {0}'.format(im.size)) dataType = str(im.dtype) imType = {'uint8':8, 'uint16':16, 'uint32':32} startTime = time.time() h, pdf, cdf = ipcv.histogram(im, bitDepth=imType[dataType]) print('Elasped time = {0} [s]'.format(time.time() - startTime)) # The follow will produce a figure containing color-coded plots of the # computed histogram, probability function (PDF), and cumulative density # function (CDF) import matplotlib.pyplot import matplotlib.backends.backend_agg maxCount = 2**imType[dataType] bins = list(range(maxCount)) figure = matplotlib.pyplot.figure('Histogram') canvas = matplotlib.backends.backend_agg.FigureCanvas(figure)
import cv2 import ipcv import time # A greyscale test image #filename = 'crowd.jpg' # A 3-channel color test image filename = 'lenna.tiff' im = cv2.imread(filename, cv2.IMREAD_UNCHANGED) print('Data type = {0}'.format(type(im))) print('Image shape = {0}'.format(im.shape)) print('Image size = {0}'.format(im.size)) startTime = time.time() h, pdf, cdf = ipcv.histogram(im) print('Elapsed time = {0} [s]'.format(time.time() - startTime)) Max = range(256) if len(im.shape) == 3: [histR, histG, histB] = h [pdfR, pdfG, pdfB] = pdf [cdfR, cdfG, cdfB] = cdf matplotlib.pyplot.figure(1) matplotlib.pyplot.subplot(3, 1, 1) matplotlib.pyplot.ylabel('number of pixels') matplotlib.pyplot.xlabel('Digital Count') matplotlib.pyplot.plot(Max, histR, 'r-')
def histogram_enhancement(img, etype='linear2', target=None, maxCount=255, pool=False): """ :NAME: histogram_enchancement :PURPOSE: This method modifies an image using basic histogram enhancement techniques: linear -- removes removes extranesous pixels on outer edge of histogram and stretches histogram to fill entire DC range equalization -- a technique to even out peaks on the histogram and product a near-flat histogram curve match -- modifies an image such that it's pixel distribution mimics that of a target image or distribution :CATEGORY: ipcv -- histogram enchancement tool :CALLING SEQUENCE: quantizedImage = histogram_enchancement(img,\ etype=etype,\ target=targer,\ maxCount = maxCount,\ pool = pool) :INPUTS: img [numpy.ndarray] input image etype [string] type of histogram enhancement to perform target [numpy.ndarray] target image or distribution using in histogram matching maxCount [int] the largest possible DC value in the image pool [boolean] whether or not to pool all colors into one cdf or loop by band :RETURN VALUE: a numpy array containing the enhanced image :SIDE EFFECTS: removes possibly pertinent data in an image :ERROR CHECKING: ValueError TypeError :REQUIRES: numpy sys.exc_info os.path.split :MODIFICATION HISTORY: Engineer: Jeff Maggio original: 09/09/16 """ #ERROR CHECKING if etype == "match": if (isinstance(target, np.ndarray) == False): print( "-------------------------------------------------------------------" ) print( "input 'target' must by a valid numpy.ndarray, currently {0}". format(type(target))) print( "-------------------------------------------------------------------" ) print("raising TypeError...") raise TypeError if isinstance(maxCount, int) == False: print( "-------------------------------------------------------------------" ) print("input 'maxCount' must be an int type, currently is {0}".format( type(target))) print( "-------------------------------------------------------------------" ) elif maxCount < 0: print( "-------------------------------------------------------------------" ) print("maxCount must be greater than 0, currently is {0}".format( maxCount)) print( "-------------------------------------------------------------------" ) if isinstance(img, np.ndarray) == True: img = img.reshape(img.shape[0], img.shape[1], 1) if (len(img.shape) == 2) else img bands = img.shape[2] if isinstance(target, np.ndarray) == True: target = target.reshape(target.shape[0], target.shape[1], 1) if (len( target.shape) == 2) else target if etype == "match": if (len(target.shape) != 1) and (target.shape[2] != img.shape[2]): print( "-------------------------------------------------------------------" ) print( "original and target images must both be of the same type (grayscale or color" ) print("raising TypeError...") print( "-------------------------------------------------------------------" ) raise TypeError #BEGIN ACTUAL WORK try: #2 is index of cdf from return tuple if "linear" in etype: for band in range(bands): #cdf is recalculated by band cdf = ipcv.histogram(img=img,channels=band,histSize=(maxCount+1),\ ranges=[0,maxCount+1],returnType=1)[2] # generating components of the line lowerBound = (float(etype.replace("linear", "")) / 200.0 ) #1/2 input on each side upperbound = (1 - lowerBound) dcLow = np.where(cdf >= lowerBound)[0][0] dcHigh = np.where(cdf <= upperbound)[0][-1] m = (maxCount / (dcHigh - dcLow)) b = maxCount - (m * dcHigh) #generating the lookup table by applying a linear transform LUT = (m * np.arange(maxCount + 1)) + b LUT = np.clip(LUT, 0, 255) print(img[:, :, band].shape) img[:, :, band] = LUT[img[:, :, band]] elif etype == 'equalize': for band in range(bands): cdf = ipcv.histogram(img=img,channels=band,histSize=(maxCount+1),\ ranges=[0,maxCount+1],returnType=1)[2] LUT = (cdf * maxCount).flatten() print(LUT.shape) img[:, :, band] = LUT[img[:, :, band]] elif etype == "match": #Generating the target CDFs tCdf = [] if len(target.shape) == 1: for band in range(bands): tCdf.append(np.cumsum(target)) else: for band in range(bands): tCdf.append(ipcv.histogram(img=target,channels=band,histSize=(maxCount+1),\ ranges=[0,maxCount+1],returnType=1)[2]) #only return the cdf here for band in range(bands): cdf = ipcv.histogram(img=img,channels=band,histSize=(maxCount+1),\ ranges=[0,maxCount+1],returnType=1)[2] LUT = np.zeros(maxCount + 1) index = 0 for percentage in cdf: upperValues = np.where(tCdf[band] >= percentage) if upperValues[0].size != 0: dc = upperValues[0][0] else: dc = 0 LUT[index] = dc index += 1 img[:, :, band] = LUT[img[:, :, band]] return img.astype(np.uint8) except Exception as e: print( "===================================================================" ) exc_type, exc_obj, exc_tb = exc_info() fname = split(exc_tb.tb_frame.f_code.co_filename)[1] print("\nfile: {0}\n\nline: {1} \n\n{2}\n".format( fname, exc_tb.tb_lineno, e)) print( "===================================================================" )
def otsu_threshold(img, maxCount=255, verbose=False): """ :NAME: otsu_threshold :PURPOSE: this method generates a binary thresholded image based off of Otsu's class discrimination method :CATEGORY: ipcv -- object recognition and thresholding tool :CALLING SEQUENCE: quantizedImage = quantize(img = inputImage,\ maxCount = max display level\ verbose = true or false) :INPUTS: img [numpy.ndarray] input image to be quanitized maxCount [int] maximum pixel value in the output array verbose [boolean] whether or not to graph the histogram :RETURN VALUE: tuple containing: returnTuple[0] -- binary numpy.array of the same shape as the input image returnTuple[1] -- threshold determined by otsu's method :ERROR CHECKING: TypeError ValueError RuntimeError :REQUIRES: np sys.exc_info os.path.split ipcv :MODIFICATION HISTORY: Engineer: Jeff Maggio 08/25/16: otsu code """ ###################### BEGIN ERROR CHECKING ####################### if isinstance(img,np.ndarray) == True: dims = ipcv.dimensions(img,"dictionary") if dims["bands"] == 1: if len(img.shape) == 3: #making the array two dimensional if it only has 1 band img = img.reshape(dims['rows'],dims['cols']) else: print("") print("input 'img' must be grayscale or single-banded") print("") raise RuntimeError else: print("") print("input 'img' must be a valid 2 dimensional numpy array, currently {0}".format(type(img))) print("") raise TypeError if isinstance(maxCount,int) == False: print("") print("input 'maxCount' must be an int, currently {0}".format(type(maxCount))) print("") raise TypeError elif maxCount <= 0: print("") print("input 'maxCount' must be greater than zero") print("") raise ValueError if isinstance(verbose,bool) == False: print("") print("input 'verbose' must be integer boolean , currently {0}".format(verbose)) print("") raise TypeError ###################### END ERROR CHECKING ####################### try: numberCounts = maxCount + 1 #generating the pdf and cdf hist,pdf,cdf = ipcv.histogram(img,histSize=numberCounts,ranges=[0,numberCounts]) meanLevelArray = np.cumsum( np.arange(0,numberCounts) * pdf ) muTotal = meanLevelArray[-1] sigmaSquaredBValues = np.zeros(numberCounts) startingK = np.where(cdf>0.0)[0][0] endingK = np.where(cdf<1.0)[0][-1] for k in range(startingK, endingK): muK = meanLevelArray[k] omegaK = cdf[k] sigmaSquaredB = ( ( (muTotal * omegaK) - muK )**2 ) / ( omegaK * (1-omegaK) ) sigmaSquaredBValues[k] = sigmaSquaredB threshold = np.argmax(sigmaSquaredBValues) LUT = np.zeros( numberCounts ) LUT[threshold+1:] = 1 img = LUT[img] if verbose == True: values = (pdf,) colors = ('r',) filename = "image_pdf_wOtsu.eps" thresholdMarker = (threshold,) labels = ("pdf",) graph = ipcv.quickplot(values,colors,labels,filename=filename,verticalMarkers=thresholdMarker,\ xLabel="Digital Counts",yLabel="probability",display=False) graph.annotate("otsu's Threshold", xy=(threshold, .01), xytext=(3, 1.5),arrowprops=dict(facecolor='black', shrink=0.05)) graph.show() return img.astype(np.uint8), threshold except Exception as e: print("===============================================================") exc_type, exc_obj, tb = exc_info() fname = split(tb.tb_frame.f_code.co_filename)[1] print("\r\nfile: {0}\r\n\r\nline: {1} \r\n\r\n{2}\r\n\r\n".format(fname,tb.tb_lineno,exc_obj,e)) print("===============================================================")
def otsu_threshold(im, maxCount=255, verbose=False): """ Title: otsu_threshold Author: Molly Hill, [email protected] Description: Finds threshold for foreground/background in given image Returns: matte - binary image indicating areas above/below threshold kOpt - optimum threshold for given image Attributes: im - source image; ndarray, must be grayscale maxCount - maximum code value of pixel; must be positive integer verbose - if True, plots histogram of image, marked with optimum threshold Requires: histogram.py, author: Carl Salvaggio """ if maxCount <= 0 or type(maxCount) is not int: msg = "Specified maximum digital count must be a positive integer." raise ValueError(msg) if type(im) is not numpy.ndarray: msg = "Specified image type must be ndarray." raise TypeError(msg) if type(verbose) is not bool: msg = "Verbose must be a boolean. Defaulting to False." print(msg) #initialize variables srcIm = numpy.copy(im) histo = ipcv.histogram(srcIm)[0] srcPDF = ipcv.histogram(srcIm)[1][0] matte = [] stdDev = [] kOpt = 0 firstMtot = [] indices = numpy.arange(maxCount + 1) for i in range(numpy.size(indices)): firstMtot.extend([srcPDF[i] * indices[i] ]) #pre-sum first moment across image meanLvl = numpy.sum(firstMtot) #mean level of image for k in range(0, maxCount): zeroM = numpy.sum(srcPDF[0:k]) #zero moment if zeroM == 0 or zeroM == 1: stdDev.extend([0]) else: firstM = numpy.sum(firstMtot[:k]) #first moment up to k stdDev.extend([ (((meanLvl * zeroM) - firstM)**2) // (zeroM * (1 - zeroM)) ]) kOpt = numpy.argmax(stdDev) + 1 matte = numpy.uint8(1 * (im >= kOpt)) #get as Booleans and convert to integers if verbose == True: #plot histogram and threshold #NOTE THIS SLOWS EVERYTHING DOWN BY A LOT matplotlib.pyplot.plot(indices, histo[0]) matplotlib.pyplot.ylabel('Number of Pixels') matplotlib.pyplot.xlabel('Digital Count') matplotlib.pyplot.ylim([0, max(histo[0])]) matplotlib.pyplot.xlim([0, maxCount]) matplotlib.pyplot.axvline(x=kOpt, label='Threshold' + str(kOpt), color='r') matplotlib.pyplot.show() return matte, kOpt