程序员最近都爱上了这个网站  程序员们快来瞅瞅吧!  it98k网:it98k.com

本站消息

站长简介/公众号

  出租广告位,需要合作请联系站长

+关注
已关注

分类  

暂无分类

标签  

暂无标签

日期归档  

2023-05(1)

2023-06(3)

python 爱心代码

发布于2023-10-15 20:13     阅读(11306)     评论(0)     点赞(30)     收藏(1)


python程序代码:heart.py

  1. from math import cos, pi
  2. import numpy as np
  3. import cv2
  4. import os, glob
  5. class HeartSignal:
  6. def __init__(self, curve="heart", title="Love U", frame_num=20, seed_points_num=2000, seed_num=None, highlight_rate=0.3,
  7. background_img_dir="", set_bg_imgs=False, bg_img_scale=0.2, bg_weight=0.3, curve_weight=0.7, frame_width=1080, frame_height=960, scale=10.1,
  8. base_color=None, highlight_points_color_1=None, highlight_points_color_2=None, wait=100, n_star=5, m_star=2):
  9. super().__init__()
  10. self.curve = curve
  11. self.title = title
  12. self.highlight_points_color_2 = highlight_points_color_2
  13. self.highlight_points_color_1 = highlight_points_color_1
  14. self.highlight_rate = highlight_rate
  15. self.base_color = base_color
  16. self.n_star = n_star
  17. self.m_star = m_star
  18. self.curve_weight = curve_weight
  19. img_paths = glob.glob(background_img_dir + "/*")
  20. self.bg_imgs = []
  21. self.set_bg_imgs = set_bg_imgs
  22. self.bg_weight = bg_weight
  23. if os.path.exists(background_img_dir) and len(img_paths) > 0 and set_bg_imgs:
  24. for img_path in img_paths:
  25. img = cv2.imread(img_path)
  26. self.bg_imgs.append(img)
  27. first_bg = self.bg_imgs[0]
  28. width = int(first_bg.shape[1] * bg_img_scale)
  29. height = int(first_bg.shape[0] * bg_img_scale)
  30. first_bg = cv2.resize(first_bg, (width, height), interpolation=cv2.INTER_AREA)
  31. # 对齐图片,自动裁切中间
  32. new_bg_imgs = [first_bg, ]
  33. for img in self.bg_imgs[1:]:
  34. width_close = abs(first_bg.shape[1] - img.shape[1]) < abs(first_bg.shape[0] - img.shape[0])
  35. if width_close:
  36. # resize
  37. height = int(first_bg.shape[1] / img.shape[1] * img.shape[0])
  38. width = first_bg.shape[1]
  39. img = cv2.resize(img, (width, height), interpolation=cv2.INTER_AREA)
  40. # crop and fill
  41. if img.shape[0] > first_bg.shape[0]:
  42. crop_num = img.shape[0] - first_bg.shape[0]
  43. crop_top = crop_num // 2
  44. crop_bottom = crop_num - crop_top
  45. img = np.delete(img, range(crop_top), axis=0)
  46. img = np.delete(img, range(img.shape[0] - crop_bottom, img.shape[0]), axis=0)
  47. elif img.shape[0] < first_bg.shape[0]:
  48. fill_num = first_bg.shape[0] - img.shape[0]
  49. fill_top = fill_num // 2
  50. fill_bottom = fill_num - fill_top
  51. img = np.concatenate([np.zeros([fill_top, width, 3]), img, np.zeros([fill_bottom, width, 3])], axis=0)
  52. else:
  53. width = int(first_bg.shape[0] / img.shape[0] * img.shape[1])
  54. height = first_bg.shape[0]
  55. img = cv2.resize(img, (width, height), interpolation=cv2.INTER_AREA)
  56. # crop and fill
  57. if img.shape[1] > first_bg.shape[1]:
  58. crop_num = img.shape[1] - first_bg.shape[1]
  59. crop_top = crop_num // 2
  60. crop_bottom = crop_num - crop_top
  61. img = np.delete(img, range(crop_top), axis=1)
  62. img = np.delete(img, range(img.shape[1] - crop_bottom, img.shape[1]), axis=1)
  63. elif img.shape[1] < first_bg.shape[1]:
  64. fill_num = first_bg.shape[1] - img.shape[1]
  65. fill_top = fill_num // 2
  66. fill_bottom = fill_num - fill_top
  67. img = np.concatenate([np.zeros([fill_top, width, 3]), img, np.zeros([fill_bottom, width, 3])], axis=1)
  68. new_bg_imgs.append(img)
  69. self.bg_imgs = new_bg_imgs
  70. assert all(img.shape[0] == first_bg.shape[0] and img.shape[1] == first_bg.shape[1] for img in self.bg_imgs), "背景图片宽和高不一致"
  71. self.frame_width = self.bg_imgs[0].shape[1]
  72. self.frame_height = self.bg_imgs[0].shape[0]
  73. else:
  74. self.frame_width = frame_width # 窗口宽度
  75. self.frame_height = frame_height # 窗口高度
  76. self.center_x = self.frame_width / 2
  77. self.center_y = self.frame_height / 2
  78. self.main_curve_width = -1
  79. self.main_curve_height = -1
  80. self.frame_points = [] # 每帧动态点坐标
  81. self.frame_num = frame_num # 帧数
  82. self.seed_num = seed_num # 伪随机种子,设置以后除光晕外粒子相对位置不动(减少内部闪烁感)
  83. self.seed_points_num = seed_points_num # 主图粒子数
  84. self.scale = scale # 缩放比例
  85. self.wait = wait
  86. def curve_function(self, curve):
  87. curve_dict = {
  88. "heart": self.heart_function,
  89. "butterfly": self.butterfly_function,
  90. "star": self.star_function,
  91. }
  92. return curve_dict[curve]
  93. def heart_function(self, t, frame_idx=0, scale=5.20):
  94. """
  95. 图形方程
  96. :param frame_idx: 帧的索引,根据帧数变换心形
  97. :param scale: 放大比例
  98. :param t: 参数
  99. :return: 坐标
  100. """
  101. trans = 3 - (1 + self.periodic_func(frame_idx, self.frame_num)) * 0.5 # 改变心形饱满度度的参数
  102. x = 15 * (np.sin(t) ** 3)
  103. t = np.where((pi < t) & (t < 2 * pi), 2 * pi - t, t) # 翻转x > 0部分的图形到3、4象限
  104. y = -(14 * np.cos(t) - 4 * np.cos(2 * t) - 2 * np.cos(3 * t) - np.cos(trans * t))
  105. ign_area = 0.15
  106. center_ids = np.where((x > -ign_area) & (x < ign_area))
  107. if np.random.random() > 0.32:
  108. x, y = np.delete(x, center_ids), np.delete(y, center_ids) # 删除稠密部分的扩散,为了美观
  109. # 放大
  110. x *= scale
  111. y *= scale
  112. # 移到画布中央
  113. x += self.center_x
  114. y += self.center_y
  115. # 原心形方程
  116. # x = 15 * (sin(t) ** 3)
  117. # y = -(14 * cos(t) - 4 * cos(2 * t) - 2 * cos(3 * t) - cos(3 * t))
  118. return x.astype(int), y.astype(int)
  119. def butterfly_function(self, t, frame_idx=0, scale=5.2):
  120. """
  121. 图形函数
  122. :param frame_idx:
  123. :param scale: 放大比例
  124. :param t: 参数
  125. :return: 坐标
  126. """
  127. # 基础函数
  128. # t = t * pi
  129. p = np.exp(np.sin(t)) - 2.5 * np.cos(4 * t) + np.sin(t) ** 5
  130. x = 5 * p * np.cos(t)
  131. y = - 5 * p * np.sin(t)
  132. # 放大
  133. x *= scale
  134. y *= scale
  135. # 移到画布中央
  136. x += self.center_x
  137. y += self.center_y
  138. return x.astype(int), y.astype(int)
  139. def star_function(self, t, frame_idx=0, scale=5.2):
  140. n = self.n_star / self.m_star
  141. p = np.cos(pi / n) / np.cos(pi / n - (t % (2 * pi / n)))
  142. x = 15 * p * np.cos(t)
  143. y = 15 * p * np.sin(t)
  144. # 放大
  145. x *= scale
  146. y *= scale
  147. # 移到画布中央
  148. x += self.center_x
  149. y += self.center_y
  150. return x.astype(int), y.astype(int)
  151. def shrink(self, x, y, ratio, offset=1, p=0.5, dist_func="uniform"):
  152. """
  153. 带随机位移的抖动
  154. :param x: 原x
  155. :param y: 原y
  156. :param ratio: 缩放比例
  157. :param p:
  158. :param offset:
  159. :return: 转换后的x,y坐标
  160. """
  161. x_ = (x - self.center_x)
  162. y_ = (y - self.center_y)
  163. force = 1 / ((x_ ** 2 + y_ ** 2) ** p + 1e-30)
  164. dx = ratio * force * x_
  165. dy = ratio * force * y_
  166. def d_offset(x):
  167. if dist_func == "uniform":
  168. return x + np.random.uniform(-offset, offset, size=x.shape)
  169. elif dist_func == "norm":
  170. return x + offset * np.random.normal(0, 1, size=x.shape)
  171. dx, dy = d_offset(dx), d_offset(dy)
  172. return x - dx, y - dy
  173. def scatter(self, x, y, alpha=0.75, beta=0.15):
  174. """
  175. 随机内部扩散的坐标变换
  176. :param alpha: 扩散因子 - 松散
  177. :param x: 原x
  178. :param y: 原y
  179. :param beta: 扩散因子 - 距离
  180. :return: x,y 新坐标
  181. """
  182. ratio_x = - beta * np.log(np.random.random(x.shape) * alpha)
  183. ratio_y = - beta * np.log(np.random.random(y.shape) * alpha)
  184. dx = ratio_x * (x - self.center_x)
  185. dy = ratio_y * (y - self.center_y)
  186. return x - dx, y - dy
  187. def periodic_func(self, x, x_num):
  188. """
  189. 跳动周期曲线
  190. :param p: 参数
  191. :return: y
  192. """
  193. # 可以尝试换其他的动态函数,达到更有力量的效果(贝塞尔?)
  194. def ori_func(t):
  195. return cos(t)
  196. func_period = 2 * pi
  197. return ori_func(x / x_num * func_period)
  198. def gen_points(self, points_num, frame_idx, shape_func):
  199. # 用周期函数计算得到一个因子,用到所有组成部件上,使得各个部分的变化周期一致
  200. cy = self.periodic_func(frame_idx, self.frame_num)
  201. ratio = 10 * cy
  202. # 图形
  203. period = 2 * pi * self.m_star if self.curve == "star" else 2 * pi
  204. seed_points = np.linspace(0, period, points_num)
  205. seed_x, seed_y = shape_func(seed_points, frame_idx, scale=self.scale)
  206. x, y = self.shrink(seed_x, seed_y, ratio, offset=2)
  207. curve_width, curve_height = int(x.max() - x.min()), int(y.max() - y.min())
  208. self.main_curve_width = max(self.main_curve_width, curve_width)
  209. self.main_curve_height = max(self.main_curve_height, curve_height)
  210. point_size = np.random.choice([1, 2], x.shape, replace=True, p=[0.5, 0.5])
  211. tag = np.ones_like(x)
  212. def delete_points(x_, y_, ign_area, ign_prop):
  213. ign_area = ign_area
  214. center_ids = np.where((x_ > self.center_x - ign_area) & (x_ < self.center_x + ign_area))
  215. center_ids = center_ids[0]
  216. np.random.shuffle(center_ids)
  217. del_num = round(len(center_ids) * ign_prop)
  218. del_ids = center_ids[:del_num]
  219. x_, y_ = np.delete(x_, del_ids), np.delete(y_, del_ids) # 删除稠密部分的扩散,为了美观
  220. return x_, y_
  221. # 多层次扩散
  222. for idx, beta in enumerate(np.linspace(0.05, 0.2, 6)):
  223. alpha = 1 - beta
  224. x_, y_ = self.scatter(seed_x, seed_y, alpha, beta)
  225. x_, y_ = self.shrink(x_, y_, ratio, offset=round(beta * 15))
  226. x = np.concatenate((x, x_), 0)
  227. y = np.concatenate((y, y_), 0)
  228. p_size = np.random.choice([1, 2], x_.shape, replace=True, p=[0.55 + beta, 0.45 - beta])
  229. point_size = np.concatenate((point_size, p_size), 0)
  230. tag_ = np.ones_like(x_) * 2
  231. tag = np.concatenate((tag, tag_), 0)
  232. # 光晕
  233. halo_ratio = int(7 + 2 * abs(cy)) # 收缩比例随周期变化
  234. # 基础光晕
  235. x_, y_ = shape_func(seed_points, frame_idx, scale=self.scale + 0.9)
  236. x_1, y_1 = self.shrink(x_, y_, halo_ratio, offset=18, dist_func="uniform")
  237. x_1, y_1 = delete_points(x_1, y_1, 20, 0.5)
  238. x = np.concatenate((x, x_1), 0)
  239. y = np.concatenate((y, y_1), 0)
  240. # 炸裂感光晕
  241. halo_number = int(points_num * 0.6 + points_num * abs(cy)) # 光晕点数也周期变化
  242. seed_points = np.random.uniform(0, 2 * pi, halo_number)
  243. x_, y_ = shape_func(seed_points, frame_idx, scale=self.scale + 0.9)
  244. x_2, y_2 = self.shrink(x_, y_, halo_ratio, offset=int(6 + 15 * abs(cy)), dist_func="norm")
  245. x_2, y_2 = delete_points(x_2, y_2, 20, 0.5)
  246. x = np.concatenate((x, x_2), 0)
  247. y = np.concatenate((y, y_2), 0)
  248. # 膨胀光晕
  249. x_3, y_3 = shape_func(np.linspace(0, 2 * pi, int(points_num * .4)),
  250. frame_idx, scale=self.scale + 0.2)
  251. x_3, y_3 = self.shrink(x_3, y_3, ratio * 2, offset=6)
  252. x = np.concatenate((x, x_3), 0)
  253. y = np.concatenate((y, y_3), 0)
  254. halo_len = x_1.shape[0] + x_2.shape[0] + x_3.shape[0]
  255. p_size = np.random.choice([1, 2, 3], halo_len, replace=True, p=[0.7, 0.2, 0.1])
  256. point_size = np.concatenate((point_size, p_size), 0)
  257. tag_ = np.ones(halo_len) * 2 * 3
  258. tag = np.concatenate((tag, tag_), 0)
  259. x_y = np.around(np.stack([x, y], axis=1), 0)
  260. x, y = x_y[:, 0], x_y[:, 1]
  261. return x, y, point_size, tag
  262. def get_frames(self, shape_func):
  263. for frame_idx in range(self.frame_num):
  264. np.random.seed(self.seed_num)
  265. self.frame_points.append(self.gen_points(self.seed_points_num, frame_idx, shape_func))
  266. frames = []
  267. def add_points(frame, x, y, size, tag):
  268. highlight1 = np.array(self.highlight_points_color_1, dtype='uint8')
  269. highlight2 = np.array(self.highlight_points_color_2, dtype='uint8')
  270. base_col = np.array(self.base_color, dtype='uint8')
  271. x, y = x.astype(int), y.astype(int)
  272. frame[y, x] = base_col
  273. size_2 = np.int64(size == 2)
  274. frame[y, x + size_2] = base_col
  275. frame[y + size_2, x] = base_col
  276. size_3 = np.int64(size == 3)
  277. frame[y + size_3, x] = base_col
  278. frame[y - size_3, x] = base_col
  279. frame[y, x + size_3] = base_col
  280. frame[y, x - size_3] = base_col
  281. frame[y + size_3, x + size_3] = base_col
  282. frame[y - size_3, x - size_3] = base_col
  283. # frame[y - size_3, x + size_3] = color
  284. # frame[y + size_3, x - size_3] = color
  285. # 高光
  286. random_sample = np.random.choice([1, 0], size=tag.shape, p=[self.highlight_rate, 1 - self.highlight_rate])
  287. # tag2_size1 = np.int64((tag <= 2) & (size == 1) & (random_sample == 1))
  288. # frame[y * tag2_size1, x * tag2_size1] = highlight2
  289. tag2_size2 = np.int64((tag <= 2) & (size == 2) & (random_sample == 1))
  290. frame[y * tag2_size2, x * tag2_size2] = highlight1
  291. # frame[y * tag2_size2, (x + 1) * tag2_size2] = highlight2
  292. # frame[(y + 1) * tag2_size2, x * tag2_size2] = highlight2
  293. frame[(y + 1) * tag2_size2, (x + 1) * tag2_size2] = highlight2
  294. for x, y, size, tag in self.frame_points:
  295. frame = np.zeros([self.frame_height, self.frame_width, 3], dtype="uint8")
  296. add_points(frame, x, y, size, tag)
  297. frames.append(frame)
  298. return frames
  299. def draw(self, times=10):
  300. frames = self.get_frames(self.curve_function(self.curve))
  301. for i in range(times):
  302. for frame in frames:
  303. frame = cv2.cvtColor(frame, cv2.COLOR_RGB2BGR)
  304. if len(self.bg_imgs) > 0 and self.set_bg_imgs:
  305. frame = cv2.addWeighted(self.bg_imgs[i % len(self.bg_imgs)], self.bg_weight, frame, self.curve_weight, 0)
  306. cv2.imshow(self.title, frame)
  307. cv2.waitKey(self.wait)
  308. if __name__ == '__main__':
  309. import yaml
  310. settings = yaml.load(open("./settings.yaml", "r", encoding="utf-8"), Loader=yaml.FullLoader)
  311. if settings["wait"] == -1:
  312. settings["wait"] = int(settings["period_time"] / settings["frame_num"])
  313. del settings["period_time"]
  314. times = settings["times"]
  315. del settings["times"]
  316. heart = HeartSignal(seed_num=5201314, **settings)
  317. heart.draw(times)

