opencv_水印去除

该博客介绍了如何使用OpenCV和scikit-image库进行图像处理,包括去除水印、边缘检测、直方图匹配、连通组件分析等技术。通过创建内外层掩码、调整亮度和饱和度,以及使用内插法填充,实现了图像中特定区域的修复和颜色均衡。同时,展示了从彩色图像到边缘标记的转换过程。
摘要由CSDN通过智能技术生成
import numpy as np
import os
import random
import cv2
from skimage.morphology import binary_dilation, binary_erosion
# pip install scikit-image
"""
移走水印
"""

def hist_match(source, template, ignore_black = True):
    """分层直方图匹配(源,模板,忽略_黑色=真)"""
    cv2.imshow('source',source)
    cv2.imshow('template',template)
    """
    https://stackoverflow.com/questions/32655686/histogram-matching-of-two-images-in-python-2-x
    图像的匹配-2-x
    调整灰度图像的像素值,使其直方图与目标图像的匹配
    Adjust the pixel values of a grayscale image such that its histogram  matches that of a target image
       Arguments:
       -----------
        source: np.ndarray
            要转换的图像;直方图是在展平的数组
            Image to transform; the histogram is computed over the flattened array
        template: np.ndarray
            模板图像;可以有不同的来源维度
            Template image; can have different dimensions to source
    Returns:
    -----------
        matched: np.ndarray
            The transformed output image
    """
    oldshape = source.shape
    source = source.ravel()#源=源行程
    template = template.ravel()

    # get the set of unique pixel values and their corresponding indices and
    # counts
    s_values, bin_idx, s_counts = np.unique(source, return_inverse=True,return_counts=True)
    if ignore_black:
        s_counts[0] = 0

    t_values, t_counts = np.unique(template, return_counts=True)#去除数组中的重复数字,并进行排序之后输出
    if ignore_black:
        t_counts[0] = 0

    # take the cumsum of the counts and normalize by the number of pixels to
    # get the empirical cumulative distribution functions for the source and
    # template images (maps pixel value --> quantile)
    s_quantiles = np.cumsum(s_counts).astype(np.float64)#s_分位数= np。累计总和
    s_quantiles /= s_quantiles[-1]
    t_quantiles = np.cumsum(t_counts).astype(np.float64)
    t_quantiles /= t_quantiles[-1]

    # interpolate linearly to find the pixel values in the template image
    # that correspond most closely to the quantiles in the source image
    interp_t_values = np.interp(s_quantiles, t_quantiles, t_values)#一维线性插值函数。

    returned_image = interp_t_values[bin_idx].reshape(oldshape)
    return returned_image.astype(np.uint8)

def coloured_image_to_edge_mark(coloured_image):
   # 彩色图像到边缘标记
   image_sum = coloured_image[:,:,0] + coloured_image[:,:,1] + coloured_image[:,:,2]
   mask = image_sum > 0
   return mask

def triple_mask(mask):#三重掩码(掩码)
    return np.stack( [mask]* 3, axis = 2)

def get_inner_and_outer_masks(mask):
    inner_mask = binary_erosion(binary_erosion(binary_dilation(mask)))#内部掩码=腐蚀(腐蚀(膨胀())
    inner_pixel_count = np.count_nonzero(inner_mask)#内部_像素_计数= np.count _非零(内部_掩码)
    #inner_mask = mask
    outer_mask = binary_dilation(binary_dilation(mask)) #外部掩码=膨胀(膨胀(谈吗)) #无颜色异常 no colour abnormaility
    outer_pixel_count = np.count_nonzero(outer_mask)#外部像素计数= np.count _非零(外部掩码)
    print("inner_pixel_coint = ",inner_pixel_count)#inner_pixel_coint =  114356
    print("outer_pixel_count = ",outer_pixel_count)
    return inner_mask, outer_mask

def balance_histograms_using_v(inner, outer):
    """
    make RGB image inner have the same brightness (i.e. v) histogram as image outer
    """
    inner_v_before, inner_hsv = rgb_to_intensity(inner)#rgb _ to _强度
    outer_v,        outer_hsv = rgb_to_intensity(outer)#rgb _ to _强度
    inner_v_after = hist_match(inner_v_before, outer_v)
    inner_hsv[:,:,2] = inner_v_after                   # edit V channel only
    return cv2.cvtColor(inner_hsv, cv2.COLOR_HSV2BGR)  # return as BGR

