簡體   English   中英

使用 OpenCV 自動調整一張紙的彩色照片的對比度和亮度

[英]Automatic contrast and brightness adjustment of a color photo of a sheet of paper with OpenCV

拍攝一張紙時(例如使用手機相機),我得到以下結果(左圖)(jpg在這里下載)。 所需的結果(使用圖像編輯軟件手動處理)在右側:

我想用 openCV 處理原始圖像以自動獲得更好的亮度/對比度(使背景更白)

假設:圖像具有 A4 縱向格式(我們不需要在本主題中對其進行透視變形),並且這張紙是白色的,可能帶有黑色或彩色的文本/圖像。

到目前為止我嘗試過的:

  1. 各種自適應閾值方法,例如 Gaussian、OTSU(請參閱 OpenCV 文檔圖像閾值)。 它通常適用於 OTSU:

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

    但它僅適用於灰度圖像,不能直接用於彩色圖像。 此外,輸出是二進制(白色或黑色),我不想要:我更喜歡保留彩色非二進制圖像作為輸出

  2. 直方圖均衡

    • 應用於 Y(RGB => YUV 變換后)
    • 或應用於 V(RGB => HSV 變換后),

    如本建議答案直方圖均衡化不是彩色圖像的工作- OpenCV的)或該一個OpenCV的Python的equalizeHist彩色圖像):

     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)

    或與HSV:

     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(對比度限制自適應直方圖均衡化),使用從11000各種tileGridSize

     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 方法,如問題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)上分別做一個自適應閾值或直方圖均衡是不因為它會弄亂顏色平衡,如所解釋的選項這里

  6. 來自scikit-image直方圖均衡化教程的“對比度拉伸”方法:

    圖像被重新縮放以包括落在第 2 個和第 98 個百分位數內的所有強度

    好一點,但仍遠未達到預期的結果(請參閱此問題頂部的圖片)。


TL;DR:如何使用 OpenCV/Python 對一張紙的彩色照片進行自動亮度/對比度優化? 可以使用什么樣的閾值/直方圖均衡/其他技術?

在此處輸入圖片說明 在此處輸入圖片說明

亮度和對比度可以分別使用 alpha (α) 和 beta (β) 進行調整。 表達式可以寫成

在此處輸入圖片說明

OpenCV 已經將其作為cv2.convertScaleAbs()因此我們可以僅將此函數與用戶定義的alphabeta值一起使用。

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

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()

但問題是

如何獲得彩色照片的自動亮度/對比度優化?

本質上的問題是如何自動計算alphabeta 為此,我們可以查看圖像的直方圖。 自動亮度和對比度優化計算 alpha 和 beta,以便輸出范圍為[0...255] 我們計算累積分布以確定顏色頻率小於某個閾值(例如 1%)的位置,並切割直方圖的右側和左側。 這為我們提供了最小和最大范圍。 這是裁剪前(藍色)和裁剪后(橙色)的直方圖的可視化。 請注意圖像中更“有趣”的部分在剪裁后如何更加明顯。

為了計算alpha ,我們取裁剪后的最小和最大灰度范圍,並將它與我們期望的輸出范圍255

α = 255 / (maximum_gray - minimum_gray)

為了計算 beta,我們將其代入公式中,其中g(i, j)=0f(i, j)=minimum_gray

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

在解決這個結果之后

β = -minimum_gray * α

對於您的形象,我們得到了這個

阿爾法:3.75

測試版:-311.25

您可能需要調整裁剪閾值以優化結果。 以下是使用 1% 閾值與其他圖像的一些示例結果

自動亮度和對比度代碼

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% 閾值的其他圖像的結果

在此處輸入圖片說明 在此處輸入圖片說明

在此處輸入圖片說明 在此處輸入圖片說明

另一種版本是使用飽和度算法而不是使用 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()

強大的局部自適應軟二值化! 這就是我所說的。

