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()
opencv_水印去除
最新推荐文章于 2024-04-27 12:41:02 发布
该博客介绍了如何使用OpenCV和scikit-image库进行图像处理,包括去除水印、边缘检测、直方图匹配、连通组件分析等技术。通过创建内外层掩码、调整亮度和饱和度,以及使用内插法填充,实现了图像中特定区域的修复和颜色均衡。同时,展示了从彩色图像到边缘标记的转换过程。
摘要由CSDN通过智能技术生成