【问题标题】:how to apply a three point triangle gradient in opencv?如何在opencv中应用三点三角形渐变?
【发布时间】:2020-09-03 09:13:03
【问题描述】:

假设我们有一个像 this one: 这样的 Delaunay 三角剖分

fillConvexPolygetVoronoiFacetList 制作

里面有三角形可以通过getTriangleList获得。我想画德劳内三角剖分 就像是一个由三角形组成的平滑渐变图像:

如何在opencv中做这样的事情?

【问题讨论】:

标签: python c++ opencv gradient triangulation


【解决方案1】:

这是在 Python/OpenCV 中执行此操作的方法,但它会比我之前介绍的 Python/Wand 版本慢,因为它必须循环并求解重心坐标的每个像素处的线性最小二乘方程。

import cv2
import numpy as np

# References: 
# https://stackoverflow.com/questions/31442826/increasing-efficiency-of-barycentric-coordinate-calculation-in-python
# https://math.stackexchange.com/questions/81178/help-with-cramers-rule-and-barycentric-coordinates

# create black background image
result = np.zeros((500,500,3), dtype=np.uint8)

# Specify (x,y) triangle vertices
a = (250,100)
b = (100,400)
c = (400,400)

# Specify colors
red = (0,0,255)
green = (0,255,0)
blue = (255,0,0)

# Make array of vertices
# ax bx cx
# ay by cy
#  1  1  1
triArr = np.asarray([a[0],b[0],c[0], a[1],b[1],c[1], 1,1,1]).reshape((3, 3))

# Get bounding box of the triangle
xleft = min(a[0], b[0], c[0])
xright = max(a[0], b[0], c[0])
ytop = min(a[1], b[1], c[1])
ybottom = max(a[1], b[1], c[1])

# loop over each pixel, compute barycentric coordinates and interpolate vertex colors
for y in range(ytop, ybottom):

    for x in range(xleft, xright):

        # Store the current point as a matrix
        p = np.array([[x], [y], [1]])

        # Solve for least squares solution to get barycentric coordinates
        (alpha, beta, gamma) = np.linalg.lstsq(triArr, p, rcond=-1)[0]

        # The point is inside the triangle if all the following conditions are met; otherwise outside the triangle
        if alpha > 0 and beta > 0 and gamma > 0:
            # do barycentric interpolation on colors
            color = (red*alpha + green*beta + blue*gamma)
            result[y,x] = color

# show results
cv2.imshow('result', result)
cv2.waitKey(0)
cv2.destroyAllWindows()

# save results
cv2.imwrite('barycentric_triange.png', result)


结果:

【讨论】:

  • 谢谢!我已修改此代码以利用向量计算(由 lstsq 支持)并删除 for 循环以获得更好的性能。看我的回答。
【解决方案2】:

在 OpenCV 中,我不认为有任何现成的功能可以做到这一点。您必须遍历图像中的每个像素并计算重心(区域)插值。例如,请参阅https://codeplea.com/triangular-interpolation

但是,在 Python/Wand(基于 ImageMagick)中,您可以按如下方式进行:

import numpy as np
from wand.image import Image
from wand.color import Color
from wand.drawing import Drawing
from wand.display import display

# define vertices of triangle
p1 = (250, 100)
p2 = (100, 400)
p3 = (400, 400)

# define barycentric colors and vertices
colors = {
    Color('RED'): p1,
    Color('GREEN1'): p2,
    Color('BLUE'): p3
}

# create black image
black = np.zeros([500, 500, 3], dtype=np.uint8)

with Image.from_array(black) as img:
    with img.clone() as mask:
        with Drawing() as draw:
            points = [p1, p2, p3]
            draw.fill_color = Color('white')
            draw.polygon(points)
            draw.draw(mask)
            img.sparse_color('barycentric', colors)
            img.composite_channel('all_channels', mask, 'multiply', 0, 0)   
            img.format = 'png'
            img.save(filename='barycentric_image.png')
            display(img)


结果:

【讨论】:

  • 嗨,有没有什么方法可以使用sparse_color 重复颜色但不同点?例如在这个帖子图片示例中:stackoverflow.com/questions/25910146/…
  • 我不确定你想从那个链接中得到什么。但是您可以使用 sparse_color Voronoi 来获得统一的颜色。见imagemagick.org/Usage/canvas/#sparse-color
  • 是的,抱歉,我不是很清楚。我是 python 和 python-wand 的初学者。我试过你的代码,它工作得很好,但我想知道是否可以使用相同的颜色给 python-wand 超过 3-4 点。在 python-wand 的文档中,写着它接受“dict mapping Color keys”。所以这意味着每种颜色只有一个点?有替代方案吗?用例是将例如 3 个红点、3 个绿点映射到不同的地方,并在所有这些点之间进行插值。
  • 看我之前给你的sparse-color链接。有几种方法。但是,如果我理解您的要求,我认为没有人会按照您的意愿行事。我不确定我是否理解。你能用图片/图表来说明你想要改变哪些点以及你希望它们如何填充吗?
  • 抱歉耽搁了,这是一个完全随机的例子:helpx.adobe.com/content/dam/help/en/illustrator/using/gradients/…(忘记亲爱的形状,我只想生成一个多点的渐变 - 有没有合适的名称)
【解决方案3】:

我修改了@fmw42answer 以利用向量计算(由np.linalg.lstsq 支持)并删除for 循环以获得更好的性能。

    #!/usr/bon/env python
    import cv2
    import numpy as np
    
    # create black background image
    result = np.zeros((500,500,3), dtype=np.uint8)
    
    # Specify (x,y) triangle vertices
    a = (250,100)
    b = (100,400)
    c = (400,400)
    
    # Specify colors
    red = np.array([0,0,255])
    green = np.array([0,255,0])
    blue = np.array([255,0,0])
    
    # Make array of vertices
    # ax bx cx
    # ay by cy
    #  1  1  1
    triArr = np.asarray([a[0],b[0],c[0], a[1],b[1],c[1], 1,1,1]).reshape((3, 3))
    
    # Get bounding box of the triangle
    xleft = min(a[0], b[0], c[0])
    xright = max(a[0], b[0], c[0])
    ytop = min(a[1], b[1], c[1])
    ybottom = max(a[1], b[1], c[1])
    
    # Build np arrays of coordinates of the bounding box
    xs = range(xleft, xright)
    ys = range(ytop, ybottom)
    xv, yv = np.meshgrid(xs, ys)
    xv = xv.flatten()
    yv = yv.flatten()
    
    # Compute all least-squares /
    p = np.array([xv, yv, [1] * len(xv)])
    alphas, betas, gammas = np.linalg.lstsq(triArr, p, rcond=-1)[0]
    
    # Apply mask for pixels within the triangle only
    mask = (alphas > 0) & (betas > 0) & (gammas > 0)
    alphas_m = alphas[mask]
    betas_m = betas[mask]
    gammas_m = gammas[mask]
    xv_m = xv[mask]
    yv_m = yv[mask]
    
    def mul(a, b) :
        # Multiply two vectors into a matrix
        return np.asmatrix(b).T @ np.asmatrix(a)
    
    # Compute and assign colors
    colors = mul(red, alphas_m) + mul(green, betas_m) + mul(blue, gammas_m)
    result[xv_m, yv_m] = colors
    
    # show results
    cv2.imshow('result', result)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

【讨论】:

    猜你喜欢
    • 2021-12-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多