我以前做過類似的事情,目的有點不同,所以這可能不完全適合您的需求,但希望它有所幫助(我也是在晚上寫了這段代碼供個人使用,所以它很丑陋)。 從某種意義上說,與您的情況相比,此代碼旨在解決更一般的情況,在這種情況下,我們可以在背景中產生大量結構化噪聲(請參見下面的演示)。

這段代碼有什么作用? 給定一張紙的照片,它會將它變白,以便它可以完美地打印。 請參閱下面的示例圖像。

預告:這就是你的頁面在這個算法之后的樣子(之前和之后)。 請注意,即使顏色標記注釋也不見了,所以我不知道這是否適合您的用例,但代碼可能有用:

為了獲得完美干凈的結果,您可能需要稍微調整過濾參數,但正如您所見,即使使用默認參數,它也能很好地工作。


第 0 步:剪切圖像以適合頁面

假設您以某種方式執行了此步驟(在您提供的示例中似乎是這樣)。 如果您需要手動注釋和重新扭曲工具,請私信我! ^^ 這一步的結果如下(我在這里使用的例子可以說比你提供的例子更難,雖然它可能不完全符合你的情況):

由此我們可以立即看出以下問題:

  • 光照條件不均勻。 這意味着所有簡單的二值化方法都不起作用。 我嘗試了很多OpenCV可用的解決方案,以及它們的組合,都沒有奏效!
  • 很多背景噪音。 就我而言,我需要去除紙張的網格,以及通過薄紙可見的紙張另一側的墨水。

第 1 步:伽瑪校正

這一步的原因是為了平衡整個圖像的對比度(因為根據光照條件,您的圖像可能會稍微曝光過度/曝光不足)。

這乍一看似乎是一個不必要的步驟,但它的重要性不容低估:從某種意義上說,它將圖像歸一化為相似的曝光分布,以便您以后可以選擇有意義的超參數(例如DELTA參數下一節,噪聲過濾參數,形態學參數等)

# Somehow I found the value of `gamma=1.2` to be the best in my case
def adjust_gamma(image, gamma=1.2):
    # build a lookup table mapping the pixel values [0, 255] to
    # their adjusted gamma values
    invGamma = 1.0 / gamma
    table = np.array([((i / 255.0) ** invGamma) * 255
        for i in np.arange(0, 256)]).astype("uint8")

    # apply gamma correction using the lookup table
    return cv2.LUT(image, table)

以下是伽馬調整的結果:

你可以看到它現在有點……“平衡”了。 如果沒有這一步,您將在后面的步驟中手動選擇的所有參數都將變得不那么健壯!


步驟 2:自適應二值化以檢測文本 Blob

在這一步中,我們將自適應地二值化文本 blob。 我稍后會添加更多評論,但這個想法基本上如下:

  • 我們將圖像分成大小為BLOCK_SIZE 訣竅是選擇足夠大的尺寸,這樣您仍然可以獲得一大塊文本和背景(即比您擁有的任何符號都大),但又要小到不會受到任何光照條件變化的影響(即“大,但仍然當地的”)。
  • 在每個塊內,我們進行局部自適應二值化:我們查看中值並假設它是背景(因為我們選擇的BLOCK_SIZE足夠大以使其大部分成為背景)。 然后,我們進一步定義DELTA基本上只是一個閾值,即“我們仍將其視為背景離中位數有多遠?”。

因此,函數process_image完成了工作。 此外,您可以修改preprocesspostprocess函數以滿足您的需要(但是,正如您從上面的示例中看到的,該算法非常健壯,即它開箱即用,無需修改太多參數)。

這部分的代碼假設前景比背景更暗(即紙上的墨水)。 但是你可以通過調整preprocess函數輕松改變它:而不是255 - image ,只返回image

# These are probably the only important parameters in the
# whole pipeline (steps 0 through 3).
BLOCK_SIZE = 40
DELTA = 25

# Do the necessary noise cleaning and other stuffs.
# I just do a simple blurring here but you can optionally
# add more stuffs.
def preprocess(image):
    image = cv2.medianBlur(image, 3)
    return 255 - image

