Python 检测图像中不均匀照明的鲁棒算法[仅需检测]

Python 检测图像中不均匀照明的鲁棒算法[仅需检测],python,algorithm,opencv,image-processing,computer-vision,Python,Algorithm,Opencv,Image Processing,Computer Vision,tesseract OCR文本识别面临的最大挑战之一是图像照明不均匀。 我需要一个算法,可以决定图像是否包含不均匀照明 测试图像 我附上了无照明图像、眩光图像(白点图像)和阴影图像。 如果我们给算法一个图像,算法应该分为两类 无不均匀照明-我们的无照明图像将属于此类别 照明不均匀-我们的眩光图像(白点图像),包含阴影的图像将属于这一类 无照明图像-A类 不均匀照明图像(眩光图像(白点图像))B类 不均匀照明图像(包含图像的阴影)类别B 初始方法 将颜色空间更改为HSV 对HSV值通道进行

tesseract OCR文本识别面临的最大挑战之一是图像照明不均匀。 我需要一个算法,可以决定图像是否包含不均匀照明

测试图像 我附上了
无照明图像
眩光图像(白点图像)
阴影图像
。 如果我们给算法一个图像,算法应该分为两类

  • 无不均匀照明-我们的
    无照明图像
    将属于此类别
  • 照明不均匀-我们的
    眩光图像(白点图像)
    包含阴影的图像
    将属于这一类
  • 无照明图像-A类

    不均匀照明图像(眩光图像(白点图像))B类

    不均匀照明图像(包含图像的阴影)类别B

    初始方法
  • 将颜色空间更改为HSV

  • 对HSV值通道进行直方图分析,以识别不均匀照明

    def get_image_stats(img_path, lbl):
        img = cv2.imread(img_path)
        gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
        blurred = cv2.GaussianBlur(gray, (25, 25), 0)
        no_text = gray * ((gray/blurred)>0.99)                     # select background only
        no_text[no_text<10] = no_text[no_text>20].mean()           # convert black pixels to mean value
        no_bright = no_text.copy()
        no_bright[no_bright>220] = no_bright[no_bright<220].mean() # disregard bright pixels
    
        print(lbl)
        std = no_bright.std()
        print('STD:', std)
        bright = (no_text>220).sum()
        print('Brigth pixels:', bright)
        plt.figure()
        plt.hist(no_text.reshape(-1,1), 25)
        plt.title(lbl)
    
        if std>25:
            print("!!! Detected uneven illumination")
        if no_text.mean()<200 and bright>8000:
            print("!!! Detected glare")
    
  • 我们可以使用感知亮度来代替前两步 渠道而非HSV的价值渠道

  • 设置低阈值以获取小于低阈值的像素数

  • 设置高阈值以获取高于高阈值的像素数

  • 检测不均匀闪电条件的低像素值百分比和高像素值百分比(百分比的设置阈值)

    但我找不到不均匀照明之间有什么相似之处 图像。我刚刚发现有些像素的值很低,而且 通过直方图分析,某些像素具有较高的值

    基本上,我的感觉是,如果设置一些低阈值,找出有多少像素小于低阈值,然后设置一些高阈值,找出有多少像素大于该阈值。通过像素计数,我们可以得出结论来检测图像中的不均匀闪电情况吗?在这里,我们需要最终确定两个阈值和像素数的百分比才能得出结论

    def显示历史(img路径):
    img=cv2.imread(img\u路径)
    hsv_img=cv2.cvt颜色(img,cv2.COLOR_BGR2HSV)
    h、 s,v=cv2.分割(hsv\U img)
    histr=cv2.calcHist(v[0],无[255],[0255])
    plt.绘图(历史)
    plt.show()
    低阈值=np计数非零(v<50)
    高阈值=np计数非零(v>200)
    总像素=img.shape[0]*img.shape[1]
    percenet_low=低阈值/总像素*100
    percenet_high=高阈值/总像素*100
    打印(“总像素-{}\n像素大于200-{}\n像素小于50-{}\n像素百分比大于200-{}\n像素百分比小于50-{}\n”。格式(总像素,高阈值,低阈值,低阈值,低阈值,高阈值)
    返回总像素、高阈值、低阈值、percenet低、percenet高
    
    那么,是否有人可以改进我最初的方法,或者提供比这种方法更好的方法来检测一般情况下图像中的不均匀照明?

    此外,我尝试了感知亮度,而不是价值通道,因为价值通道取(b,g,r)值的最大值。我认为感知亮度是一个不错的选择

    def get_sense_亮度(float_img):
    float_img=np。float64(float_img)#单元8将产生溢出
    b、 g,r=cv2.分割(浮动)
    浮动亮度=np.sqrt(
    (0.241*(r**2))+(0.691*(g**2))+(0.068*(b**2)))
    亮度\通道=np.uint8(np.absolute(浮点\亮度))
    返回亮度通道
    def显示历史(img路径):
    img=cv2.imread(img\u路径)
    v=获取亮度(img)
    histr=cv2.calcHist(v[0],无[255],[0255])
    plt.绘图(历史)
    plt.show()
    低阈值=np计数非零(v<50)
    高阈值=np计数非零(v>200)
    总像素=img.shape[0]*img.shape[1]
    percenet_low=低阈值/总像素*100
    percenet_high=高阈值/总像素*100
    打印(“总像素-{}\n像素大于200-{}\n像素小于50-{}\n像素百分比大于200-{}\n像素百分比小于50-{}\n”。格式(总像素,高阈值,低阈值,低阈值,低阈值,高阈值)
    返回总像素、高阈值、低阈值、percenet低、percenet高
    
    感知亮度通道的直方图分析

    正如艾哈迈特建议的那样。

    def get_二进制像素百分比(img=None,img_path=None):
    如果img为无:
    如果img_路径不是无:
    gray\u img=cv2.imread(img\u路径,0)
    其他:
    返回“无img或img_路径”
    其他:
    打印(图像形状)
    如果长度(图像形状)>2:
    灰色\u img=cv2.CVT颜色(img,cv2.COLOR\u bgr2灰色)
    其他:
    灰色\u img=img
    h、 w=灰色形状
    高斯模糊=cv2.GaussianBlur(灰度(5,5,0)
    阈值,大津img=cv2。阈值(高斯模糊,0,255,
    cv2.THRESH_二进制+cv2.THRESH_大津)
    imwrite(“binary/{}”.format(img_path.split('/')[-1]),otsu_img)
    黑色像素=np。计数非零(大津img==0)
    #白色像素=np。计数非零(大津img==255)
    黑色像素百分比=黑色像素/(高*宽)*100
    #白色像素百分比=白色像素/(高*宽)*100
    返回黑色像素百分比
    
    当我们通过大津二值化得到35%以上的黑色像素百分比时,我们可以检测到80%左右的不均匀照明图像。当照明发生在i的一个小区域时
    import cv2
    import numpy as np
    import skimage.filters as filters
    
    # read the image
    img = cv2.imread('8W0bp.jpg')
    #img = cv2.imread('ob87W.jpg')
    #img = cv2.imread('HLJuA.jpg')
    
    # convert to gray
    gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
    
    # blur
    smooth = cv2.GaussianBlur(gray, (33,33), 0)
    
    # divide gray by morphology image
    division = cv2.divide(gray, smooth, scale=255)
    
    # sharpen using unsharp masking
    sharp = filters.unsharp_mask(division, radius=1.5, amount=2.5, multichannel=False, preserve_range=False)
    sharp = (255*sharp).clip(0,255).astype(np.uint8)
    
    # save results
    cv2.imwrite('8W0bp_division.jpg',division)
    cv2.imwrite('8W0bp_division_sharp.jpg',sharp)
    #cv2.imwrite('ob87W_division.jpg',division)
    #cv2.imwrite('ob87W_division_sharp.jpg',sharp)
    #cv2.imwrite('HLJuA_division.jpg',division)
    #cv2.imwrite('HLJuA_division_sharp.jpg',sharp)
    
    # show results
    cv2.imshow('smooth', smooth)  
    cv2.imshow('division', division)  
    cv2.imshow('sharp', sharp)  
    cv2.waitKey(0)
    cv2.destroyAllWindows()
    
    def get_image_stats(img_path, lbl):
        img = cv2.imread(img_path)
        gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
        blurred = cv2.GaussianBlur(gray, (25, 25), 0)
        no_text = gray * ((gray/blurred)>0.99)                     # select background only
        no_text[no_text<10] = no_text[no_text>20].mean()           # convert black pixels to mean value
        no_bright = no_text.copy()
        no_bright[no_bright>220] = no_bright[no_bright<220].mean() # disregard bright pixels
    
        print(lbl)
        std = no_bright.std()
        print('STD:', std)
        bright = (no_text>220).sum()
        print('Brigth pixels:', bright)
        plt.figure()
        plt.hist(no_text.reshape(-1,1), 25)
        plt.title(lbl)
    
        if std>25:
            print("!!! Detected uneven illumination")
        if no_text.mean()<200 and bright>8000:
            print("!!! Detected glare")
    
     good_img
    STD: 11.264569863071165
    Brigth pixels: 58
    
     glare_img
    STD: 15.00149131296984
    Brigth pixels: 15122
    !!! Detected glare
    
     uneven_img
    STD: 57.99510339944441
    Brigth pixels: 688
    !!! Detected uneven illumination
    
    %matplotlib inline
    import numpy as np
    import cv2
    from matplotlib import pyplot as plt
    from scipy.signal import find_peaks 
    
    def get_perceived_brightness( float_img):
        float_img = np.float64(float_img)  # unit8 will make overflow
        b, g, r = cv2.split(float_img)
        float_brightness = np.sqrt((0.241 * (r ** 2)) + (0.691 * (g ** 2)) + (0.068 * (b ** 2)))
        brightness_channel = np.uint8(np.absolute(float_brightness))
        return brightness_channel
        
    # from: https://stackoverflow.com/questions/46300577/find-locale-minimum-in-histogram-1d-array-python
    def smooth(x,window_len=11,window='hanning'):
        if x.ndim != 1:
            raise ValueError("smooth only accepts 1 dimension arrays.")
    
        if x.size < window_len:
            raise ValueError("Input vector needs to be bigger than window size.")
    
        if window_len<3:
            return x
    
        if not window in ['flat', 'hanning', 'hamming', 'bartlett', 'blackman']:
            raise ValueError("Window is on of 'flat', 'hanning', 'hamming', 'bartlett', 'blackman'")
    
        s=np.r_[x[window_len-1:0:-1],x,x[-2:-window_len-1:-1]]
    
        if window == 'flat': #moving average
            w=np.ones(window_len,'d')
        else:
            w=eval('np.'+window+'(window_len)')
    
        y=np.convolve(w/w.sum(),s,mode='valid')
        return y
        
    
    image_file_name = 'im3.jpg'
    image = cv2.imread(image_file_name)
    
    # image category
    category = 0
    
    # gray convertion
    image_gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    
    height = image.shape[0]
    width = image.shape[1]
    
    # First test. Does the image have any big white spots?
    saturation_thresh = 250
    raw_saturation_region = cv2.threshold(image_gray, saturation_thresh, 255,  cv2.THRESH_BINARY)[1]
    num_raw_saturation_regions, raw_saturation_regions,stats, _ = cv2.connectedComponentsWithStats(raw_saturation_region)
    
    # index 0 is the background -> to remove
    area_raw_saturation_regions = stats[1:,4]
    
    min_area_bad_spot = 1000 # this can be calculated as percentage of the image area
    if (np.max(area_raw_saturation_regions) > min_area_bad_spot):
        category = 2 # there is at least one spot
    
    # Second test. Is the image dark?   
    min_mean_intensity = 60
    
    if category == 0 :    
        mean_intensity = np.mean(image_gray)
    
        if (mean_intensity < min_mean_intensity):
            category = 3 # dark image
            
    
    window_len = 15 # odd number
    delay = int((window_len-1)/2)  # delay is the shift introduced from the smoothing. It's half window_len
    
    # for example if the window_len is 15, the delay is 7
    # infact hist.shape = 256 and smooted_hist.shape = 270 (= 256 + 2*delay)
    
    if category == 0 :  
        perceived_brightness = get_perceived_brightness(image)
        hist,bins = np.histogram(perceived_brightness.ravel(),256,[0,256])
    
        # smoothed_hist is shifted from the original one    
        smoothed_hist = smooth(hist,window_len)
        
        # smoothed histogram syncronized with the original histogram
        sync_smoothed_hist = smoothed_hist[delay:-delay]    
        
        # if number the peaks with:
        #    20<bin<250
        #    prominance >= mean histogram value
        # the image could have shadows (but it could have also a background with some colors)
        mean_hist = int(height*width / 256)
    
        peaks, _ = find_peaks(sync_smoothed_hist, prominence=mean_hist)
        
        selected_peaks = peaks[(peaks > 20) & (peaks < 250)]
        
        if (selected_peaks.size>1) :
            category = 4 # there are shadows
    
    # all tests are passed. The image is ok
    if (category == 0) :
        category=1 # the image is ok