Leap motion 擴增實境
利用 OpenCV 校正攝影機以及 Leap Motion Sensor 的位置,再利用 Three.js 來達成擴增實境的效果。
這是試玩的結果(還是跟 IPython notebook 一起探索),所以並沒有以最佳效果為目標。實際上 leap motion sensor 可以放在比較好的位置。
稍早拿來和 google hangout api 配合的效果。
原理上不難,只是中間要解決一堆技術上的小麻煩。
七行就簡單解決了。如果你知道背後的數學,上面的程式碼其實是很直接的。但用 Python 解就麻煩了,因為上面這個遞迴解超出 Python 的遞迴深度。可以用迴圈來取代遞迴,如下面的演算法:
f a b l m | a*b>l || (a==1 && b > m) = 0 | otherwise = (f a c l m)+1+(f c b l m) where c=a+b main = putStrLn $ show result where result= (f 1 100 l2 m) + 49+l2-m l = 10^8 l2 = l `div` 2 m = floor (sqrt (fromIntegral l2)
(程式碼我從 Project Euler 直接貼過來,排版有點亂掉)
def p198(M=10**8, z=100, cnt=0): M2=M/2 a=m=int(M2**0.5) stack=range(z,m) while stack: b=stack[-1] if a*b>M2: a=stack.pop() else: cnt+=1 stack.append(a+b) return cnt+M2-z/2
print int(x**0.5).Haskell 的作法要先將整數轉成浮點數,開根號,然後再用 floor 轉成整數,最後再用 show 轉成字串才能印出。所以是
putStrLn.show $ (floor . sqrt) (fromIntegral n)如果有許多浮點數與整數,那更複雜一點的運算就要轉來轉去。雖說這樣可以強迫正確的設計風格,但有時只是簡單的小計算,這樣也未免太累了。
![]() |
圖片來源: wikipedia |
我們都知道 python 的 iterator很有趣,比方下面這個generator 能生成所有質數(根本就只是把質數的定義講一遍而已):
(n for n in itertools.count() if n>1 and not any(n%k==0 for k in range(2,n)))比較複雜一點的費波納契數列 callable iterator
iter((lambda x:x.append(sum(x)) or x.pop(0)).__get__([1,1]),None)
我們也知道新的 send/yield 能夠玩出不少把戲,不過這篇簡報簡直把 generator 玩得爐火純青 Generator Tricks for System Programming 。
generator 根本就能夠當成 pipeline 來玩。
比方你有一組 log,最後一個欄位不是數字就是 ' -'(當成 0),你想算這些欄位的總和,原來你可能會寫成
但用 generaotr 來思考wwwlog = open("access-log")
total = 0
for line in wwwlog:
bytestr = line.rsplit(None,1)[1]
if bytestr != '-':
total += int(bytestr)
print "Total",total
wwwlog = open("access-log")
bytecolumn = (line.rsplit(None,1)[1] for line in wwwlog)
bytes = (int(x) for x in bytecolumn if x != '-')
print "Total", sum(bytes)
配合另外一個也很神的 generator_tools ,就能玩出更多把戲。
每次一提到中文程式語言,常常會看到人批評「中文程式語言」不實用,浪費時間。
「程式裡面用的英文很簡單啦,不會造成困擾」
「打英文還比較快」
沒有錯,對「一般人」來說,也許的確不實用。
大部分的人都把自己當成是「一般人」(常常也沒有錯),但是忽略掉自己前面和後面都還有人。
同樣的那群「一般人」,他們會偏好中文的文件和軟體,也會感謝翻譯成中文化的作者。
如果單從我自己的角度來看,中文化根本沒必要,浪費時間。因為「文件中用的英文很簡單,不會造成困擾」、「直接看英文還比較快」。
而我的英文還很爛呢。
如果連我都覺得中文化翻譯都是沒有意義、浪費時間,更何況是對英文程度正常的人來說?
還好,不是每個人都只從自己的需求來看世界。
那些想磨練英文而翻譯的人不談(其實翻譯品質堪慮),中文化的工作者本身往往不需要這些中文化文件和軟體的,他們是為了別人而做的。
「一般人」的確需要感謝那些翻譯者,感謝他們不像「一般人」一樣,只因自己不需要,就認為浪費時間、不實用。
很明顯的,發展中文程式語言的人,也不是因為自己需要才發展的。
前幾天到我家裝潢,弄電路、音響的人,有些甚至連 In, Out 這麼簡單的字, 都不是很確定。以前當兵的同袍,英文的程度也是遠低於我的預料。他們才是真正的一般人。
但我認為他們其實都很聰明,如果有適當的環境,如果程式可以用中文寫,他們也許就能學會寫程式。
這是我關心中文程式語言的初衷。即使遠在當兵之前,我還過份高估一般人英文程度的時候也一樣。
因為我知道,這個世界很大,有很多不同的人,很多狀況會出乎我預料。
比方,我也實在不知道 OOPS 開放課程計畫的中文翻譯到底要給誰看。
我無法想像能有多少人能學習高等課程,卻看不懂英文。我無法想像有人需要這套計畫。
我反而能想像那種因為是中文,而望文生義誤解的人(推翻相對論,滿口量子能量場、證明尺規三分角的那類人)。
但同樣的,我知道世界上的人有千百種,如果要我下注,我還是會站在有人需要那邊。
require "tk"
W,H,Blk=40,40,{0xf=>"red",0x2e=>"#0f0",0x27=>"blue",0x47=>"#ff0",
0x66=>"#0ff",0xC6=>"#38f",0x6C=>"#f0f", 0=>"#000"}
Bkeys=Blk.keys.select{|x| x!=0}
new_piece= proc{ proc{|pc| {:a=>(0..16).select{|i|(pc>>i)&1>0}.map!{|i|
[(i>>2)+1,i&3]},:i=>3,:j=> -2,:c=>pc}}.call Bkeys[rand(Bkeys.length)]}
def collide(na,ni,nj)
na.map {|n| true if @board[nj+n[1]][ni+n[0]]!=0}.include? true
end
def redraw
@canvas.delete "all"
@board.each_with_index{|b,j| b.each_with_index {|c,i|
TkcRectangle.new(@canvas, i*W, j*H, i*W+W,j*H+H,
'fill'=>Blk[@p[:a].include?([i-@p[:i],j-@p[:j]])? @p[:c]: c])}}
end
@score, @bw, @bh, @p = 0, 10, 20, new_piece.call
@board=Array.new(@bh+3){|j| j!=@bh ? [0]*@bw+[15]*3:[15]*(@bw+2)+[0]}
(@canvas=TkCanvas.new :width=>@bw*W,:height=>@bh*H).pack
TkRoot.new.bind("Any-Key") { |e| na,ni,nj=@p[:a],@p[:i],@p[:j]
ni+= e.keysym=="Left" ? -1:(e.keysym=="Right" ? 1:0)
na=na.map{|n| [n[1],3-n[0]]} if e.keysym=="Up"
nj+=1 if e.keysym=="Down"
@p[:a],@p[:i],@p[:j]=na,ni,nj unless collide na,ni,nj
redraw}
TkTimer.new(200,-1){|e| !(collide(@p[:a],@p[:i],@p[:j]+1) or !(@p[:j]+=1)) or
proc{ @p[:a].each{|n| @board[n[1]+@p[:j]][n[0]+@p[:i]]=@p[:c]}
@board=@board.inject([]){|a,l| l.include?(0)? a+[l]:[[0]*@bw+[15]*3]+a}
@p[:j]<0 ? exit : @p=new_piece.call}.call
redraw}.start(0).mainloop
這個比 python 的 tk 版要短,不過可讀性差多了。 原來寫的可讀性比較高,最開始甚至還有定義一組 class,不過行數大約是五十行。 後來不用 class,就變成 40行以內,然後再進一步犧牲一點可讀性,就降到了三十行以內。 ruby 高手可能可以找到不少以上程式碼的可笑錯誤吧! ruby 和 python 的比較一直是個熱門話題,不過呢,我得先要等我更了解一點 ruby 再說。
If you have to ask why, you're not a member of the intended audience. Please go on about your business and accept my apologies for this distraction.為什麼要在 vim, emacs, utorrent, ptt2, word (, 或者漢書)裡面玩 tetris?
import sys,random
from pygame import *
score,bw,bh,tickcnt,TICK=0,10,20,0,USEREVENT+1
blk={0xf:0xff0000,0x2e:0xff00,0x27:0xff,0x47:0xffff00,0x66:0xffff,0xC6:0x7fff,0x6C:0xff00ff}
board=[[0xf if j== bh else 0]*bw+[0xf]*3 for j in range(bh+3)]
new_piece=lambda pc:([((z>>2)+1,z&3) for z in range(16) if (pc>>z)&1],3,-2,pc)
piece,px,py,pc=new_piece(random.choice(blk.keys()))
collide=lambda piece,px,py: [1 for (i,j) in piece if board[j+py][i+px]]
def tick():
global piece,px,py,pc,tickcnt,board,score
keys=key.get_pressed()
npx=px+(-1 if keys[K_LEFT] else (1 if keys[K_RIGHT] else 0))
npiece=[(j,3-i) for (i,j) in piece] if keys[K_UP] else piece #Rotate
if not collide(npiece,npx,py): piece,px=npiece,npx
if keys[K_DOWN]: py=(j for j in range(py,bh) if collide(piece,px,j+1)).next()
if tickcnt%5==0:
if collide(piece,px,py+1):
if py<0: sys.exit("GAME OVER: score %i"%score)
for i,j in piece: board[j+py][i+px]=pc
piece,px,py,pc=new_piece(random.choice(blk.keys()))
else: py+=1
nb=[l for l in board[:bh] if 0 in l]+board[bh:]
s=len(board)-len(nb)
if s: score,board= score+2**s,[board[-1][:] for j in range(s)]+nb
for i,j,c in [(i,j,blk.get(board[j][i],0)) for i in range(bw) for j in range(bh)]:
draw.rect(scr,blk[pc] if (i-px,j-py) in piece else c,((i*box[0],j*box[1]),box))
dummy,tickcnt = display.flip(),tickcnt+1
if init(): scr = display.set_mode((400,800))
dummy,box=time.set_timer(TICK,100),(scr.get_width()/bw,scr.get_height()/bh)
while 1:{QUIT: sys.exit, TICK: tick}.get(event.wait().type,str)()
import sys,random
from Tkinter import *
score,bw,bh,H,W=0,10,20,40,40
blk={0xf:"red",0x2e:"#0f0",0x27:"blue",0x47:"#ff0",0x66:"#0ff",0xC6:"#38f",0x6C:"#f0f"}
board=[[0xf if j== bh else 0]*bw+[0xf]*3 for j in range(bh+3)]
new_piece=lambda pc:([((z>>2)+1,z&3) for z in range(16) if (pc>>z)&1],3,-2,pc)
piece,px,py,pc=new_piece(random.choice(blk.keys()))
collide=lambda piece,px,py: [1 for (i,j) in piece if board[j+py][i+px]]
def tick(e=None):
global piece,px,py,pc,tickcnt,board,score
keys= e.keysym if e else ""
npx=px+(-1 if keys=="Left" else (1 if keys=="Right" else 0))
npiece=[(j,3-i) for (i,j) in piece] if keys=="Up" else piece #Rotate
if not collide(npiece,npx,py): piece,px=npiece,npx
if keys=="Down": py=(j for j in range(py,bh) if collide(piece,px,j+1)).next()
if e==None:
if collide(piece,px,py+1):
if py<0: sys.exit("GAME OVER: score %i"%score)
for i,j in piece: board[j+py][i+px]=pc
piece,px,py,pc=new_piece(random.choice(blk.keys()))
else: py+=1
nb=[l for l in board[:bh] if 0 in l]+board[bh:]
s=len(board)-len(nb)
if s: score,board=score+2**s, [board[-1][:] for j in range(s)]+nb
scr.after(300,tick)
scr.delete("all")
for i,j,c in [(i,j,blk.get(board[j][i],"#000")) for i in range(bw) for j in range(bh)]:
scr.create_rectangle(i*W,j*H,(i+1)*W,(j+1)*H,fill=blk[pc] if (i-px,j-py) in piece else c)
scr=Canvas(width=bw*W,height=bh*H,bg="#000")
scr.after(300,tick)
scr.bind_all("<Key>",tick)
scr.pack()
scr.mainloop()
import random,pygame;R=range;L=len;W=10;Q=H=20;P=pygame;G=255;v=lambda b:['0'*W
]*(H-L(b))+b;a=p=x=y=c=d=0;D=P.display;T=P.USEREVENT;m=lambda i,j:j-y in R(L(p)
)and i-x in R(L(p[0]))and'0'!=p[j-y][i-x]and`d+1`or b[j][i];S=D.set_mode((W*Q,H
*Q));k=lambda p,x,y:1-max(p[i][j]!='0'and(y+i>=H or x+j<0or x+j>=W or'0'!=b[i+y
][j+x])for j in R(L(p[0]))for i in R(L(p)));n=E=b=v([]);P.time.set_timer(T,100)
exec("while E!=P.QUIT:\n E=P.event.wait().type;K=P.key.get_pressed()\n if n:d="
"random.randrange(7);q='111';p=[['1'*4],['010',q],['110','011'],['011','110'],"
"[q,'100'],['11','11'],[q,'001']][d];x=W/2-L(p[0])/2;y=n=0\n if E==T:\n [P.dr"
"aw.rect(S,[(h,g,f)for f in(0,G)for g in(0,G)for h in(0,G)][int(m(i/H,i%H))],("
"(i/H*Q,i%H*Q),(Q,Q)))for i in R(W*H)];t=''.join;z=[t([p[L(p)-1-i][j]for i in "
"R(L(p))])for j in R(L(p[0]))];p,x,y=[p,z][K[P.K_UP]*k(z,x,y)],x-(K[P.K_LEFT]*"
"k(p,x-1,y))+(K[P.K_RIGHT]*k(p,x+1,y)),y+(K[P.K_DOWN]*k(p,x,y+1));B=[t([m(i,j)"
"for i in R(W)])for j in R(H)];e=sum(r.find('0')<0for r in B);D.flip();c+=1\n "
" if c%5<1:\n if k(p,x,y+1)<1:\n if y<1:break\n a+=e and 2**e;n=b=v([r"
" for r in B if~r.find('0')])\n else:y+=1\n");print 'GAME OVER: score %i'%a
這樣,算是一個滿意的結果,行數雖然一樣,但是字元比較少,而且程式碼都是直接餵給 python的,沒有經過 exec。如果要使用 exec 的話,那縮到 14 行是沒有問的。from pygame import *;R,T,e=range,USEREVENT+1,[1];W,H=10,20;E={0xf:0xff0000,0x2e:
0xff00,0x27:0xff,0x47:0xffff00,0x66:0xffff,0xC6:0x7fff,0x6C:0xff00ff};B=[[0xf if
j==H else 0]*W+[0xf]*3 for j in R(H+3)];S=n=0;import sys,random as C;C=C.choice
O=lambda:(lambda Z:([((z/4)+1,z&3) for z in R(8) if (Z>>z)&1],3,-2,Z))(C(E.keys(
)));P,X,Y,Z=O();L=lambda P,X,Y:[1 for (i,j) in P if B[j+Y][i+X]];d=display;e=[1
];init();F=d.set_mode((400,800));time.set_timer(T,100);l=len
while (0 if d.flip() or e.__setitem__(0,event.wait().type) else e[0])!=QUIT:
if e[0]==T:K=key.get_pressed();U=X+(-1 if K[K_LEFT] else(1 if K[K_RIGHT] else 0
));V=Y+(1 if K[K_DOWN] else 0);Q=[(j,3-i) for i,j in P] if K[K_UP] else P;(P,X,Y
)=(P,X,Y) if L(Q,U,V) else(Q,U,V);n%5 or(L(P,X,Y+1) and((Y<0 and sys.exit("GAME\
OVER: score %i"%S))or[B[j+Y].__setitem__(i+X,Z) for i,j in P]));(P,X,Y,Z)=(P,X,Y
,Z) if n%5 else(O() if L(P,X,Y+1) else(P,X,Y+1,Z));n+=1;D=[z for z in B[:H] if 0
in z]+B[H:];s=l(B)-l(D);(S,B)=(S+2**s,[B[-1][:] for j in R(s)]+D) if s else(S,B
);[draw.rect(F,E[Z] if (i-X,j-Y) in P else c,((i*40,j*40),(40,40))) for i,j,c in
[(z%W,z/W,E.get(B[z/W][z%W],0)) for z in R(W*H)]]
不過壓縮之後的東西不是文字,我用 base64 再轉過,才能餵到 python 裡面。所以這樣減肥的效果有限。但是這樣有個好處,因為簡單。原來我是打算用一個 dict 然後對程式碼來做多重替換,這樣可以省下不少空間。比方說裡面用 lambda 定義的一些程式,現在我可以用 re 來對程式碼替換了。簡單的說就是一個簡單的巨集功能。但是用 zip 的方式更聰明,因為手動的建字典建巨集工作,現在由 zip 幫你做了。exec("""eJxtUu9P4kAQ/c5fsV/QXTrUFjg17a3JFaoYUZDfsNmYCltuOWhJ25yC8X+/3QJqzDVp++a9
6XRm9sn1Jk4ylG5TSIJoHq9RkKJ7t07vzdnvWM6EGyaK3GwXwVoguc8uuX066Pldf+g/9A3bHUGT2hZU
LNenb9Zr6Kg7tNQF1mtFHCKNL3KsUG2PDvz5+SFSuK7xxR6f1w/fhuG761HGVIRkiJaUoiYSq1Qgi5dG
huZ5qYrCOEFLJCOkRlkI3DSqhLttugrWz/MATR3MMN6d1Yhhw+6kSvL83Wf+JdHF8fTqakdObA5VKFdg
StwejajldmAME5jSNq5j3/wjtikmhLitY/lcd5idV8USlkRX7uiSHlsaE86kMebclZHMMHGv6Vymm1Ww
NVORPa3jucC4prZxaVmqaibXIhc0SHAfbEUX5iJEXUycAlqs4udghQ49QQQe9Nw7qroyF+qrTSLSVMzV
bwZ0bOCyrbu4Y3dPLf+6z/ebwx9k9/ameWT1z4d0Ynyqjfbo4UN0HynDS6iW5X57asx8Sn7MHnQOuZ2C
pqI4Qy38CAMYEifvlubBXiz+oNRS0+ighXPZsPV4OTP5aTnKl6Z4VQs7vfl176P20O86KJ3FiUBFeVrs
EZ37tRHny67pVKv/Pzel6DYdNDGoXUANynafdvCY08wnsnS044bHmg53U7oSEfZIWb8aJJ8hdXrg0Z5R
KZVSYB4r25w5/LsTU8KNRuHYKMy0wvCuOILd2Qh8fWbYYwpzpkgO6hS+m3NUahKuVzNPghczEbMMX4PP
pnmfWJbHsCxPDpbLD2AGGMtSzYKlehBQ5gL11pMfjReu5EZZJNIbePktVwLZztvj4Lb/sXboO933vDvx
V0SZ+RJo75rZdiMgzRKCSeEfrBs8BQ==""".decode("base64").decode("zip"))
Copyright ©
松鼠博士的魔法眼鏡 | Powered by Blogger
Design by SimpleWpThemes | Blogger Theme by NewBloggerThemes.com