# Again, this step is fully optional and you can even keep
# the body empty. I just did some opening. The algorithm is
# pretty robust, so this stuff won't affect much.
def postprocess(image):
    kernel = np.ones((3,3), np.uint8)
    image = cv2.morphologyEx(image, cv2.MORPH_OPEN, kernel)
    return image

# Just a helper function that generates box coordinates
def get_block_index(image_shape, yx, block_size): 
    y = np.arange(max(0, yx[0]-block_size), min(image_shape[0], yx[0]+block_size))
    x = np.arange(max(0, yx[1]-block_size), min(image_shape[1], yx[1]+block_size))
    return np.meshgrid(y, x)

# Here is where the trick begins. We perform binarization from the 
# median value locally (the img_in is actually a slice of the image). 
# Here, following assumptions are held:
#   1.  The majority of pixels in the slice is background
#   2.  The median value of the intensity histogram probably
#       belongs to the background. We allow a soft margin DELTA
#       to account for any irregularities.
#   3.  We need to keep everything other than the background.
#
# We also do simple morphological operations here. It was just
# something that I empirically found to be "useful", but I assume
# this is pretty robust across different datasets.
def adaptive_median_threshold(img_in):
    med = np.median(img_in)
    img_out = np.zeros_like(img_in)
    img_out[img_in - med < DELTA] = 255
    kernel = np.ones((3,3),np.uint8)
    img_out = 255 - cv2.dilate(255 - img_out,kernel,iterations = 2)
    return img_out

# This function just divides the image into local regions (blocks),
# and perform the `adaptive_mean_threshold(...)` function to each
# of the regions.
def block_image_process(image, block_size):
    out_image = np.zeros_like(image)
    for row in range(0, image.shape[0], block_size):
        for col in range(0, image.shape[1], block_size):
            idx = (row, col)
            block_idx = get_block_index(image.shape, idx, block_size)
            out_image[block_idx] = adaptive_median_threshold(image[block_idx])
    return out_image

# This function invokes the whole pipeline of Step 2.
def process_image(img):
    image_in = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    image_in = preprocess(image_in)
    image_out = block_image_process(image_in, BLOCK_SIZE)
    image_out = postprocess(image_out)
    return image_out

結果是像這樣的漂亮斑點,緊跟墨跡:


第 3 步:二值化的“軟”部分

有了覆蓋符號的斑點以及更多的斑點,我們終於可以進行白化程序了。

如果我們仔細觀察帶有文字的紙張(尤其是那些有手寫的紙張)的照片,從“背景”(白紙)到“前景”(深色墨水)的轉變並不明顯,而是非常漸進的. 本節中其他基於二值化的答案提出了一個簡單的閾值(即使它們是本地自適應的,它仍然是一個閾值),它適用於印刷文本,但會產生不那么漂亮的手寫結果。

因此,本節的動機是我們希望保留從黑色到白色逐漸傳輸的效果,就像使用自然墨水的紙張的自然照片一樣。 這樣做的最終目的是使其可打印。

主要思想很簡單:像素值(經過上面的閾值處理后)與局部最小值的差異越大,它就越有可能屬於背景。 我們可以使用一系列Sigmoid函數來表達這一點,重新縮放到局部塊的范圍(以便該函數在整個圖像中自適應地縮放)。

# This is the function used for composing
def sigmoid(x, orig, rad):
    k = np.exp((x - orig) * 5 / rad)
    return k / (k + 1.)

