一直對抖音上的各類人臉處理算法很感興趣,個別的我以爲目前的水平我能寫個簡單的實現方式,可是涉及複雜的,仍是太菜了。可是以前在抖音上看到了一個用網頁寫的旋轉漢字時鐘的視頻,感受很好玩,並且我覺着寫出來徹底沒問題,就用Pygame 和以前實現的旋轉立方體的底層代碼實現了一下。原視頻地址找不到了,只有個模糊的印象,因此就跟着感受走了。最終實現的東西,須要運算量較大,並且速度很慢。因此我如果有時間了,我會移植爲C或者C++語言再看看。由於個人代碼都是由以前的C語言改來的,因此改回去不會很難,只有麻煩不麻煩。python
另外,本文及實現的內容純粹是爲了好玩而徹底本身編寫的,且原視頻中介紹的是用其餘代碼實現的,並且我也沒有記錄原做者是誰,沒法進行感謝。但不論怎樣,如果有侵權的地方望告知,會馬上進行處理。算法
先上圖:緩存
也能調整大小(非動態),只要修改文件中的一個全局變量就能夠修改大小了:ide
上一篇的 pygame實現旋轉立方體 裏面已經實現了點陣字庫的讀取和顯示,當時只是寫了個單個字符的,如今拓展一下,支持多個字符的就能夠了。由於主要用到中文字符,也就沒添加英文字符的支持。其實很簡單,原理相通的。函數
多字符的實現無非就是計算多字符對應點陣的座標,而後進行投影計算就能夠了。字體
而後根據圓的座標方程得出圓上某點與角度的關係,能夠獲得此角度下字符應該顯示在什麼位置,而後將旋轉後的字符在此位置顯示,就行了。優化
當知道圓的半徑和某點的角度值後,就能獲得座標關係式,以下:動畫
已知 圓心:(x0,y0) 半徑:r 角度:a0 求圓上任意一點座標:(x1,y1),可得公式以下:ui
x1=x0+r*cos(a0 * 3.14/180)
y1=y0+r*sin(a0 * 3.14/180)spa
而後將字符串沿Z軸旋轉(當前角度a0 減去 90度)的度數就可以將字符旋轉到a0的角度,符合當前角度。選擇適當的半徑,就可以將字符調整到合適的位置,而後輸出顯示就能夠了。
中文字符串16x16點陣的顯示:在Transform3D.py 文件中
1 def Show3D16x16Char(font,ax,ay,az,x,y,Z_Size,frontcolor,backcolor,model=1.5,fontmultiple=1.0): 2 '''**********************************************************/ 3 |**函數: Show3D16x16Font 4 |**功能:顯示3D的16x16字符,爲從漢字庫讀取的字符數據,支持數千個漢字 5 | 本函數須要 binascii 庫的支持,不支持此庫的環境沒法運行本函數 6 |**說明:font:欲顯示的漢字,目前只支持一個字符的顯示,只支持漢字的顯示 7 | sx,sy,sz :角度值 8 | x,y: 欲顯示的座標位置 9 | Z_Size:距旋轉軸的距離 10 | frontcolor,backcolor:顏色,前景色和背景色 11 | model:顯示模式,只有模式爲1時才填充背景色,不然只填充前景色 12 | 13 |**做者: wcc 執念執戰 14 |**時間:2019-6-3 15 |********************************************************''' 16 length = len(font) 17 if length == 0:#字符數要大於一個 18 return 19 if length == 1: #只支持一個字符的顯示 20 text = font 21 else: 22 text = font[0] 23 24 25 26 m=0 27 i=0 28 k=0 29 j=0 30 XO=0 31 YO=0 32 #fontmultiple=1.0 #放大倍數,放到外面統一調整 33 34 35 gMAT=[[0.0 for i in range(4)] for n in range(4)] 36 Point0=zuobiaostruct() 37 Point1=zuobiaostruct() 38 PointDis=zuobiaostruct() 39 40 gMAT=structure_3D() #//構建單位矩陣 41 gMAT=Translate3D(gMAT,-8,-8,-8); #//平移變換矩陣 42 gMAT=Scale_3D(gMAT,fontmultiple,fontmultiple,fontmultiple); #//比例變換矩陣 43 gMAT=Rotate_3D(gMAT,ax,ay,az); #//旋轉變換矩陣 44 45 for m in range(length): #理論上就是將單個的字符延長爲多個字符,最重要的就是座標的肯定和計算 46 47 text=font[m] 48 gb2312 = text.encode('gb2312') 49 hex_str = binascii.b2a_hex(gb2312) 50 result = str(hex_str,encoding = 'utf-8' ) #換算出漢字對應的字符地址 51 if eval('0x' + result[:2]) <128: 52 print("請輸入中文") #目前只支持gb2312中文字符,英文字符沒加。原理同樣,能夠取模保存起來 53 return 54 else: 55 56 area = eval('0x' + result[:2]) - 0xA0 57 index = eval('0x' + result[2:]) - 0xA0 #換算爲16進制地址 58 offset = (94 * (area - 1)+ (index - 1))*32 #得出具體地址 59 font_rect = None 60 with open("D:/Mystudy/Python/pyGame/HZK16","rb") as f: #16x16字符集的地址,從中讀取出一個字符的點陣數據 61 f.seek(offset) 62 font_rect = f.read(32)#獲得32個點陣數據 63 f.close() 64 65 66 #gMAT=Translate3D(gMAT,8,8,8); #//平移變換矩陣 x:調節距離中心點的位置,至關於下面Point0.z 67 68 69 70 for i in range(16): 71 for k in range(8): 72 temp = 0x01 << k 73 for j in range(2): 74 data=font_rect[i*2+j] #取出數據 75 if data & temp == temp: 76 77 Point0.x=16-(k+(1-j)*8)+m*16+m*2 #第m個字符的當前點的座標 78 ''' 79 每一個字符16個像素,第m個字符共m*16個像素,每兩個字符間的間距設爲2,則m*16+m*2,前面的時實現當前點陣座標的計算 80 ''' 81 Point0.y=i #(i*8)+k 82 Point0.z=Z_Size #//此參數可以改變字符距離旋轉軸中心的距離 83 84 Point1=vector_matrix_MULTIPLY(Point0,gMAT)#//矢量與矩陣相乘 85 PointDis=PerProject(Point1,XO,YO) #//映射投影 86 Gui_Point(PointDis.x+x,PointDis.y+y,frontcolor) 87 else: 88 if model ==1: #模式爲1 時纔會繪製底色 89 Point0.x=16-(k+(1-j)*8)+m*16+m*2 90 Point0.y=i #(i*8)+k 91 Point0.z=Z_Size #//此參數可以改變字符距離旋轉軸中心的距離 92 93 Point1=vector_matrix_MULTIPLY(Point0,gMAT)#//矢量與矩陣相乘 94 PointDis=PerProject(Point1,XO,YO) #//映射投影 95 Gui_Point(PointDis.x+x,PointDis.y+y,backcolor) 96
下面是實現旋轉時鐘的代碼:
1 # -*- coding: utf-8 -*- 2 """ 3 Created on Sat Jun 29 15:22:12 2019 4 5 @author: Administrator 6 """ 7 8 from Transform3D import * 9 10 11 import pygame 12 import time 13 import math 14 15 fontmultiple=0.8 #字符倍數比例函數,修改次數據能夠實現總體的放大和縮小,建議此數值在0.8-1.3 之間,看起來比較合適 16 17 SCREEN_X_MAX = int(800*fontmultiple) #屏幕的寬和高 18 SCREEN_Y_MAX = int(800*fontmultiple) 19 20 BLACK=(0,0,0) 21 WHITE=(255,255,255) 22 RED=(255,0,0) 23 GREEN=(0,255,0) 24 BLUE=(0,0,255) 25 26 ForeColor = RED #前景色和背景色 27 BackColor = BLACK 28 29 30 pygame.init() 31 screen = pygame.display.set_mode((SCREEN_X_MAX,SCREEN_Y_MAX)) 32 33 34 #myfont=pygame.font.Font(None,1) 35 #textImage=myfont.render("test",True,WHITE) 36 37 Week_zw=("一","二","三","四","五","六","日") #周的中文 38 Day_zw=("零","一","二","三","四","五","六","七","八","九","十") #可用於全部須要0-10的中文的地方 39 Mouth_day=[31,28,31,30,31,30,31,31,30,31,30,31] #月份時長表 40 41 42 43 ''' 44 圓: (x-a)^2+(y-b)^2=r^2 45 a,b:圓心座標 46 r:半徑 47 48 圓心:(x0,y0) 49 半徑:r 50 角度:a0 51 圓上任意一點:(x1,y1) 52 x1=x0+r*cos(a0 * 3.14/180) 53 y1=y0+r*sin(a0 * 3.14/180) 54 ''' 55 def Show_Year(x0,y0,r,angel): 56 ''' 57 顯示年 58 ''' 59 60 timenow=time.localtime(time.time()) 61 yearstr=Day_zw[(int)(timenow[0]/1000)]+Day_zw[int((timenow[0]%1000)/100)]+Day_zw[int((timenow[0]%100)/10)]+Day_zw[int((timenow[0]%10))]+"年" 62 Show3D16x16Char(yearstr,0,0,angel-90,x0,y0,1,ForeColor,BackColor,0,fontmultiple) 63 def Show_Week(x0,y0,r,agl): 64 ''' 65 顯示周 66 ''' 67 timenow=time.localtime(time.time()) #獲取時間 68 for i in range(7): 69 angel=360/7 70 71 if i < timenow[6]: 72 angel=agl-angel*(timenow[6]-i) 73 ForeColor=RED 74 elif i >timenow[6]: 75 angel=agl+angel*(i-timenow[6]) 76 ForeColor=RED 77 else : 78 angel=agl #當前周設爲90度,即在水平方向上 79 ForeColor=WHITE 80 81 x1=x0 + r * math.sin(angel * 3.14/180) #由角度計算出當前應在的座標點 82 y1=y0 + r * math.cos(angel * 3.14/180) 83 Show3D16x16Char("周"+Week_zw[i],0,0,angel-90,(int)(x1),(int)(y1),1,ForeColor,BackColor,0,fontmultiple)#輸出旋轉後的字符串 84 85 def Show_Month(x0,y0,r,agl): 86 ''' 87 顯示月份 88 ''' 89 timenow=time.localtime(time.time())#獲取時間 90 for i in range(1,13): #一年12個月 91 angel=360/12 92 93 if i < timenow[1]: 94 angel=agl-angel*(timenow[1]-i) #其餘月份相應獲得角度推算 95 ForeColor=RED 96 elif i >timenow[1]: 97 angel=agl+angel*(i-timenow[1]) 98 ForeColor=RED 99 else : 100 angel=agl #當前月份設爲90度,即在水平方向上 101 ForeColor=WHITE 102 x1=x0 + r * math.sin(angel * 3.14/180) #由角度計算出當前應在的座標點 103 y1=y0 + r * math.cos(angel * 3.14/180) 104 if i>10: 105 monthstr=Day_zw[10]+Day_zw[i%10]+"月" #得出要顯示的字符串 106 else: 107 monthstr = Day_zw[i]+"月" 108 Show3D16x16Char(monthstr,0,0,angel-90,(int)(x1),(int)(y1),1,ForeColor,BackColor,0,fontmultiple)#顯示字符串 109 110 def Show_Day(x0,y0,r,agl): 111 ''' 112 顯示日期 113 ''' 114 timenow=time.localtime(time.time()) #獲取時間 115 if (timenow[0]%4==0 and timenow[0]%100 !=0) or (timenow[0]%400 == 0): 116 Mouth_day[1]=29 #閏年,補全二月的時長表 117 else: 118 Mouth_day[1]=28 119 120 #Mouth_day 爲月份的時長表 121 for i in range(1,Mouth_day[timenow[1]-1]+1): 122 angel=360/Mouth_day[timenow[1]-1] 123 124 if i < timenow[2]: 125 angel=agl-angel*(timenow[2]-i)#其餘日期相應獲得角度推算 126 ForeColor=RED 127 elif i >timenow[2]: 128 angel=agl+angel*(i-timenow[2]) 129 ForeColor=RED 130 else : 131 angel=agl #當前日期設爲90度,即在水平方向上 132 ForeColor=WHITE 133 x1=x0 + r * math.sin(angel * 3.14/180)#由角度計算出當前應在的座標點 134 y1=y0 + r * math.cos(angel * 3.14/180) 135 if i>20: 136 if i%10 !=0: 137 daystr= Day_zw[(int)(i/10) ]+ Day_zw[10] +Day_zw[i%10]+"日"#得出要顯示的字符串 138 else: 139 daystr= Day_zw[(int)(i/10) ]+ Day_zw[10]+"日" 140 elif i>10: 141 daystr= Day_zw[10]+Day_zw[i%10]+"日" 142 else: 143 daystr=Day_zw[i]+"日" 144 Show3D16x16Char(daystr,0,0,angel-90,(int)(x1),(int)(y1),1,ForeColor,BackColor,0,fontmultiple)#顯示字符串 145 146 147 def Show_Hour(x0,y0,r,agl): 148 ''' 149 顯示時 150 ''' 151 152 timenow=time.localtime(time.time())#獲取時間 153 154 for i in range(0,24): 155 angel=360/24 156 157 if i < timenow[3]: 158 angel=agl-angel*(timenow[3]-i)#其餘時間相應獲得角度推算 159 ForeColor=RED 160 elif i >timenow[3]: 161 angel=agl+angel*(i-timenow[3]) 162 ForeColor=RED 163 else : 164 angel=agl #當前時間設爲90度,即在水平方向上 165 ForeColor=WHITE 166 x1=x0 + r * math.sin(angel * 3.14/180) 167 y1=y0 + r * math.cos(angel * 3.14/180) 168 if i>20: 169 if i%10 !=0: 170 daystr= Day_zw[(int)(i/10) ]+ Day_zw[10] +Day_zw[i%10]+"時" #獲得要顯示的字符串 171 else: 172 daystr= Day_zw[(int)(i/10) ]+ Day_zw[10]+"時" 173 elif i>10: 174 daystr= Day_zw[10]+Day_zw[i%10]+"時" 175 else: 176 daystr=Day_zw[i]+"時" 177 Show3D16x16Char(daystr,0,0,angel-90,(int)(x1),(int)(y1),1,ForeColor,BackColor,0,fontmultiple) #顯示角度計算後的字符串 178 179 def Show_Min(x0,y0,r,agl): 180 ''' 181 顯示分 182 ''' 183 184 timenow=time.localtime(time.time()) 185 186 for i in range(0,60): 187 angel = 360/60 188 189 if i < timenow[4]: 190 angel=agl-angel*(timenow[4]-i)-(360/60/60 * timenow[5] ) #將秒數也代入角度計算中就能夠獲得更精確的角度偏移,並且每秒鐘都會移動,好看 191 ForeColor=RED 192 elif i > timenow[4]: 193 angel = agl + angel * (i-timenow[4]) - (360/60/60 * timenow[5] ) 194 ForeColor = RED 195 else : 196 angel = agl - (360/60/60 * timenow[5] ) #當前日期設爲90度-秒鐘的角度,實現動態顯示 197 ForeColor = WHITE 198 x1 = x0 + r * math.sin(angel * 3.14/180) #由角度計算出當前應在的座標點 199 y1 = y0 + r * math.cos(angel * 3.14/180) 200 if i>=20: 201 if i%10 !=0: 202 daystr = Day_zw[(int)(i/10) ]+ Day_zw[10] +Day_zw[i%10]+"分" #獲得要顯示的字符串 203 else: 204 daystr = Day_zw[(int)(i/10) ]+ Day_zw[10]+"分" 205 elif i>10: 206 daystr = Day_zw[10]+Day_zw[i%10]+"分" 207 else: 208 daystr = Day_zw[i]+"分" 209 Show3D16x16Char(daystr,0,0,angel-90,(int)(x1),(int)(y1),1,ForeColor,BackColor,0,fontmultiple)#顯示角度計算後的字符串 210 211 212 i=0 213 while True: 214 for event in pygame.event.get(): 215 if event.type in (QUIT,KEYDOWN): 216 pygame.quit() 217 sys.exit() 218 219 i+=1 #開始的動畫,調整大小能夠調整速度,由於沒優化,因此顯示速度很慢,數據大一些動畫能儘早結束 220 if i>90: 221 i=90 222 223 Show_Year(SCREEN_X_MAX/2-40*fontmultiple,SCREEN_Y_MAX/2,32,90) #顯示年 224 Show_Week(SCREEN_X_MAX/2,SCREEN_Y_MAX/2,65*fontmultiple,180-i) #顯示周 225 Show_Month(SCREEN_X_MAX/2,SCREEN_Y_MAX/2,110*fontmultiple,i) #顯示月份 226 Show_Day(SCREEN_X_MAX/2,SCREEN_Y_MAX/2,170*fontmultiple,180-i) #顯示日期 227 Show_Hour(SCREEN_X_MAX/2,SCREEN_Y_MAX/2,245*fontmultiple,i) #顯示時 228 Show_Min(SCREEN_X_MAX/2,SCREEN_Y_MAX/2,325*fontmultiple,180-i) #顯示分 229 230 pygame.draw.circle(screen,ForeColor,((int)(SCREEN_X_MAX/2),(int)(SCREEN_Y_MAX/2)),int(55*fontmultiple),1) #顯示幾個圓 231 pygame.draw.circle(screen,ForeColor,((int)(SCREEN_X_MAX/2),(int)(SCREEN_Y_MAX/2)),int(100*fontmultiple),1) 232 pygame.draw.circle(screen,ForeColor,((int)(SCREEN_X_MAX/2),(int)(SCREEN_Y_MAX/2)),int(160*fontmultiple),1) 233 pygame.draw.circle(screen,ForeColor,((int)(SCREEN_X_MAX/2),(int)(SCREEN_Y_MAX/2)),int(235*fontmultiple),1) 234 pygame.draw.circle(screen,ForeColor,((int)(SCREEN_X_MAX/2),(int)(SCREEN_Y_MAX/2)),int(315*fontmultiple),1) 235 236 pygame.display.update() 237 screen.fill(0) #屏幕清零 238 #time.sleep(30/1000)
其餘調用的代碼都在 Transform3D.py文件中,可在個人另外一篇博客《python+基本3D顯示》中下載,而後將上面的字符串顯示代碼加進去,就能夠了。
--------------------------------------------2019-10-26補充----------------------------------
時鐘界面的速度很慢(尤爲是剛開始的旋轉那兒,很卡的,纔剛能到幾幀),流暢度不夠。猜想是由於每一個漢字的點陣都要打開文件讀取一次的緣由(也有多是python自己效率低)。
如果想加快,建議的方法以下:
1:先試試將字體庫讀取如緩存,而後調用,不要每一個點陣都要open一次文件
2:將計算函數使用numpy庫替換和優化
如果還不行,可使用C/C++改寫底層的點陣讀取和計算,可是這麼作不如直接使用C/C++寫完整的代碼了......
------------------------------------------------------------------------------------------------------
本文水平有限,內容不少詞語因爲知識水平問題不嚴謹或很離譜,但主要做爲記錄做用,能理解就行了,但願之後的本身和路過的大神對必要的錯誤提出批評與指點,對好笑的錯誤不要嘲笑,指出來我會改正的。
另外,轉載使用請註明做者和出處,不要刪除文檔中的關於做者的註釋。
隨夢,隨心,隨願,恆執念,爲夢執戰,執戰蒼天! ------------------執念執戰