def fill_in(io, edge_mask, outer_mask):
    """
    http://opencv-python-tutroals.readthedocs.io/en/latest/py_tutorials/py_photo/py_inpainting/py_inpainting.html
    """
    fill_in_method = cv2.INPAINT_TELEA # other choice cv2.INPAINT_NS - makes little visible difference
    # 算法基于Bertalmio,Marcelo,Andrea L.Bertozzi和Guillermo Sapiro于2001年发表“Navier - Stokes, 流体动力学与图像和视频修补”大作。
    # 算法基于流体动力学,采用偏微分方程,原则幽默heurisitic。首先沿已知区域边缘行进到未知区域(边缘连续)。
    # 照片继续(连接相同强度点成线,类似轮廓   连接相同高度点),
    # 并在修复区域边界处--匹配渐变矢量。获得颜色后,填充颜色--以减少该区域最小差异。使用标志:cv2.INPAINT_NS启用此算法。
    io_hsv        = rgb_to_hsv(io)
    h_before      = io_hsv[:,:,0]
    s_before      = io_hsv[:,:,1]
    v_before      = io_hsv[:,:,2]

    outer_mask_uint    = np.where(outer_mask,255,0).astype(np.uint8)
    s_after   = cv2.inpaint(s_before, outer_mask_uint, 15, fill_in_method)       # use outer mask to fill in saturation
    h_after   = cv2.inpaint(h_before, outer_mask_uint, 15 ,fill_in_method)       # use outer mask to fill in hue
    v_after   = cv2.inpaint(v_before,       edge_mask,  2, fill_in_method)  # use edge to fill in hue

    io_hsv[:,:,0] = h_after
    io_hsv[:,:,1] = s_after
    io_hsv[:,:,2] = v_after
    return hsv_to_rgb(io_hsv)

def rgb_to_hsv(im):
    # HSV 颜色空间转换 RGB 颜色空间,重塑为圆柱体  而不是立方体,亮度是单独维度
    # - Hue:色调,检查哪种“纯”颜色。例如“红色”颜色所有阴影和色调都将--具有相同色调。 [0, 179]
    # - Saturation:饱和度,颜色有多“白”。完全饱和的颜色将是“纯色”,如“纯红色”。零饱和度的颜色将是纯白色。 [0, 255]
    # - Value:值允许控制颜色的亮度。值为零表示纯黑色,而增加值会产生较浅的颜色。 [0, 255]
    # 转换图像为HSV颜色空间并展示
    return cv2.cvtColor(im, cv2.COLOR_BGR2HSV)#颜色转换为hsv

def hsv_to_rgb(im):
    return cv2.cvtColor(im, cv2.COLOR_HSV2BGR)

def rgb_to_intensity(im):
     """rgb _ to _强度"""
     hsv  = rgb_to_hsv(im)#获取某维度
     return hsv[:,:,2], hsv

def make_random_colour_map_with_stats(stats, pop_thresh = 0):
    n = len(stats)#统计数
    colour_map = np.zeros( [n, 3], dtype=np.uint8) #给定形状和类型的用0填充的数组
    for i in range(n):
        if ( (pop_thresh != 0) and (stats[i][4] < pop_thresh) ) or  (i == 0):
            # 将小区域和区域0(背景)设为黑色
             colour_map[i] = [0,0,0] # make small regions and region 0 (background) black
        else:
            for j in range(3):
                # 大区域是非零的随机颜色
                colour_map[i,j] = 1 + random.randint(0,254)     # big regions are a non-zero random colou
    return colour_map

"""

Image comes from here

https://www.architecture.com/image-library/RIBApix/licensed-image/poster/balintore-castle-angus-the-entrance-front/posterid/RIBA65186.html
"""

def display_and_output_image(name, im):#显示和输出图像
    cv2.imshow(name,im)
    file_name = os.path.join( "C:\\Users\\david\\Desktop\\", name + ".jpg")
    cv2.imwrite(file_name,im)


