如何在opencv中应用三点三角形渐变?
how to apply a three point triangle gradient in opencv?
假设我们有像 this one:
这样的 Delaunay 三角剖分
生产于 fillConvexPoly
getVoronoiFacetList
里面有三角形可以通过getTriangleList
获得。我想画德劳内三角剖分
就像它是由像这样的三角形组成的平滑渐变图像:
如何在opencv中做这样的事情?
在 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)
结果:
这是 Python/OpenCV 中的方法,但它会比我之前介绍的 Python/Wand 版本慢,因为它必须在每个循环中求解线性最小二乘方程重心坐标的像素。
import cv2
import numpy as np
# References:
#
# 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)
结果:
我修改了 of @fmw42 以利用矢量计算(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()
假设我们有像 this one:
这样的 Delaunay 三角剖分生产于 fillConvexPoly
getVoronoiFacetList
里面有三角形可以通过getTriangleList
获得。我想画德劳内三角剖分
就像它是由像这样的三角形组成的平滑渐变图像:
如何在opencv中做这样的事情?
在 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)
结果:
这是 Python/OpenCV 中的方法,但它会比我之前介绍的 Python/Wand 版本慢,因为它必须在每个循环中求解线性最小二乘方程重心坐标的像素。
import cv2
import numpy as np
# References:
#
# 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)
结果:
我修改了 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()