1. 绘制五角星
1.1 绘制分析
- 国旗的长宽比是3:2,假如长600px,高就是600*2/3=400px;
- 将旗面划分为4个等分长方形,每个长方形的尺寸(300*200);
- 将左上方长方形划分长宽15×10个方格,每个方格的尺寸(20*20),假如l=20px;
- opencv 以图片左上角为原点(0,0),向下为y的正轴,向右为x的正轴;
- 大五角星的中心位于该长方形(5l,5l),左上角位于(2l,2l);
- 大五角星外接圆的直径为6l,半径为3l;
- 四颗小五角星的中心点;
- 第一颗中心点位于(10l,2l),左上角位于(9l,l);
- 第二颗中心点位于(12l,4l),左上角位于(11l,3l);
- 第三颗中心点位于(12l,7l),左上角位于(11l,6l);
- 第四颗中心点位于(10l,9l),左上角位于(9l,8l);
- 每颗小五角星外接圆的直径均为2l,半径为l;
- 四颗小五角星均有一角尖正对大五角星的中心点;
- 国旗RGB16进制码:红色:#de2910;金色:#ffde00。
2. 绘制步骤
- 绘制整体红色的背景图,设置图像的尺寸;
- 根据【1.2 绘制分析】的1,2,3步骤,绘制辅助线;
- 根据五角星的中心点和左上角的坐标,绘制五个五角星;
- 根据四个小五角星和大五角星的中心,计算四个小五角星的旋转角度;
- 将绘制的五角星根据OpenCV-Python学习(5)—— OpenCV 图像像素的读写操作,写入第一步绘制的红色图像中,此处使用坐标是五角星的左上角坐标。
3. 创建红色背景
3.1 实现代码
- 根据宽高,创建纯黑色尺寸的图像;
- 将图像的颜色修改为红色;
- 返回创建的国旗背景图像。
# 创建旗帜
def create_flag(width, height):
img = np.zeros((height,width,3),dtype=np.uint8)
img[::] = get_hex_to_bgr("#de2910")
return img
4. 辅助线绘制
4.1 实现代码
- 根据【1.2 绘制分析】的1,2,3步骤,绘制辅助线;
- 将背景图像进行四等分;
- 将左上角长方形分割成15*10的方格;
- 根据五个五角星的中心点坐标,连线最后好计算旋转角度。
# 绘制辅助线
def draw_line(flag,width,height,half_width,half_height,l):
color = (0,0,0)
lineType = cv.LINE_8
# 四等分
cv.line(flag, (half_width,0), (half_width,height), color,lineType=lineType)
cv.line(flag, (0,half_height), (width,half_height), color,lineType=lineType)
# 四分之一位置辅助线
for i in range(1,int(half_width / l)):
cv.line(flag, (i * l,0), (i * l,half_height), color,lineType=lineType)
for i in range(1,int(half_height / l)):
cv.line(flag, (0,i * l), (half_width,i * l), color,lineType=lineType)
# 五角星圆心链接
o = (5*l,5*l)
others = [(10*l,2*l),(12*l,4*l),(12*l,7*l),(10*l,9*l)]
for point in others:
cv.line(flag, o, point, color,lineType=lineType)
return flag
5. 五角星绘制
5.1 五角星各个坐标点计算
# 以五角星的重心为原点,计算各点坐标
def get_star_point(r = 100):
# 计算没一份的度数和内五边形的r
pi_val = np.pi / 180
min_r = r * np.sin(18 * pi_val) / np.cos(36 * pi_val)
# 外五边形的坐标
a = [0,r]
b = [r * np.cos(18 * pi_val), r * np.sin(18 * pi_val)]
c = [r * np.cos(54 * pi_val), - r * np.sin(54 * pi_val)]
d = [- r * np.cos(54 * pi_val), - r * np.sin(54 * pi_val)]
e = [- r * np.cos(18 * pi_val), r * np.sin(18 * pi_val)]
# 内五边形的坐标
in_a = [min_r * np.cos(54 * pi_val),min_r * np.sin(54 * pi_val)]
in_b = [min_r * np.cos(18 * pi_val),- min_r * np.sin(18 * pi_val)]
in_c = [0, - min_r]
in_d = [- min_r * np.cos(18 * pi_val),- min_r * np.sin(18 * pi_val)]
in_e = [- min_r * np.cos(54 * pi_val),min_r * np.sin(54 * pi_val)]
return {
"out": [a, b, c, d, e],
"in": [in_a, in_b, in_c, in_d, in_e]
}
5.2 坐标系坐标转换
将坐标系中五角星坐标点转换为opencv绘制中坐标点!
# 将坐标系中五角星坐标点转换为opencv绘制中坐标点
def handle_star_point(points = []):
return list(map(lambda items: [int(items[0]),-int(items[1])], points))
5.3 绘制五角星
- 根据五角星半径创建长宽为2r,颜色为#de2910的背景图;
- 根据半径获取五角星的各个点的坐标;
- 将各个点的坐标进行opencv的坐标转换;
- 使用多边形图形绘制方法,绘制五角星;
- 注意: 五角星的圆心重新设置。
# 创建五角星
def create_star(r):
img = np.zeros((r * 2,r * 2,3),dtype=np.uint8)
img[::] = get_hex_to_bgr("#de2910")
points = get_star_point(r)
[a,b,c,d,e] = handle_star_point(points.get("out"))
[in_a,in_b,in_c,in_d,in_e] = handle_star_point(points.get("in"))
# 设置多边形点
pts = np.array([a,in_a,in_a,b,b,in_b,in_b,c,c,in_c,in_c,d,d,in_d,in_d,e,e,in_e,in_e,a])
# 由于图片宽度 r * 2,设置原点(r,r)
pts[:,:] += r
cv.polylines(img, [pts], False, get_hex_to_bgr("#ffde00"),1,lineType=cv.LINE_AA)
cv.fillPoly(img,[pts],get_hex_to_bgr("#ffde00"),lineType=cv.LINE_AA)
return img
5.4 将绘制的五角星写入开始创建的背景中
- 通过传入国旗长的尺寸,计算各个辅助点的基础值;
- 创建红色旗帜;
- 绘制辅助线;
- 创建最大的五角星;
- 大五角星左上角坐标(2L,2L);
- 小五角星从上到下依次绘制;
- 小五角星一左上角坐标(9L,L);
- 小五角星二左上角坐标(11L,3L);
- 小五角星三左上角坐标(11L,6L);
- 小五角星四左上角坐标(9L,8L)。
def draw_flag(size = 600):
# 计算辅助坐标点
width, height,half_width,half_height,l = withdata_base_val(size)
# 创建旗帜
flag = create_flag(width,height)
# 创建最大的五角星
star_max = create_star(3*l)
# 大五角星左上角坐标(2L,2L)
flag[2*l:8*l,2*l:8*l] = star_max
# 创建小五角星
star_min = create_star(l)
# 小五角星从上到下依次绘制
# 小五角星一左上角坐标(9L,L)
flag[l:3*l,9*l:11*l] = star_min
# 小五角星二左上角坐标(11L,3L)
flag[3*l:5*l,11*l:13*l] = star_min
# 小五角星三左上角坐标(11L,6L)
# flag[6*l:8*l,11*l:13*l] = star_min
# 小五角星四左上角坐标(9L,8L)
flag[8*l:10*l,9*l:11*l] = star_min
# 绘制辅助线
flag = draw_line(flag,width,height,half_width,half_height,l)
cv.imshow("flag", flag)
cv.waitKey(0)
cv.destroyAllWindows()
6. 计算四个小五角星的旋转角度
- 大五角星的中心点(5l,5l);
- 四个小五角星的中心点(10l,2l),(12l,4l),(12l,7l),(10l,9l);
- 将四个小五角星的最上边一个角旋转到中心点的连线上;
- 旋转角度 np.arctan((5l-2l)/(10l-5l))计算的角度加90度,注意此处是np.arctan返回的弧度,需要通过np.degrees转为度第一个五角星;90 + np.degrees(np.arctan(3/5));
- 第二个五角星:90 + np.degrees(np.arctan(1/7));
- 第三个五角星:90 - np.degrees(np.arctan(2/7));
- 第四个五角星:90 - np.degrees(np.arctan(4/5))。
6.1 代码实现
def draw_flag(size = 600):
# 计算辅助坐标点
width, height,half_width,half_height,l = withdata_base_val(size)
# 创建旗帜
flag = create_flag(width,height)
# 创建最大的五角星
star_max = create_star(3*l)
# 大五角星左上角坐标(2L,2L)
flag[2*l:8*l,2*l:8*l] = star_max
# 创建小五角星
star_min = create_star(l)
# 小五角星从上到下依次绘制
# 计算旋转角度
[angle1,angle2,angle3,angle4] = np.degrees(np.arctan([3/5,1/7,2/7,4/5]))
# 小五角星一左上角坐标(9L,L)
flag[l:3*l,9*l:11*l] = star_rotate(star_min,90 + angle1)
# 小五角星二左上角坐标(11L,3L)
flag[3*l:5*l,11*l:13*l] = star_rotate(star_min,90 + angle2)
# 小五角星三左上角坐标(11L,6L)
flag[6*l:8*l,11*l:13*l] = star_rotate(star_min,90 - angle3)
# 小五角星四左上角坐标(9L,8L)
flag[8*l:10*l,9*l:11*l] = star_rotate(star_min,90 - angle4)
# 绘制辅助线
flag = draw_line(flag,width,height,half_width,half_height,l)
cv.imshow("flag", flag)
cv.waitKey(0)
cv.destroyAllWindows()
7. 完整代码
import sys
import numpy as np
import cv2 as cv
# 以五角星的重心为原点,计算各点坐标
def get_star_point(r = 100):
# 计算没一份的度数和内五边形的r
pi_val = np.pi / 180
min_r = r * np.sin(18 * pi_val) / np.cos(36 * pi_val)
# 外五边形的坐标
a = [0,r]
b = [r * np.cos(18 * pi_val), r * np.sin(18 * pi_val)]
c = [r * np.cos(54 * pi_val), - r * np.sin(54 * pi_val)]
d = [- r * np.cos(54 * pi_val), - r * np.sin(54 * pi_val)]
e = [- r * np.cos(18 * pi_val), r * np.sin(18 * pi_val)]
# 内五边形的坐标
in_a = [min_r * np.cos(54 * pi_val),min_r * np.sin(54 * pi_val)]
in_b = [min_r * np.cos(18 * pi_val),- min_r * np.sin(18 * pi_val)]
in_c = [0, - min_r]
in_d = [- min_r * np.cos(18 * pi_val),- min_r * np.sin(18 * pi_val)]
in_e = [- min_r * np.cos(54 * pi_val),min_r * np.sin(54 * pi_val)]
return {
"out": [a, b, c, d, e],
"in": [in_a, in_b, in_c, in_d, in_e]
}
# 将坐标系中五角星坐标点转换为opencv绘制中坐标点
def handle_star_point(points = []):
return list(map(lambda items: [int(items[0]),-int(items[1])], points))
# 将16进制颜色转成opencv可以使用BGR颜色值
def get_hex_to_bgr(hex):
hex = hex[1:]
r = int(hex[0:2],16)
g = int(hex[2:4],16)
b = int(hex[4:6], 16)
bgr = (b,g,r)
return bgr
# 计算基础值
def withdata_base_val(width):
width = width
# 由于宽高比是3:2,所以计算高度
height = width * 2 / 3
# 五角星所占用区域是左上角的四分之一位置,计算四分之一位置的宽高
half_width = width / 2
half_height = height / 2
# 绘制四分之一网格宽15等分,高10等分
l = half_width / 15
return list(map(lambda val: int(val),[width,height,half_width,half_height,l]))
# 创建五角星
def create_star(r):
img = np.zeros((r * 2,r * 2,3),dtype=np.uint8)
img[::] = get_hex_to_bgr("#de2910")
points = get_star_point(r)
[a,b,c,d,e] = handle_star_point(points.get("out"))
[in_a,in_b,in_c,in_d,in_e] = handle_star_point(points.get("in"))
# 设置多边形点
pts = np.array([a,in_a,in_a,b,b,in_b,in_b,c,c,in_c,in_c,d,d,in_d,in_d,e,e,in_e,in_e,a])
# 由于图片宽度 r * 2,设置原点(r,r)
pts[:,:] += r
cv.polylines(img, [pts], False, get_hex_to_bgr("#ffde00"),1,lineType=cv.LINE_AA)
cv.fillPoly(img,[pts],get_hex_to_bgr("#ffde00"),lineType=cv.LINE_AA)
return img
# 创建旗帜
def create_flag(width, height):
img = np.zeros((height,width,3),dtype=np.uint8)
img[::] = get_hex_to_bgr("#de2910")
return img
# 绘制辅助线
def draw_line(flag,width,height,half_width,half_height,l):
color = (0,0,0)
lineType = cv.LINE_8
# 四等分
cv.line(flag, (half_width,0), (half_width,height), color,lineType=lineType)
cv.line(flag, (0,half_height), (width,half_height), color,lineType=lineType)
# 四分之一位置辅助线
for i in range(1,int(half_width / l)):
cv.line(flag, (i * l,0), (i * l,half_height), color,lineType=lineType)
for i in range(1,int(half_height / l)):
cv.line(flag, (0,i * l), (half_width,i * l), color,lineType=lineType)
# 五角星圆心链接
o = (5*l,5*l)
others = [(10*l,2*l),(12*l,4*l),(12*l,7*l),(10*l,9*l)]
for point in others:
cv.line(flag, o, point, color,lineType=lineType)
return flag
def star_rotate(star,deg):
# 图片的高度和宽度
height,width = star.shape[:2]
# 以图像中心作为旋转中心
x, y = width//2, height//2
mar = cv.getRotationMatrix2D((x,y), deg, 1.0)
img = cv.warpAffine(star, mar, (width, height), borderValue=get_hex_to_bgr("#de2910"))
return img
def draw_flag(size = 600):
# 计算辅助坐标点
width, height,half_width,half_height,l = withdata_base_val(size)
# 创建旗帜
flag = create_flag(width,height)
# 创建最大的五角星
star_max = create_star(3*l)
# 大五角星左上角坐标(2L,2L)
flag[2*l:8*l,2*l:8*l] = star_max
# 创建小五角星
star_min = create_star(l)
# 小五角星从上到下依次绘制
# 计算旋转角度
[angle1,angle2,angle3,angle4] = np.degrees(np.arctan([3/5,1/7,2/7,4/5]))
# 小五角星一左上角坐标(9L,L)
flag[l:3*l,9*l:11*l] = star_rotate(star_min,90 + angle1)
# 小五角星二左上角坐标(11L,3L)
flag[3*l:5*l,11*l:13*l] = star_rotate(star_min,90 + angle2)
# 小五角星三左上角坐标(11L,6L)
flag[6*l:8*l,11*l:13*l] = star_rotate(star_min,90 - angle3)
# 小五角星四左上角坐标(9L,8L)
flag[8*l:10*l,9*l:11*l] = star_rotate(star_min,90 - angle4)
# 绘制辅助线
flag = draw_line(flag,width,height,half_width,half_height,l)
cv.imshow("flag", flag)
cv.waitKey(0)
cv.destroyAllWindows()
if __name__ == "__main__":
val = sys.argv[1] if len(sys.argv) > 1 else 600
size = int(val)
draw_flag(size)
8. 总结
- 注意:五角星坐标点计算点的坐标系和opencv的坐标系不同,需要进行转换;
- 注意:五角星绘制的时候需要对坐标的中心点进行移动到中心;
- 旋转角度的计算时,需要注意np.arctan返回的是弧度,需要使用np.degrees转换为度;
- 国旗绘制完成,去掉辅助线函数,得到一个完整的国旗图像;
- 可以监听cv.waitKey(0) == ord(‘s’),进行图像的保存, cv.imwrite()。