def create_letter_mask(image_saturation):
    """
    https://stackoverflow.com/questions/35854197/how-to-use-opencvs-connected-components-with-stats-in-python
    threshold saturation to detect letters (low saturation)
    检测字母的阈值饱和度(低饱和度)
    find big connected components (small connected components are noise)
     查找大的连接组件(小的连接组件是噪音)
    # 创建字母掩码
    """
    connectivity = 4 #连通性
    #阈值(image_saturation,)
    ret, thresh_s = cv2.threshold(image_saturation, 42, 255, cv2.THRESH_BINARY_INV)  # 50 too high, 25 too low
    # cv2.THRESH_OTSU 最小二乘法,cv2.THRESH_TRIANGLE三角算法。cv2.THRESH_OTSU适合双峰图;cv2.THRESH_TRIANGLE适合单峰图。
    output = cv2.connectedComponentsWithStats(thresh_s, connectivity, cv2.CV_32S)# 处理不规则连通区域
    # depth:矩阵元素一个通道数据类型,值和type相关。如type为CV_16SC2,一个2通道16位有符号整数。
    blob_image = output[1]#滴图像  液滴;黏稠的一滴;色斑;色点;形状不易确定的一团;零分
    stats = output[2]#统计数据
    pop_thresh = 60# 弹出或流行
    # 大斑点颜色图=制造随机颜色图用统计
    big_blob_colour_map = make_random_colour_map_with_stats(stats, pop_thresh)
    all_blob_colour_map = make_random_colour_map_with_stats(stats)#所有斑点颜色图
    big_blob_coloured_image = big_blob_colour_map[blob_image]# 大块颜色图 # output
    all_blob_coloured_image = all_blob_colour_map[blob_image]#所有块颜色图  # output
    display_and_output_image("big_blob_coloured_image", big_blob_coloured_image)#显示图像
    display_and_output_image("all_blob_coloured_image", all_blob_coloured_image)
    letter_mask = coloured_image_to_edge_mark(big_blob_coloured_image)#字母掩码=彩色图像到边缘标记
    return letter_mask#字母掩码

def main():
    """
    original image comes from here

    https://www.architecture.com/image-library/RIBApix/licensed-image/poster/balintore-castle-angus-the-entrance-front/posterid/RIBA65186.html
    """
    im = cv2.imread("mark1.jpg")#读取原始图
    print (im.shape)#(422, 640, 3)
    display_and_output_image("image",im)#显示图片
    hsv = rgb_to_hsv(im)#转换为hsv
    image_saturation = hsv[:,:,1]   # 图像饱和度output
    display_and_output_image("image_saturation",image_saturation)#显示饱和度,并写入一个文件
    letter_mask = create_letter_mask(image_saturation) #字母掩码
    

    # outer mask bigger than letter mask
    # inner mask smaller than letter mask
    # edge mask is between inner and outer mask and contains black line round letters (i.e. to be removed)
    inner_mask, outer_mask =  get_inner_and_outer_masks(letter_mask)#获取内部和外部掩码
    edge_mask = np.logical_and( np.logical_not(inner_mask), outer_mask) #边缘掩码= (内部掩码),外部掩码)
    edge_mask = np.where(edge_mask,255,0).astype(np.uint8)
    display_and_output_image("edge_mask",edge_mask) #显示和输出图像

    inner_image = np.where( triple_mask(inner_mask), im, 0)#condition:array_like,bool ,当为True时,产生x,否则产生y
    outer_image = np.where( triple_mask(outer_mask) ,0 ,im)
    cv2.imwrite('inner_image.jpg',inner_image)
    cv2.imwrite('outer_image.jpg',outer_image)

    # 平衡_内部_图像 = 平衡_直方图_使用_v(内部_图像,外部_图像)
    balanced_inner_image = balance_histograms_using_v(inner_image,outer_image)
    cv2.imwrite('balanced_inner_image.jpg',balanced_inner_image)
    
    before_filling_in = balanced_inner_image + outer_image #填充前=平衡的内部图像+外部图像
    display_and_output_image("before_filling_in",before_filling_in)

    cv2.imwrite('sss.jpg',before_filling_in)
    after_filling_in = fill_in(before_filling_in, edge_mask, outer_mask)  #填充后=填充(前填充、边缘掩码、外部遮罩)                   # output
    display_and_output_image("after_filling_in",after_filling_in)

    cv2.waitKey(0)
    cv2.destroyAllWindows()



if __name__ == '__main__':
    main()
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值