当前位置:   article > 正文

opencv_水印去除_去除印章 opencv

去除印章 opencv
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()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
  • 223
  • 224
  • 225
  • 226
  • 227
  • 228
  • 229
  • 230
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/weixin_40725706/article/detail/975422
推荐阅读
相关标签
  

闽ICP备14008679号