การทำ dct2 ด้วย numpy

กำลังหัดใช้ numpy และต้องการทำ dct 2d บน array ครับ พอจะมีฟังชันอะไรให้ใช้ได้หรือเปล่า(ผมเขียนเองแล้วช้ามาก for loop กระจายเลย)

khao_lek's picture

โชว์codeหน่อยได้ไหมคับ

>
def DCT(S, N, U):    
    Pi = math.pi    
    F2 = lambda Sl:Sl[0] * math.cos(float(U) * Pi *(Sl[1]+0.5)/float(N))
    s = sum(map(F2, zip(S,range(N))))
 
    if U == 0:
        Cu = 1.0/math.sqrt(8);
    else:
        Cu = 0.5;                   
    return Cu*s
 
def DCT2(S, N):
    # start by do DCT on row    
    res = map( lambda x: [x]*N, [0]*N )
    for u in range(N):
        for v in range(N):
            res[u][v] = DCT(S[u], N, v)
 
    #redo it again on column, we will get res2 as DCT2 matrix
    res2 = map( lambda x: [x]*N, [0]*N)
    for u in range(N):
        for v in range(N):
            r = getcol(res, N, u)
            res2[v][u] = DCT(r, N, v)
 
    return res2[:]
</blockcode>

จะว่าไปก็ไม่แค่ numpy หรอกครับ ผมจับจุดพวก list, sequence ไม่ถูกเลยต่างหาก ว่าเขียนแบบไหนเร็วแบบไหนช้า

หลักการใช้ Numpy ง่าย ๆ คือ ตัด for ออกให้หมดครับ ลองอ่าน Numerical Programming ดูครับ

รู้สึกว่าโค้ดที่ให้ยังไม่สมบูรณ์ครับ ไม่รู้จะใส่ S, N ใน DCT2 เป็นอะไร ขอตัวอย่างแบบครบถ้วนได้ไหมครับ ผมอยากลองใช้ Numpy เขียนดู

S เป็น array 2 D ขนาด N*N ครับ
getcol(S,N,U) return column ที่ U ใน S ครับ
ถ้า numpy คงราวๆนี้มั้ง S[:][U]

อ่อ ผมอ่านแล้วยังงงๆอยู่ึครับ เช่นผมต้องการ apply function F เข้ากับทุกสมาชิกใน array แต่ F ต้องการ argument เป็นตำแหน่งของสมาชิกใน array ด้วย (คือ ต้องการทั้ง A[i] และ i) เราจะเขียนยังไงครับ

สมการเดิมอยู่ที่ http://en.wikipedia.org/wiki/Jpeg#Discrete_cosine_transform ครับ ตรง G(u,v) แต่ผมใช้การคำนวณสองครั้งด้วย http://en.wikipedia.org/wiki/Discrete_cosine_transform#DCT-II แทน เพราะเขาอ้างว่าทำให้ process ได้เร็วกว่าครับ

ดูแล้วโค้ดยังขาดอยู่หลายจุด ผมเลยเติมให้ครับ

from numpy import *
from numpy.random import *
import math
import time
 
def getcol(S,col):
	return list(array(S)[:,col])
 
def DCT(S, N, U):    
	Pi = math.pi    
	F2 = lambda Sl:Sl[0] * math.cos(float(U) * Pi *(Sl[1]+0.5)/float(N))
	s = sum(map(F2, zip(S,range(N))))
 
	if U == 0:
		Cu = 1.0/math.sqrt(8);
	else:
		Cu = 0.5;                   
	return Cu*s
 
def DCT2(S, N):
	# start by do DCT on row    
	res = map( lambda x: [x]*N, [0]*N )
	for u in range(N):
		for v in range(N):
			res[u][v] = DCT(S[u], N, v)
 
	#redo it again on column, we will get res2 as DCT2 matrix
	res2 = map( lambda x: [x]*N, [0]*N)
	for u in range(N):
		for v in range(N):
			r = getcol(res, u)
			res2[v][u] = DCT(r, N, v)
 
	return res2[:]
 
s = randint(1, 256, (40,40))
begin = time.time()
DCT2(s,40)
print time.time()-begin

ผมลองเขียนใหม่ดู โดยลด for และ map ลง เปลี่ยนไปใช้การคูณ matrix แทน

