使用 OpenCV 自动调整一张纸的彩色照片的对比度和亮度

2024-01-12

当拍摄一张纸时(例如使用手机摄像头),我得到以下结果(左图)(jpg下载here https://i.stack.imgur.com/mY7ep.jpg)。所需的结果(使用图像编辑软件手动处理)在右侧:

我想用 openCV 处理原始图像以获得更好的亮度/对比度自动地(让背景更白).

假设:图像具有 A4 纵向格式(在本主题中,我们不需要对其进行透视变形),并且纸张是白色的,可能包含黑色或彩色的文本/图像。

到目前为止我尝试过的:

  1. Various 自适应阈值处理方法,例如 Gaussian、OTSU(请参阅 OpenCV 文档图像阈值处理 https://docs.opencv.org/3.4.0/d7/d4d/tutorial_py_thresholding.html)。它通常与 OTSU 配合良好:

    ret, gray = cv2.threshold(img, 0, 255, cv2.THRESH_OTSU + cv2.THRESH_BINARY)
    

    但它只适用于灰度图像而不是直接用于彩色图像。而且,输出是二进制(白色或黑色),这是我不想要的:我更喜欢保留彩色非二进制图像作为输出

  2. 直方图均衡化 https://en.wikipedia.org/wiki/Histogram_equalization

    • 应用于 Y(RGB => YUV 变换后)
    • 或应用于 V(RGB => HSV 变换后),

    正如这所建议的answer https://stackoverflow.com/a/15009815/1422096 (直方图均衡不适用于彩色图像 - OpenCV https://stackoverflow.com/questions/15007304/histogram-equalization-not-working-on-color-image-opencv) 或这个one https://stackoverflow.com/a/38312281/1422096 (OpenCV Python equalizeHist 彩色图像 https://stackoverflow.com/questions/31998428/opencv-python-equalizehist-colored-image):

    img3 = cv2.imread(f)
    img_transf = cv2.cvtColor(img3, cv2.COLOR_BGR2YUV)
    img_transf[:,:,0] = cv2.equalizeHist(img_transf[:,:,0])
    img4 = cv2.cvtColor(img_transf, cv2.COLOR_YUV2BGR)
    cv2.imwrite('test.jpg', img4)
    

    或单纯疱疹病毒:

    img_transf = cv2.cvtColor(img3, cv2.COLOR_BGR2HSV)
    img_transf[:,:,2] = cv2.equalizeHist(img_transf[:,:,2])
    img4 = cv2.cvtColor(img_transf, cv2.COLOR_HSV2BGR)
    

    不幸的是,结果非常糟糕,因为它在局部产生了可怕的微观对比(?):

    我也尝试了YCbCr,效果类似。

  3. 我也尝试过CLAHE(对比度有限自适应直方图均衡) https://docs.opencv.org/3.1.0/d5/daf/tutorial_py_histogram_equalization.html与各种tileGridSize from 1 to 1000:

    img3 = cv2.imread(f)
    img_transf = cv2.cvtColor(img3, cv2.COLOR_BGR2HSV)
    clahe = cv2.createCLAHE(tileGridSize=(100,100))
    img_transf[:,:,2] = clahe.apply(img_transf[:,:,2])
    img4 = cv2.cvtColor(img_transf, cv2.COLOR_HSV2BGR)
    cv2.imwrite('test.jpg', img4)
    

    但结果也同样糟糕。

  4. 按照问题中的建议,使用 LAB 颜色空间执行此 CLAHE 方法如何在 RGB 彩色图像上应用 CLAHE https://stackoverflow.com/questions/25008458/how-to-apply-clahe-on-rgb-color-images:

    import cv2, numpy as np
    bgr = cv2.imread('_example.jpg')
    lab = cv2.cvtColor(bgr, cv2.COLOR_BGR2LAB)
    lab_planes = cv2.split(lab)
    clahe = cv2.createCLAHE(clipLimit=2.0,tileGridSize=(100,100))
    lab_planes[0] = clahe.apply(lab_planes[0])
    lab = cv2.merge(lab_planes)
    bgr = cv2.cvtColor(lab, cv2.COLOR_LAB2BGR)
    cv2.imwrite('_example111.jpg', bgr)
    

    也给出了不好的结果。输出图像:

  5. 进行自适应阈值处理或直方图均衡每个通道分别(R、G、B)不是一个选项,因为它会扰乱色彩平衡,如所解释的here https://stackoverflow.com/questions/15007304/histogram-equalization-not-working-on-color-image-opencv/15009815.

  6. “对比拉伸”方法来自scikit-image的教程直方图均衡 https://scikit-image.org/docs/dev/auto_examples/color_exposure/plot_equalize.html:

    图像被重新缩放以包含第 2 个和第 98 个百分位数内的所有强度

    稍微好一点,但距离期望的结果还很远(参见这个问题顶部的图片)。


TL;DR:如何使用 OpenCV/Python 对一张纸的彩色照片进行自动亮度/对比度优化?可以使用什么样的阈值/直方图均衡/其他技术?


可以使用 alpha (α)和贝塔(β), 分别。这些变量通常被称为gain and bias参数。表达式可以写成

OpenCV 已经将其实现为cv2.convertScaleAbs() https://docs.opencv.org/2.4/modules/core/doc/operations_on_arrays.html#convertscaleabs所以我们可以将这个函数与用户定义的一起使用alpha and beta values.

import cv2

image = cv2.imread('1.jpg')

alpha = 1.95 # Contrast control (1.0-3.0)
beta = 0 # Brightness control (0-100)

manual_result = cv2.convertScaleAbs(image, alpha=alpha, beta=beta)

cv2.imshow('original', image)
cv2.imshow('manual_result', manual_result)
cv2.waitKey()

但问题是

如何自动优化彩色照片的亮度/对比度?

本质上问题是如何自动计算alpha and beta。为此,我们可以查看图像的直方图。自动亮度和对比度优化计算 alpha 和 beta,使输出范围为[0...255]。我们计算累积分布以确定颜色频率小于某个阈值(例如 1%)的位置,并剪切直方图的右侧和左侧。这给了我们最小和最大范围。这是剪切之前(蓝色)和剪切之后(橙色)的直方图的可视化。请注意图像中更“有趣”的部分在剪切后如何更加明显。

计算alpha,我们取裁剪后的最小和最大灰度范围,并将其除以我们所需的输出范围255

α = 255 / (maximum_gray - minimum_gray)

为了计算 beta,我们将其代入公式,其中g(i, j)=0 and f(i, j)=minimum_gray

g(i,j) = α * f(i,j) + β

解决后的结果是这样的

β = -minimum_gray * α

对于您的图像,我们得到这个

阿尔法:3.75

贝塔值:-311.25

您可能需要调整剪切阈值以优化结果。以下是对其他图像使用 1% 阈值的一些示例结果:-> After

自动亮度和对比度代码

import cv2
import numpy as np
from matplotlib import pyplot as plt

# Automatic brightness and contrast optimization with optional histogram clipping
def automatic_brightness_and_contrast(image, clip_hist_percent=1):
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    
    # Calculate grayscale histogram
    hist = cv2.calcHist([gray],[0],None,[256],[0,256])
    hist_size = len(hist)
    
    # Calculate cumulative distribution from the histogram
    accumulator = []
    accumulator.append(float(hist[0]))
    for index in range(1, hist_size):
        accumulator.append(accumulator[index -1] + float(hist[index]))
    
    # Locate points to clip
    maximum = accumulator[-1]
    clip_hist_percent *= (maximum/100.0)
    clip_hist_percent /= 2.0
    
    # Locate left cut
    minimum_gray = 0
    while accumulator[minimum_gray] < clip_hist_percent:
        minimum_gray += 1
    
    # Locate right cut
    maximum_gray = hist_size -1
    while accumulator[maximum_gray] >= (maximum - clip_hist_percent):
        maximum_gray -= 1
    
    # Calculate alpha and beta values
    alpha = 255 / (maximum_gray - minimum_gray)
    beta = -minimum_gray * alpha
    
    '''
    # Calculate new histogram with desired range and show histogram 
    new_hist = cv2.calcHist([gray],[0],None,[256],[minimum_gray,maximum_gray])
    plt.plot(hist)
    plt.plot(new_hist)
    plt.xlim([0,256])
    plt.show()
    '''

    auto_result = cv2.convertScaleAbs(image, alpha=alpha, beta=beta)
    return (auto_result, alpha, beta)

image = cv2.imread('1.jpg')
auto_result, alpha, beta = automatic_brightness_and_contrast(image)
print('alpha', alpha)
print('beta', beta)
cv2.imshow('auto_result', auto_result)
cv2.waitKey()

使用此代码的结果图像:

使用 1% 阈值的其他图像的结果

替代版本是添加gain and bias使用饱和度算法而不是使用 OpenCV 的图像cv2.convertScaleAbs()。内置方法不采用绝对值,这会导致无意义的结果(例如,在 alpha = 3 且 beta = -210 的 44 处的像素在 OpenCV 中变为 78,而实际上它应该变为 0)。

import cv2
import numpy as np
# from matplotlib import pyplot as plt

def convertScale(img, alpha, beta):
    """Add bias and gain to an image with saturation arithmetics. Unlike
    cv2.convertScaleAbs, it does not take an absolute value, which would lead to
    nonsensical results (e.g., a pixel at 44 with alpha = 3 and beta = -210
    becomes 78 with OpenCV, when in fact it should become 0).
    """

    new_img = img * alpha + beta
    new_img[new_img < 0] = 0
    new_img[new_img > 255] = 255
    return new_img.astype(np.uint8)

# Automatic brightness and contrast optimization with optional histogram clipping
def automatic_brightness_and_contrast(image, clip_hist_percent=25):
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

    # Calculate grayscale histogram
    hist = cv2.calcHist([gray],[0],None,[256],[0,256])
    hist_size = len(hist)

    # Calculate cumulative distribution from the histogram
    accumulator = []
    accumulator.append(float(hist[0]))
    for index in range(1, hist_size):
        accumulator.append(accumulator[index -1] + float(hist[index]))

    # Locate points to clip
    maximum = accumulator[-1]
    clip_hist_percent *= (maximum/100.0)
    clip_hist_percent /= 2.0

    # Locate left cut
    minimum_gray = 0
    while accumulator[minimum_gray] < clip_hist_percent:
        minimum_gray += 1

    # Locate right cut
    maximum_gray = hist_size -1
    while accumulator[maximum_gray] >= (maximum - clip_hist_percent):
        maximum_gray -= 1

    # Calculate alpha and beta values
    alpha = 255 / (maximum_gray - minimum_gray)
    beta = -minimum_gray * alpha

    '''
    # Calculate new histogram with desired range and show histogram 
    new_hist = cv2.calcHist([gray],[0],None,[256],[minimum_gray,maximum_gray])
    plt.plot(hist)
    plt.plot(new_hist)
    plt.xlim([0,256])
    plt.show()
    '''

    auto_result = convertScale(image, alpha=alpha, beta=beta)
    return (auto_result, alpha, beta)

image = cv2.imread('1.jpg')
auto_result, alpha, beta = automatic_brightness_and_contrast(image)
print('alpha', alpha)
print('beta', beta)
cv2.imshow('auto_result', auto_result)
cv2.imwrite('auto_result.png', auto_result)
cv2.imshow('image', image)
cv2.waitKey()
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

使用 OpenCV 自动调整一张纸的彩色照片的对比度和亮度 的相关文章

随机推荐