Nik*_*141 1 python opencv numpy image-processing scipy
我有一个 128x128 图像存储为 2D numpy.ndarray(它实际上是一个热图,因此每个条目只是一个标量值)。我已确定:
P = (x0, y0)v = [v0, v1]L,穿过P并垂直于vs(假设具体为s百分比)我想将图像拉伸远离该线,即沿着,L的方向。我所说的“远离”的意思是,上的点在变换下应该保持不变。下图描述了 是正数的情况,因此所有点都远离:vsLLsL
如果s是负数,那么我想让所有点更接近L。
如果L通过原点,那么这只是一个简单的线性变换,我可以使用归一化v时间(1 + s)和一些单位向量L作为我的基向量。然而,因为L不一定通过原点,所以我的印象是这是某种类型的仿射变换。
解决方案的一些首选品质:
\nL变换前后应保持在图像中的相同位置我尝试的第一件事是使用某种类型的图像调整大小cv2.resize。然而,这只允许L通过原点的线性变换,并且还需要我调整实际图像本身的大小。
我尝试的下一件事是cv2.getAffineTransform与 一起使用cv2.warpAffine。由于cv2.getAffineTransform需要两组点(一组在前,一组在后),所以我决定取两个共线点并计算它们的中点(这些是“之前”点),然后将原始的两个点缩放远离中点以获得“之后”点。该过程的图像:
我的代码:
\ndef get_transformation_matrix(pt1 : tuple[float, float], pt2 : tuple[float, float], scale_factor : float) -> np.ndarray:\n """Returns the transformation matrix to scale an image along the line containing `pt1` and `pt2`,\n centered at their midpoint, by `scale_factor` (a percentage increase/decrease)."""\n # the vector from pt1 to pt2, divided by 2\n # AKA the vector from pt1 to center\n # AKA the vector from center to pt2\n slope_vector = np.array([pt2[0] - pt1[0], pt2[1] - pt1[1]]) / 2\n\n # midpoint of the two given points\n center = np.array([(pt2[0] - pt1[0]) / 2 + pt1[0],\n (pt2[1] - pt1[1]) / 2 + pt1[1]])\n \n # new points are just center \xc2\xb1 resized slope_vector\n new_pt1 = center - slope_vector * (1 + scale_factor / 100)\n new_pt2 = center + slope_vector * (1 + scale_factor / 100)\n\n initial_points = np.float32([pt1, center, pt2])\n final_points = np.float32([new_pt1, center, new_pt2])\n \n return cv2.getAffineTransform(initial_points, final_points)\n\nM = wf.get_transformation_matrix([100, 100], [200, 100], 10)\nprint(M)\ndst = cv2.warpAffine(data, M, data.shape)\nRun Code Online (Sandbox Code Playgroud)\n然而M返回的只是[[0. 0. 0.] [0. 0. 0.]],而且dst是一张全黑的图像。我不确定出了什么问题,但我发现的每个教程都使用三角形作为初始点和最终点cv2.getAffineTransform,所以也许使用共线点会导致问题?
我最后看到的是scipy.ndimage.affine_transform,它看起来很有希望,但需要我知道变换矩阵和偏移量,而且我不太确定如何找到它们。
最后,我在这篇文章中添加这个子问题,因为它与上面的内容相关:假设我不想从不变的线延伸L,而是想对点进行均匀的扩张P。我认为这与直线的情况非常相似,除了基向量L不再是单位向量,但我想我不妨确认一下。
如果之前有人问过这个问题,我深表歉意:我环顾四周,没有找到太多东西,但有可能我只是不知道要使用什么搜索词。
\n使用示例中的第一张图像:
import cv2
import numpy as np
P = np.array([158, 298]) # w, h - coordinates from the image
V = np.array([251, 155]) - P
s = np.linalg.norm(V) # length of V
Run Code Online (Sandbox Code Playgroud)
基本上,您需要做的是 1. 平移图像,使 P 位于原点 (0, 0);2.旋转图像,使V平行于其中一个轴(我将使用Y轴);3. 沿 Y 轴缩放图像(如果您在上一步中选择了,则沿 X 轴缩放图像);4. 将图像旋转回来,恢复步骤2;将图像平移回来,恢复步骤 1。所有这些步骤都可以通过仿射变换矩阵来表达,并组合成单个变换矩阵。让我们看看这里,我们所需要的只是“仿射变换”小节中的图片。
第一个(翻译)矩阵是:
M_translate_forward = np.array([[1, 0, -P[0]],
[0, 1, -P[1]],
[0, 0, 1 ]])
Run Code Online (Sandbox Code Playgroud)
第二个(旋转):
cos_phi = abs(V[1]) / s
sin_phi = abs(V[0]) / s
M_rotate_forward = np.array([[cos_phi, -sin_phi, 0],
[sin_phi, cos_phi, 0],
[ 0, 0, 1]])
Run Code Online (Sandbox Code Playgroud)
第三个(沿 Y 轴缩放;我硬编码了一个较小的缩放因子,因为使用图片中的实际矢量长度会产生过多的缩放):
scale_W = 1
scale_H = 1.5 #s
M_scale = np.array([[scale_W, 0, 0],
[ 0, scale_H, 0],
[ 0, 0, 1]])
Run Code Online (Sandbox Code Playgroud)
现在,我们必须将结果旋转和平移回其原始角度和位置。不必手动编写矩阵,只需反转我们在前面步骤中创建的矩阵即可:
M_rotate_backward = np.linalg.inv(M_rotate_forward)
M_translate_backward = np.linalg.inv(M_translate_forward)
Run Code Online (Sandbox Code Playgroud)
为了将它们组合在一起,我们使用矩阵乘法:
M = M_translate_backward @ M_rotate_backward @ M_scale@ M_rotate_forward @ M_translate_forward
Run Code Online (Sandbox Code Playgroud)
现在,将 warpAffine 与它的前两行一起使用(从文档来看,形状组件似乎也应该重新排序):
img = cv2.imread("img.png")
img2 = cv2.warpAffine(img, M[:2], (img.shape[1], img.shape[0]))
cv2.imwrite("out.png", img2)
Run Code Online (Sandbox Code Playgroud)
整个代码示例,只process_image()需要函数,剩下的用于测试:像 一样运行它python code.py image.jpg,然后在带有图像的窗口中单击-拖动-释放 LMB - 它将绘制矢量和线段,并在第二个窗口中它将显示缩放后的图像,并且还会生成缩放因子从 1 到 2.5 的out_000.jpg~out_003.jpg图像,以便您可以检查缩放是否实际上发生在指定线周围。
import cv2
import numpy as np
import sys
def get_x(P, V, y):
return int(-V[1] * (y - P[1]) / V[0]) + P[0]
def get_y(P, V, x):
return int(-V[0] * (x - P[0]) / V[1]) + P[1]
# the actual scaling function - the rest is for drawing and testing
def process_image(img, P, V, S):
M_translate_forward = np.array([[1, 0, -P[0]],
[0, 1, -P[1]],
[0, 0, 1 ]], dtype = float)
Vn = V / np.linalg.norm(V)
cos_phi = Vn[1]
sin_phi = Vn[0]
M_rotate_forward = np.array([[cos_phi, -sin_phi, 0],
[sin_phi, cos_phi, 0],
[ 0, 0, 1]])
scale_W = 1
scale_H = S
M_scale = np.array([[scale_W, 0, 0],
[ 0, scale_H, 0],
[ 0, 0, 1]])
M_rotate_backward = np.linalg.inv(M_rotate_forward)
M_translate_backward = np.linalg.inv(M_translate_forward)
M = M_translate_backward @ M_rotate_backward @ M_scale@ M_rotate_forward @ M_translate_forward
return cv2.warpAffine(img, M[:2], img.shape[:2][::-1])
v_origin = None
v_direction = None
img_tmp = None
def on_mouse_click(event, x, y, flags, param):
global v_origin, v_direction, img_src, img_tmp
if event == cv2.EVENT_LBUTTONDOWN:
img_tmp = img_src.copy()
v_origin = (x, y)
cv2.circle(img_tmp, v_origin, 2, (0, 0, 255), cv2.FILLED)
cv2.imshow("Source", img_tmp)
elif event == cv2.EVENT_LBUTTONUP:
v_direction = (x, y)
P = np.array(v_origin)
V = np.array(v_direction) - P
if all([a == b for a, b in zip(v_origin, v_direction)]): return
cv2.arrowedLine(img_tmp, v_origin, v_direction, (0, 0, 255), 5)
if abs(V[1]) > abs(V[0]):
x0, x1 = P[0] - 100, P[0] + 100
y0, y1 = (get_y(P, V, x) for x in (x0, x1))
else:
y0, y1 = P[1] - 100, P[1] + 100
x0, x1 = (get_x(P, V, y) for y in (y0, y1))
cv2.line(img_tmp, (x0, y0), (x1, y1), (255, 0, 0), 5)
cv2.imshow("Source", img_tmp)
cv2.imshow("Scaled", process_image(img_tmp, P, V, 1.5))
cv2.imwrite("out_000.jpg", img_tmp)
for scale, suffix in ((1.5, "001"), (2.0, "002"), (2.5, "003")):
img2 = process_image(img_tmp, P, V, scale)
fname = f"out_{suffix}.jpg"
cv2.imwrite(fname, img2)
img_src = cv2.imread(sys.argv[1])
img_tmp = img_src.copy()
cv2.namedWindow("Source")
cv2.setMouseCallback("Source", on_mouse_click)
cv2.imshow("Source", img_tmp)
cv2.namedWindow("Scaled")
while True:
key = cv2.waitKey(1) & 0xFF
if key == ord("q"): break
cv2.destroyAllWindows()
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
508 次 |
| 最近记录: |