from numpy import *
from numpy.random import *
import time
 
def dct(s):
	row = s.shape[0]
	col = s.shape[1]
	res = zeros_like(s)
 
	cu_row = zeros_like(s)+0.5
	cu_row[:,0] = 1.0/8**0.5
	cu_col = zeros_like(s)+0.5
	cu_col[0,:] = 1.0/8**0.5
	cu_mat = cu_row*cu_col
 
	row_range = arange(row)
	col_range = arange(col)
 
	s = matrix(s)
	for i in row_range :
		for j in col_range :
			cos_row = matrix(cos(pi/row*(row_range+0.5)*i))
			cos_col = matrix(cos(pi/col*(col_range+0.5)*j)).T
			res[i][j] = cos_row * (s * cos_col)
 
	res = cu_mat*res
	return res

แม้ว่าจะเหลือโค้ดเดิมน้อยมาก แต่ผมก็ลอกมาของคุณ house นั่นแหละครับ

ส่วนที่ผมคิดว่าแก้ได้ง่าย และเร็วมีดังนี้ครับ

res = map( lambda x: [x]*N, [0]*N )

แก้เป็น

res = zeros_like(s)

เป็นการสร้าง array โดยใช้โมดูลที่มีมาให้ (ใน Numpy ยิ่งเราเขียนเองน้อยเท่าไร ยิ่งดี และยิ่งเร็ว)

หรือ def DCT2(S, N) ส่งแค่ S ก็น่าจะพอแล้วครับ N ใช้ S.shape หาเอา หากส่งทั้งสองตัว นอกจากจะต้องมี argument มากขึ้นแล้ว อาจเกิดอาการ mismatch ได้

ตัวแปร Cu ก็เป็นอีกตัวครับ ที่แก้ได้เร็ว ยิ่งเราใช้ if มากเท่าไร โปรแกรมยิ่งช้าครับ ซึ่งโปรแกรมของคุณ house ใช้ if ถึง NxN

F2 = lambda Sl:Sl[0] * math.cos(float(U) * Pi *(Sl[1]+0.5)/float(N))

ผมคิดว่าน่าจะเขียนเป็น function ไปเลยดีกว่าครับ เพราะวิธีนี้จะกิน memory ค่อนข้างมาก คือทุกครั้งที่เรียก DCT() จะมีฟังก์ชั่นใหม่ถูกสร้างขึ้นหนึ่งครั้ง หากทำเป็นฟังก์ชั่นไปเลย สร้างครั้งเดียวใช้ได้ตลอดไป

ส่วน A[i] และ i ให้ส่งไปสองตัวได้เลยครับ

แต่สรุปแล้ว ผมไม่สามารถลด for ออกไปได้ทั้งหมด ซึ่งในกรณีนี้ ผมแนะนำว่าควรไปหา library ที่มีอยู่แล้ว หรือเขียนด้วย fortran แล้วใช้ f2py เอา ซึ่ง syntax ของ fortran90 ไม่หนีจาก Numpy มากครับ อาจจะ copy and paste ได้เลยด้วยซ้ำ

khao_lek's picture

อยากให้อธิบาย lambda หน่อยได้ไหมครับ
อยากรู้นะครับ (การประยุกต์ใช้เหมือนกับอะไร, วิธีการใช้งาน และ การสร้าง คราวๆ ก็ได้นะครับ)

sugree's picture

lamba คือฟังก์ชั่นบรรทัดเดียว ลองดูตัวอย่าง

F = lambda a,b: a+b

เหมือนกับ

def F(a,b):
    return a+b
khao_lek's picture

ขอบคุณครับ

ขอบคุณครับ

ย้าย Codenone

ประกาศย้าย Codenone ไปใช้ Forum ของ Blognone แทนครับ ตามไปตั้งกระทู้ต่อได้ที่ Codenone Forum (รายละเอียดอ่านจากกระทู้ ย้าย Codenone ไปรวมกับ Blognone)

กระทู้เก่าๆ จะย้ายตามไปในภายหลัง ตอนนี้ปิดการโพสต์กระทู้ไว้ เหลือไว้เฉพาะอ้างอิงเท่านั้น