Python 检测图像中不均匀照明的鲁棒算法[仅需检测]
tesseract OCR文本识别面临的最大挑战之一是图像照明不均匀。 我需要一个算法,可以决定图像是否包含不均匀照明 测试图像 我附上了Python 检测图像中不均匀照明的鲁棒算法[仅需检测],python,algorithm,opencv,image-processing,computer-vision,Python,Algorithm,Opencv,Image Processing,Computer Vision,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")
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