# Here, we combine the local blocks. A bit lengthy, so please
# follow the local comments.
def combine_block(img_in, mask):
    # First, we pre-fill the masked region of img_out to white
    # (i.e. background). The mask is retrieved from previous section.
    img_out = np.zeros_like(img_in)
    img_out[mask == 255] = 255
    fimg_in = img_in.astype(np.float32)

    # Then, we store the foreground (letters written with ink)
    # in the `idx` array. If there are none (i.e. just background),
    # we move on to the next block.
    idx = np.where(mask == 0)
    if idx[0].shape[0] == 0:
        img_out[idx] = img_in[idx]
        return img_out

    # We find the intensity range of our pixels in this local part
    # and clip the image block to that range, locally.
    lo = fimg_in[idx].min()
    hi = fimg_in[idx].max()
    v = fimg_in[idx] - lo
    r = hi - lo

    # Now we use good old OTSU binarization to get a rough estimation
    # of foreground and background regions.
    img_in_idx = img_in[idx]
    ret3,th3 = cv2.threshold(img_in[idx],0,255,cv2.THRESH_BINARY+cv2.THRESH_OTSU)

    # Then we normalize the stuffs and apply sigmoid to gradually
    # combine the stuffs.
    bound_value = np.min(img_in_idx[th3[:, 0] == 255])
    bound_value = (bound_value - lo) / (r + 1e-5)
    f = (v / (r + 1e-5))
    f = sigmoid(f, bound_value + 0.05, 0.2)

    # Finally, we re-normalize the result to the range [0..255]
    img_out[idx] = (255. * f).astype(np.uint8)
    return img_out

# We do the combination routine on local blocks, so that the scaling
# parameters of Sigmoid function can be adjusted to local setting
def combine_block_image_process(image, mask, block_size):
    out_image = np.zeros_like(image)
    for row in range(0, image.shape[0], block_size):
        for col in range(0, image.shape[1], block_size):
            idx = (row, col)
            block_idx = get_block_index(image.shape, idx, block_size)
            out_image[block_idx] = combine_block(
                image[block_idx], mask[block_idx])
    return out_image

# Postprocessing (should be robust even without it, but I recommend
# you to play around a bit and find what works best for your data.
# I just left it blank.
def combine_postprocess(image):
    return image

# The main function of this section. Executes the whole pipeline.
def combine_process(img, mask):
    image_in = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    image_out = combine_block_image_process(image_in, mask, 20)
    image_out = combine_postprocess(image_out)
    return image_out

有些東西被評論,因為它們是可選的。 combine_process函數從上一步中獲取掩碼,並執行整個組合管道。 您可以嘗試使用它們來獲取特定數據(圖像)。 結果很整潔:

可能我會在此答案中為代碼添加更多注釋和解釋。 將在 Github 上上傳整個內容(連同裁剪和變形代碼)。

在此處輸入圖片說明

這種方法應該適用於您的應用程序。 首先,您會找到一個閾值,它可以在強度直方圖中很好地分離分布模式,然后使用該值重新調整強度。

from skimage.filters import threshold_yen
from skimage.exposure import rescale_intensity
from skimage.io import imread, imsave

img = imread('mY7ep.jpg')

yen_threshold = threshold_yen(img)
bright = rescale_intensity(img, (0, yen_threshold), (0, 255))

imsave('out.jpg', bright)

我在這里使用 Yen 的方法,可以在此頁面上了解有關此方法的更多信息。

我認為這樣做的方法是 1) 從 HCL 色彩空間中提取色度(飽和度)通道。 (HCL 比 HSL 或 HSV 效果更好)。 只有顏色應該具有非零飽和度,所以明亮,灰色陰影會變暗。 2) 閾值使用 otsu 閾值作為掩碼使用的結果。 3) 將您的輸入轉換為灰度並應用局部區域(即自適應)閾值。 4) 將mask放入原圖的alpha通道,然后將局部區域閾值結果與原圖合成,使得彩色區域與原圖保持一致,其他地方都使用局部區域閾值結果。

抱歉,我不太了解 OpeCV,但這里是使用 ImageMagick 的步驟。

請注意,通道從 0 開始編號。(H=0 或紅色,C=1 或綠色,L=2 或藍色)

輸入:

在此處輸入圖片說明

magick image.jpg -colorspace HCL -channel 1 -separate +channel tmp1.png


在此處輸入圖片說明

magick tmp1.png -auto-threshold otsu tmp2.png


