使用OpenCV,绘画国旗

moyu 发布于 2025-09-27 222 次阅读


一、引言:当代码遇见象征

国旗是国家的象征,五星红旗的红色象征革命,五星黄色象征光明。其设计不仅寓意深刻,在制式上也极为严谨。作为一名技术爱好者,我常常思考如何用程序化的方式来表达这种严谨与庄严。OpenCV作为一个强大的计算机视觉库,其精准的绘图功能正是实现这一想法的完美工具。

我们的目标: 使用OpenCV(cv2)创建一个像素级精确的五星红旗图像。

二、核心工具:OpenCV绘图基础

在开始绘制国旗之前,我们需要了解OpenCV的几个核心绘图函数,它们是我们的“画笔”和“颜料”:

  1. 创建画布 (np.zeros): 图像在OpenCV中是一个由数字组成的矩阵。我们通过np.zeros创建一个指定尺寸(高度,宽度,颜色通道数)的全零矩阵(黑色画布)。
  2. 绘制矩形 (cv2.rectangle): 用于绘制国旗的红色旗面。
  3. 绘制多边形 (cv2.fillPoly): 这是绘制五角星的关键。五角星本质上是一个由10个顶点连接而成的封闭多边形。
  4. 颜色空间: OpenCV默认使用BGR颜色空间,而不是常见的RGB。这意味着红色的表示是(0, 0, 255),而不是(255, 0, 0)

三、五星红旗的绘制逻辑与算法分解

绘制国旗的核心难点在于五角星的定位与绘制。我们不能凭感觉画,必须遵循一定的数学规律。

步骤1:定义画布与旗面

  • 设定一个基础尺寸,例如高度为400像素。根据3:2的比例,宽度即为600像素。
  • 创建一个600x900的红色矩形作为旗面。

步骤2:理解五角星的几何结构

  • 一个正五角星可以内接于一个圆。其十个顶点可以通过圆的半径和角度计算出来。
  • 关键公式:已知圆心 (cx, cy) 和外接圆半径 r,每个顶点相对于圆心的角度为 90° + i * 36°(其中i为顶点索引,从0到9)。通过三角函数即可计算出每个顶点的坐标。
  • 注意:五角星的十个顶点需要按正确的顺序连接,奇数索引点为外顶点,偶数索引点为内顶点。

步骤3:精确定位五星坐标(算法核心)
这是最具技术含量的部分,需要根据国旗标准进行比例计算。

(1).顶点生成算法:

  • 每个五角星有10个顶点(5个外顶点 + 5个内顶点)
  • 外顶点位于半径为r的外接圆上
  • 内顶点位于半径为kid×r的内接圆上
  • 顶点按内外交替顺序排列:内→外→内→外...

(2).角度计算公式:

  • 内顶点角度:i×72° + 54° + 旋转角
  • 外顶点角度:i×72° + 90° + 旋转角
  • 其中i从0到4循环

(3).大星定位:

  • 中心点:(150, 150) - 基于格值系统的坐标原点
  • 半径:90像素(3个格值单位)
  • 不旋转(旋转角=0)

(4).小星定位极坐标:
以大星中心为极点建立极坐标系:

  1. 第一小星:极坐标(5√10×gezhi, arctan(-3/5))
    • 直角坐标:(150+5gezhi, 150-3gezhi)
  2. 第二小星:极坐标(5√2×gezhi, arctan(-1/7))
    • 直角坐标:(150+7gezhi, 150-1gezhi)
  3. 第三小星:极坐标(√53×gezhi, arctan(2/7))
    • 直角坐标:(150+7gezhi, 150+2gezhi)
  4. 第四小星:极坐标(√41×gezhi, arctan(4/5))
    • 直角坐标:(150+5gezhi, 150+4gezhi)

(5).小星旋转角度计算原理:

  • 建立从小星中心指向大星中心的向量
  • 计算该向量与水平轴的夹角
  • 调整初始角度使小星的一个角尖准确指向大星

(6).数学表达式:
旋转角 = arctan(Δy/Δx) - 90°
其中Δx, Δy为从小星指向大星的向量分量

  1. 极坐标→直角坐标
    x = r×cosθ + 中心x
    y = 中心y - r×sinθ (注意图像坐标系y轴向下)
  2. 顶点序列生成
    按内-外交替顺序连接10个顶点形成完整五角星
  3. 填充渲染
    将生成的顶点坐标序列用金黄色填充
import cv2
import math
import numpy as np

# 参数
gezhi_size = 30
# 0.38,内点长度与外点长度比
kid = (2 * math.cos(math.radians(36)) * 10 * math.sin(math.radians(18))) / (1 + math.sin(math.radians(18))) / 10


# 获取小五角星旋转角度函数
def dushuo(coordinate1, coordinate2):
    dx = coordinate1[0] - coordinate2[0]
    dy = -(coordinate1[1] - coordinate2[1])
    ranle = math.atan2(dy, dx)
    ranle = math.degrees(ranle)
    ranle -= 90
    if ranle  < 0:
        ranle += 360
    return  ranle


# dushuo([150,150],[150+5*gezhi,150-3*gezhi])
# 获取五角星坐标函数
def points(r, x_cs, y_cs, rangle):
    sum_points = []
    for i in range(5):
        s1 = i * 72 + 54 + rangle
        s2 = (i) * 72 + 90 + rangle
        rand1 = math.radians(s1)
        rand2 = math.radians(s2)
        x_nei = (kid * r) * math.cos(rand1)
        y_nei = (kid * r) * math.sin(rand1)
        x_wai = (r) * math.cos(rand2)
        y_wai = (r) * math.sin(rand2)
        sum_points.append([x_nei + x_cs, y_cs - y_nei])
        sum_points.append([x_wai + x_cs, y_cs - y_wai])
    return sum_points


# 画出五角星函数
def chuli(sum_points, img):
    pts = np.array(sum_points, dtype=np.int32)
    pts = pts.reshape((-1, 1, 2))
    # cv2.circle(img,center=(150,150),radius=90,color=(0,0,0),thickness=2)
    cv2.fillPoly(img, [pts], (0, 255, 255))


if __name__ == '__main__':
    # 画出红旗
    img = np.zeros((600, 900, 3), dtype="uint8")
    img[:, :, :] = (0, 0, 255)
    # 调用获取四个小五角星角度的函数
    rangle1 = dushuo([150, 150], [150 + 5 * gezhi_size, 150 - 3 * gezhi_size])
    rangle2 = dushuo([150, 150], [150 + 7 * gezhi_size, 150 - 1 * gezhi_size])
    rangle3 = dushuo([150, 150], [150 + 7 * gezhi_size, 150 + 2 * gezhi_size])
    rangle4 = dushuo([150, 150], [150 + 5 * gezhi_size, 150 + 4 * gezhi_size])
    print(rangle1,rangle2,rangle3,rangle4)

    # 画大五角星(r=90,[150,150],0)
    # 第一个小五角星(r=30,150+5ge,150-3ge,1)
    # 第二个小五角星(r=30,150+7ge,150-1ge,2)
    # 第三个小五角星(r=30,150+7ge,150+2ge,3)
    # 第四个小五角星(r=30,150+5ge,150+4ge,4)
    # 获取五个五角星的中心点,建立极坐标轴
    point1 = points(90, 150, 150, 0)
    point2 = points(30, 150 + 5 * gezhi_size, 150 - 3 * gezhi_size, rangle1)
    point3 = points(30, 150 + 7 * gezhi_size, 150 - 1 * gezhi_size, rangle2)
    point4 = points(30, 150 + 7 * gezhi_size, 150 + 2 * gezhi_size, rangle3)
    point5 = points(30, 150 + 5 * gezhi_size, 150 + 4 * gezhi_size, rangle4)
    # 调用生成五角星函数
    chuli(point1, img)
    chuli(point2, img)
    chuli(point3, img)
    chuli(point4, img)
    chuli(point5, img)
    xian_size = 2
    # 画线
    for i in range(9):
        cv2.line(img, (0, 30 * (i + 1)), (450, 30 * (i + 1)), thickness=xian_size, color=(0, 0, 0))
    cv2.line(img, (0, 300), (900, 300), color=(0, 0, 0), thickness=xian_size)
    for i in range(14):
        cv2.line(img, (30 * (i + 1), 0), (30 * (i + 1), 300), thickness=xian_size, color=(0, 0, 0))
    cv2.line(img, (450, 0), (450, 600), color=(0, 0, 0), thickness=xian_size)
    # 连接线
    cv2.line(img, (150, 150), (150 + 5 * gezhi_size, 150 - 3 * gezhi_size), color=(0, 0, 0), thickness=xian_size)
    cv2.line(img, (150, 150), (150 + 7 * gezhi_size, 150 - 1 * gezhi_size), color=(0, 0, 0), thickness=xian_size)
    cv2.line(img, (150, 150), (150 + 7 * gezhi_size, 150 + 2 * gezhi_size), color=(0, 0, 0), thickness=xian_size)
    cv2.line(img, (150, 150), (150 + 5 * gezhi_size, 150 + 4 * gezhi_size), color=(0, 0, 0), thickness=xian_size)
    # 画原
    cv2.circle(img, (150, 150), color=(0, 0, 0), radius=90, thickness=xian_size)
    cv2.circle(img, (150 + 5 * gezhi_size, 150 - 3 * gezhi_size), color=(0, 0, 0), radius=30, thickness=xian_size)
    cv2.circle(img, (150 + 7 * gezhi_size, 150 - 1 * gezhi_size), color=(0, 0, 0), radius=30, thickness=xian_size)
    cv2.circle(img, (150 + 7 * gezhi_size, 150 + 2 * gezhi_size), color=(0, 0, 0), radius=30, thickness=xian_size)
    cv2.circle(img, (150 + 5 * gezhi_size, 150 + 4 * gezhi_size), color=(0, 0, 0), radius=30, thickness=xian_size)

    cv2.imshow('五星红旗', img)
    cv2.waitKey()
    cv2.destroyAllWindows()

求出每一个小五角星旋转角度依次为:

text

120.96375653207352    98.13010235415595     74.05460409907712      51.34019174590992

此作者没有提供个人介绍。
最后更新于 2025-10-17