其中也要到这个py文件的相同的文件夹里引入settings.yaml文件:

  1. # 颜色:RGB三原色数值 0~255
  2. # 设置高光时,尽量选择接近主色的颜色,看起来会和谐一点
  3. # 视频里的蓝色调
  4. #base_color: # 主色 默认玫瑰粉
  5. # - 30
  6. # - 100
  7. # - 100
  8. #highlight_points_color_1: # 高光粒子色1 默认淡紫色
  9. # - 150
  10. # - 120
  11. # - 220
  12. #highlight_points_color_2: # 高光粒子色2 默认淡粉色
  13. # - 128
  14. # - 140
  15. # - 140
  16. base_color: # 主色 默认玫瑰粉
  17. - 228
  18. - 100
  19. - 100
  20. highlight_points_color_1: # 高光粒子色1 默认淡紫色
  21. - 180
  22. - 87
  23. - 200
  24. highlight_points_color_2: # 高光粒子色2 默认淡粉色
  25. - 228
  26. - 140
  27. - 140
  28. period_time: 1000 * 2 # 周期时间,默认1.5s一个周期
  29. times: 5 # 播放周期数,一个周期跳动1次
  30. frame_num: 24 # 一个周期的生成帧数
  31. wait: 60 # 每一帧停留时间, 设置太短可能造成闪屏,设置 -1 自动设置为 period_time / frame_num
  32. seed_points_num: 2000 # 构成主图的种子粒子数,总粒子数是这个的8倍左右(包括散点和光晕)
  33. highlight_rate: 0.2 # 高光粒子的比例
  34. frame_width: 720 # 窗口宽度,单位像素,设置背景图片后失效
  35. frame_height: 640 # 窗口高度,单位像素,设置背景图片后失效
  36. scale: 9.1 # 主图缩放比例
  37. curve: "butterfly" # 图案类型:heart, butterfly, star
  38. n_star: 7 # n-角型/星,如果curve设置成star才会生效,五角星:n-star:5, m-star:2
  39. m_star: 3 # curve设置成star才会生效,n-角形 m-star都是1,n-角星 m-star大于1,比如 七角星:n-star:7, m-star:2 或 3
  40. title: "Love Li Xun" # 仅支持字母,中文乱码
  41. background_img_dir: "src/center_imgs" # 这个目录放置背景图片,建议像素在400 X 400以上,否则可能报错,如果图片实在小,可以调整上面scale把爱心缩小
  42. set_bg_imgs: false # true或false,设置false用默认黑背景
  43. bg_img_scale: 0.6 # 0 - 1,背景图片缩放比例
  44. bg_weight: 0.4 # 0 - 1,背景图片权重,可看做透明度吧
  45. curve_weight: 1 # 同上
  46. # ======================== 推荐参数: 直接复制数值替换上面对应参数 ==================================
  47. # 蝴蝶,报错很可能是蝴蝶缩放大小超出窗口宽和高
  48. # curve: "butterfly"
  49. # frame_width: 800
  50. # frame_height: 720
  51. # scale: 60
  52. # base_color: [100, 100, 228]
  53. # highlight_points_color_1: [180, 87, 200]
  54. # highlight_points_color_2: [228, 140, 140]

本代码是搬运github上的:

网址如下:

https://github.com/131250208/FunnyToys/blob/main/heart.py

演示:

 

原文链接:https://blog.csdn.net/CSH__/article/details/127935092



所属网站分类: 技术文章 > 博客

作者:我Lovepython

链接:https://www.pythonheidong.com/blog/article/2026457/69c728e0c31fee052789/

来源:python黑洞网

任何形式的转载都请注明出处,如有侵权 一经发现 必将追究其法律责任

30 0
收藏该文
已收藏

评论内容:(最多支持255个字符)