在此處輸入圖片說明

magick image.jpg -colorspace gray -negate -lat 20x20+10% -negate tmp3.png


在此處輸入圖片說明

magick tmp3.png \( image.jpg tmp2.png -alpha off -compose copy_opacity -composite \) -compose over -composite result.png


在此處輸入圖片說明

添加:

這是 Python Wand 代碼,它產生相同的輸出結果。 它需要 Imagemagick 7 和 Wand 0.5.5。

#!/bin/python3.7

from wand.image import Image
from wand.display import display
from wand.version import QUANTUM_RANGE

with Image(filename='text.jpg') as img:
    with img.clone() as copied:
        with img.clone() as hcl:
            hcl.transform_colorspace('hcl')
            with hcl.channel_images['green'] as mask:
                mask.auto_threshold(method='otsu')
                copied.composite(mask, left=0, top=0, operator='copy_alpha')
                img.transform_colorspace('gray')
                img.negate()
                img.adaptive_threshold(width=20, height=20, offset=0.1*QUANTUM_RANGE)
                img.negate()
                img.composite(copied, left=0, top=0, operator='over')
                img.save(filename='text_process.jpg')

首先,我們將文本和顏色標記分開。 這可以在具有色彩飽和度通道的色彩空間中完成。 我改用了一種受 本文啟發的非常簡單的方法:對於(淺色)灰色區域,min(R,G,B)/max(R,G,B) 的比率將接近 1,對於彩色區域,<<1。 對於深灰色區域,我們得到 0 到 1 之間的任何值,但這並不重要:這些區域要么進入顏色蒙版,然后按原樣添加,要么不包含在蒙版中,並有助於二值化的輸出文本。 對於黑色,我們使用轉換為 uint8 時 0/0 變為 0 的事實。

灰度圖像文本被局部閾值化以產生黑白圖像。 你可以從這個比較那個調查中選擇你最喜歡的技術。 我選擇了 NICK 技術,它可以很好地處理低對比度並且相當穩健,即在大約 -0.3 和 -0.1 之間選擇參數k適用於非常廣泛的條件,這有利於自動處理。 對於提供的示例文檔,所選擇的技術並沒有起到很大的作用,因為它是相對均勻的照明,但為了處理非均勻照明的圖像,它應該是一種局部閾值技術。

在最后一步,顏色區域被添加回二值化的文本圖像。

所以這個解決方案與@fmw42 的解決方案非常相似(所有的想法都歸功於他),除了不同的顏色檢測和二值化方法。

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

# make mask and inverted mask for colored areas
b,g,r = cv2.split(cv2.blur(image,(5,5)))
np.seterr(divide='ignore', invalid='ignore') # 0/0 --> 0
m = (np.fmin(np.fmin(b, g), r) / np.fmax(np.fmax(b, g), r)) * 255
_,mask_inv = cv2.threshold(np.uint8(m), 0, 255, cv2.THRESH_BINARY+cv2.THRESH_OTSU)
mask = cv2.bitwise_not(mask_inv)

# local thresholding of grayscale image
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
text = cv2.ximgproc.niBlackThreshold(gray, 255, cv2.THRESH_BINARY, 41, -0.1, binarizationMethod=cv2.ximgproc.BINARIZATION_NICK)

# create background (text) and foreground (color markings)
bg = cv2.bitwise_and(text, text, mask = mask_inv)
fg = cv2.bitwise_and(image, image, mask = mask)

out = cv2.add(cv2.cvtColor(bg, cv2.COLOR_GRAY2BGR), fg) 

在此處輸入圖片說明

如果您不需要顏色標記,您可以簡單地將灰度圖像二值化:

image = cv2.imread('mY7ep.jpg')
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
text = cv2.ximgproc.niBlackThreshold(gray, 255, cv2.THRESH_BINARY, at_bs, -0.3, binarizationMethod=cv2.ximgproc.BINARIZATION_NICK)

在此處輸入圖片說明

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM