其他分享
首页 > 其他分享> > OpenCV图像处理笔记[16]

OpenCV图像处理笔记[16]

作者:互联网

Painting Light

1. 前期工作

理解核心代码所需的预备知识
导包
import cv2
import rtree
import scipy
import trimesh
import numpy as np
import tensorflow as tf
from scipy.spatial import ConvexHull
from cv2.ximgproc import createGuidedFilter

来自:https://github.com/mikedh/trimesh

​ 来自:https://www.osgeo.cn/rtree/

导向滤波(Guided Filter)

引导滤波是由 何凯明等人于2010年发表在ECCV的文章《Guided Image Filtering》中提出的,后续于2013年发表了 改进算法快速引导滤波的实现

来自:https://jinzhangyu.github.io/2018/09/06/2018-09-06-OpenCV-Python%E6%95%99%E7%A8%8B-16-%E5%B9%B3%E6%BB%91%E5%9B%BE%E5%83%8F-3/

数据预处理

理解参考https://zhuanlan.zhihu.com/p/24157634

  1. 在TensorFlow张量上调用Keras层

    创建一个TensorFlow会话并且注册Keras。Keras将使用注册的会话来初始化它在内部创建的所有变量。

session = tf.Session()
tf.keras.backend.set_session(session)
ip3 = tf.placeholder(dtype=tf.float32, shape=(None, None, None, 3))
srcnn = tf.keras.models.load_model('srcnn.net')
srcnn_op = srcnn(tf.pad(ip3 / 255.0, [[0, 0], [16, 16], [16, 16], [0, 0]], 'REFLECT'))[:, 16:-16, 16:-16, :] * 255.0
session.run(tf.global_variables_initializer())
srcnn.load_weights('srcnn.net')

2. 图片大小处理

min_resize()

​ 按m尺寸比例调整图像大小

​ 如果 图像宽 < 图像长

​ 将S0设为目标尺寸

​ S1 即最终结果的长 = 目标值 m 与实际值 shape[0] 的 比例 × 原图的长

​ 否则

​ 将S0 设为最终结果的宽 = 目标值 m 与实际值shape[1] 的 比例 × 原图的宽

​ S1 设为目标尺寸

​ 新图像尺寸就调整为min (长s1, 宽s0)

​ 比较新比例和 原图比例

​ 如果 尺寸: 新图 < 原图

​ 缩小原图

​ 否则

​ 放大原图

# Some image resizing tricks.
def min_resize(x, m):
    if x.shape[0] < x.shape[1]:
        s0 = m
        s1 = int(float(m) / float(x.shape[0]) * float(x.shape[1]))
    else:
        s0 = int(float(m) / float(x.shape[1]) * float(x.shape[0]))
        s1 = m
    new_max = min(s1, s0)
    raw_max = min(x.shape[0], x.shape[1])
    if new_max < raw_max:
        interpolation = cv2.INTER_AREA
    else:
        interpolation = cv2.INTER_LANCZOS4
    y = cv2.resize(x, (s1, s0), interpolation=interpolation)
    return y
d_resize()

再次调整尺寸

比较出向下取样和向上取样两种图像之间的大小,选择对应的插值(这里可以简单理解为:缩小使用cv.INTER_AREA方法,放大使用cv2.INTER_LANCZOS4方法)

​ 将该图缩小

​ 将该图放大

# Some image resizing tricks.
def d_resize(x, d, fac=1.0):
    new_min = min(int(d[1] * fac), int(d[0] * fac))
    raw_min = min(x.shape[0], x.shape[1])
    if new_min < raw_min:
        interpolation = cv2.INTER_AREA
    else:
        interpolation = cv2.INTER_LANCZOS4
    y = cv2.resize(x, (int(d[1] * fac), int(d[0] * fac)), interpolation=interpolation)
    return y

3. 图片梯度处理

get_image_gradient()

梯度的方向是函数f(x,y)变化最快的方向,当图像中存在边缘时,一定有较大的梯度值,相反,当图像中有比较平滑的部分时,灰度值变化较小,则相应的梯度也较小,图像处理中把梯度的模简称为梯度,由图像梯度构成的图像成为梯度图像

# Some image gradient computing tricks.
def get_image_gradient(dist):
    cols = cv2.filter2D(dist, cv2.CV_32F, np.array([[-1, 0, +1], [-2, 0, +2], [-1, 0, +1]]))
    rows = cv2.filter2D(dist, cv2.CV_32F, np.array([[-1, -2, -1], [0, 0, 0], [+1, +2, +1]]))
    return cols, rows

4. 生成灯光效果

generate_lighting_effects()
1. 向下取样pyrDown函数及使用

图像金字塔操作的将是图像的像素问题

​ 向下取样 : 将图像的尺度变小,变成原来的四分之一

​ 从高分辨率到低分辨率图像,缩小图像

def generate_lighting_effects(stroke_density, content):

    # Computing the coarse lighting effects
    # In original paper we compute the coarse effects using Gaussian filters.
    # Here we use a Gaussian pyramid to get similar results.
    # This pyramid-based result is a bit better than naive filters.
    h512 = content
    h256 = cv2.pyrDown(h512)
    h128 = cv2.pyrDown(h256)
    h64 = cv2.pyrDown(h128)
    h32 = cv2.pyrDown(h64)
    h16 = cv2.pyrDown(h32)
    c512, r512 = get_image_gradient(h512)
    c256, r256 = get_image_gradient(h256)
    c128, r128 = get_image_gradient(h128)
    c64, r64 = get_image_gradient(h64)
    c32, r32 = get_image_gradient(h32)
    c16, r16 = get_image_gradient(h16)
    c = c16
2. 向上取样pyrUp函数操作

向上取样:在每个方向上扩大为原来的2倍,新增的行和列以0填充。放大图像

  c = d_resize(cv2.pyrUp(c), c32.shape) * 4.0 + c32
    c = d_resize(cv2.pyrUp(c), c64.shape) * 4.0 + c64
    c = d_resize(cv2.pyrUp(c), c128.shape) * 4.0 + c128
    c = d_resize(cv2.pyrUp(c), c256.shape) * 4.0 + c256
    c = d_resize(cv2.pyrUp(c), c512.shape) * 4.0 + c512
    r = r16
    r = d_resize(cv2.pyrUp(r), r32.shape) * 4.0 + r32
    r = d_resize(cv2.pyrUp(r), r64.shape) * 4.0 + r64
    r = d_resize(cv2.pyrUp(r), r128.shape) * 4.0 + r128
    r = d_resize(cv2.pyrUp(r), r256.shape) * 4.0 + r256
    r = d_resize(cv2.pyrUp(r), r512.shape) * 4.0 + r512
    coarse_effect_cols = c
    coarse_effect_rows = r
3. 规范化
# Normalization —— 标准化
EPS = 1e-10
max_effect = np.max((coarse_effect_cols**2 + coarse_effect_rows**2)**0.5)
coarse_effect_cols = (coarse_effect_cols + EPS) / (max_effect + EPS)
coarse_effect_rows = (coarse_effect_rows + EPS) / (max_effect + EPS)

# Refinement  —— 改进
stroke_density_scaled = (stroke_density.astype(np.float32) / 255.0).clip(0, 1)
coarse_effect_cols *= (1.0 - stroke_density_scaled ** 2.0 + 1e-10) ** 0.5
coarse_effect_rows *= (1.0 - stroke_density_scaled ** 2.0 + 1e-10) ** 0.5
refined_result = np.stack([stroke_density_scaled, coarse_effect_rows, coarse_effect_cols], axis=2)

return refined_result

归一化/标准化:
不同的评价指标往往具有不同的量纲和量纲单位,这样无法对结果进行分析,难以对结果进行衡量
,为了消除指标之间的量纲影响,需要对数据进行标准化处理,以使数据指标之间存在可比性。
(所谓数据归一化处理就是将所有数据都映射到同一尺度。)
归一化:是指变量减去它的均值,再除以标准差;
优点:归一化后加快了梯度下降求最优解的速度;并且有可能提高精度。
为什么要进行图像归一化?(Normalization)
1、转换成标准模式,防止仿射变换的影响。
2、减小几何变换的影响。
3、加快梯度下降求最优解的速度。
在这个项目里使用的方法是(0,1)标准化——这是最简单也是最容易想到的方法,通过遍历featur
vector(特征向量)里的每一个数据,将Max和Min的记录下来,并通过Max-Min作为基数
,Max=1)进行数据的归一化处理:

5. 运行函数

run
1. 参数:
参数 表示
image 原图像
mask 掩码图像
ambient_intensity 环境强度
light_intensity 光照密度
light_source_height 光源高度
gamma_correction \(\gamma\) 修正
stroke_density_clipping 笔画密度剪裁
light_color_red\green\blue 光照颜色
enabling_multiple_channel_effects 启用多通道效果
def run(image, mask, ambient_intensity, light_intensity, light_source_height, gamma_correction, stroke_density_clipping, light_color_red, light_color_green, light_color_blue, enabling_multiple_channel_effects):

2. 图片处理
# Some pre-processing to resize images and remove input JPEG artifacts.
raw_image = min_resize(image, 512)
raw_image = run_srcnn(raw_image)
raw_image = min_resize(raw_image, 512)
raw_image = raw_image.astype(np.float32)
unmasked_image = raw_image.copy()
3. 掩膜处理

设置\(\alpha\)值

if mask is not None:
    alpha = np.mean(d_resize(mask, raw_image.shape).astype(np.float32) / 255.0, axis=2, keepdims=True)
    raw_image = unmasked_image * alpha
4. 计算凸包形状调色板
# Compute the convex-hull-like palette.
h, w, c = raw_image.shape
flattened_raw_image = raw_image.reshape((h * w, c))
raw_image_center = np.mean(flattened_raw_image, axis=0)
hull = ConvexHull(flattened_raw_image)

raw_image.reshape((h * w, c) : 将图像扁平化处理,把color作为特征

计算出图像中心即g

convexHull第一个参数是要求凸包的点集,第二个参数是输出的凸包点,第三个参数是一个bool变量,表示求得的凸包是顺时针方向还是逆时针方向,true是顺时针方向。注意:第二个参数可以为vector,此时返回的是凸包点在原轮廓点集中的索引,也可以为vector,此时存放的是凸包点的位置。

凸包(Convex Hull)是一个计算几何(图形学)中的概念,在一个实数向量空间V中,对于给定集合X,所有包含X的凸集的交集S被称为X的凸包。
X的凸包可以用X内所有点(x1, x2….xn)的线性组合来构造。在二维欧几里得空间中,凸包可以想象为一条刚好包着所有点的橡皮圈,用不严谨的话来讲,给定二维平面上的点集,凸包就是将最外层的点连接起来构成的凸多边形,它能包含点集中所有的点。常见的有Graham’s Scan法和Jarvis步进法

5. 估计笔画密度图
# Estimate the stroke density map.
intersector = trimesh.Trimesh(faces=hull.simplices, vertices=hull.points).ray
start = np.tile(raw_image_center[None, :], [h * w, 1])

函数原型:tile(array, repeat)

说明:

  1. array:Array类数组。

  2. ​ repeat:各个维度上重复的次数。

功能: 重复array 的各个维度,得到的新数组的维度由repeat的维度d和array.ndim的大小决定,shape值由扩充后的array和repeat相应维度值的乘积得到。

ndarray.ndim

指数组的维度,即数组轴(axes)的个数,其数量等于秩(rank

direction = flattened_raw_image - start
print('Begin ray intersecting ...')
index_tri, index_ray, locations = intersector.intersects_id(start, direction, return_locations=True, multiple_hits=True)
print('Intersecting finished.')
intersections = np.zeros(shape=(h * w, c), dtype=np.float32)
intersection_count = np.zeros(shape=(h * w, 1), dtype=np.float32)
CI = index_ray.shape[0]
for c in range(CI):
    i = index_ray[c]
    intersection_count[i] += 1
    intersections[i] += locations[c]
intersections = (intersections + 1e-10) / (intersection_count + 1e-10)
intersections = intersections.reshape((h, w, 3))
intersection_count = intersection_count.reshape((h, w))
intersections[intersection_count < 1] = raw_image[intersection_count < 1]

intersection: 交点

intersection_distance = np.sqrt(np.sum(np.square(intersections - raw_image_center[None, None, :]), axis=2, keepdims=True))

交点(命中点)距离: \(\sqrt{\Sigma (c_p - g)^2}\)

pixel_distance = np.sqrt(np.sum(np.square(raw_image - raw_image_center[None, None, :]), axis=2, keepdims=True))

对应公式:像素距离= \(\sqrt{\Sigma (c_p - g)^2}\)

stroke_density = ((1.0 - np.abs(1.0 - pixel_distance / intersection_distance)) * stroke_density_clipping).clip(0, 1) * 255

对应公式: $ 1 - | 1 - \frac{\sqrt{\Sigma(c_p - h_p)^2}}{\sqrt{\Sigma (c_p - h_p)^2} * 笔画密度裁剪}| $

6. 提升笔画密度图质量
# A trick to improve the quality of the stroke density map.
# It uses guided filter to remove some possible artifacts.
# You can remove these codes if you like sharper effects.
guided_filter = createGuidedFilter(pixel_distance.clip(0, 255).astype(np.uint8), 1, 0.01)
for _ in range(4):
    stroke_density = guided_filter.filter(stroke_density)
7. 生成笔画密度估计值和灯光效果
# Visualize the estimated stroke density.
cv2.imwrite('stroke_density.png', stroke_density.clip(0, 255).astype(np.uint8))

# Then generate the lighting effects
raw_image = unmasked_image.copy()
lighting_effect = np.stack([
   generate_lighting_effects(stroke_density, raw_image[:, :, 0]),
   generate_lighting_effects(stroke_density, raw_image[:, :, 1]),
   generate_lighting_effects(stroke_density, raw_image[:, :, 2])
], axis=2)
update_mouse
    def update_mouse(event, x, y, flags, param):
        global gx
        global gy
        gx = - float(x % w) / float(w) * 2.0 + 1.0
        gy = - float(y % h) / float(h) * 2.0 + 1.0
        return

    light_source_color = np.array([light_color_blue, light_color_green, light_color_red])

    global gx
    global gy

    while True:
        light_source_location = np.array([[[light_source_height, gy, gx]]], dtype=np.float32)
        light_source_direction = light_source_location / np.sqrt(np.sum(np.square(light_source_location)))
        final_effect = np.sum(lighting_effect * light_source_direction, axis=3).clip(0, 1)
        if not enabling_multiple_channel_effects:
            final_effect = np.mean(final_effect, axis=2, keepdims=True)
        rendered_image = (ambient_intensity + final_effect * light_intensity) * light_source_color * raw_image
        rendered_image = ((rendered_image / 255.0) ** gamma_correction) * 255.0
        canvas = np.concatenate([raw_image, rendered_image], axis=1).clip(0, 255).astype(np.uint8)
        cv2.imshow('Move your mouse on the canvas to play!', canvas)
        cv2.setMouseCallback('Move your mouse on the canvas to play!', update_mouse)
        cv2.waitKey(10)

标签:raw,16,image,cv2,OpenCV,shape,图像处理,图像,np
来源: https://www.cnblogs.com/tow1/p/16546451.html