80x60 RGB 픽셀 배열 이상의 게임 반복을 최적화하십시오
-
19-09-2019 - |
문제
좋아, 그래서 나는 최적화가 필요한 파이썬 코드를 가지고있다.
- 작은 (80x60-pixel) 이미지에 대한 게임 반복이며 RGB 값을 추출합니다.
- 현재 중첩 된 루프를 사용하고 있습니다. 차라리 더 빨리 루프를 위해 교체하고 싶습니다.
map()
C 함수이지만 그렇게하면 x, y 값을 얻을 수있는 방법이나 정의해야 할 함수의 범위에서 정의 된 로컬 값을 어떻게 얻을 수 있는지 알 수 없습니다. - 사용합니다
map()
루프에 대한이 현재 세트보다 빠릅니까? 어떻게 사용하고 여전히 x를 얻을 수 있습니까? - 나는 현재 Pygame 표면을 사용하고 있으며
surfarray/pixelarray
모듈이지만 모든 픽셀을 변경/받기 때문에Surface.get_at()/set_at()
. - 또한 약간 관련이 없습니다 ... Python이 숫자 목록을 가로 지르지 않고 다른 언어와 같이 숫자를 증가시키는 경우 더 빨리 만들 수 있다고 생각하십니까? Python에 Python이 ()와 oreach ()를 위해 정상을 포함하지 않는 이유는 무엇입니까?
- 조건부의 양은 아마도 일을 느리게 만들 것입니다. 가장 느린 부분은 이웃을 확인하는 것입니다 (목록 N을 작성하는 곳) ... 2D 배열에서 슬라이스 액세스로 전체 비트를 교체했지만 제대로 작동하지 않습니다.
수정 된 버전의 코드 :
xr = xrange(80)
yr = xrange(60)
# surface is an instance of pygame.Surface
get_at = surface.get_at()
set_at = surface.set_at()
for x in xr:
# ....
for y in yr:
# ...
pixelR = get_at((x,y))[0]
pixelG = get_at((x,y))[1]
pixelB = get_at((x,y))[2]
# ... more complex stuff here which changes R,G,B values independently of each other
set_at((x,y),(pixelR,pixelG,pixelB))
기능의 정식 버전 :
# xr, yr = xrange(80), xrange(60)
def live(surface,xr,yr):
randint = random.randint
set_at = surface.set_at
get_at = surface.get_at
perfect = perfectNeighbours #
minN = minNeighbours # All global variables that're defined in a config file.
maxN = maxNeighbours #
pos = actual # actual = (80,60)
n = []
append = n.append
NEIGHBOURS = 0
for y in yr: # going height-first for aesthetic reasons.
decay = randint(1,maxDecay)
growth = randint(1,maxGrowth)
for x in xr:
r, g, b, a = get_at((x,y))
del n[:]
NEIGHBOURS = 0
if x>0 and y>0 and x<pos[0]-1 and y<pos[1]-1:
append(get_at((x-1,y-1))[1])
append(get_at((x+1,y-1))[1])
append(get_at((x,y-1))[1])
append(get_at((x-1,y))[1])
append(get_at((x+1,y))[1])
append(get_at((x-1,y+1))[1])
append(get_at((x+1,y+1))[1])
append(get_at((x,y+1))[1])
for a in n:
if a > 63:
NEIGHBOURS += 1
if NEIGHBOURS == 0 and (r,g,b) == (0,0,0): pass
else:
if NEIGHBOURS < minN or NEIGHBOURS > maxN:
g = 0
b = 0
elif NEIGHBOURS==perfect:
g += growth
if g > 255:
g = 255
b += growth
if b > growth: b = growth
else:
if g > 10: r = g-10
if g > 200: b = g-100
if r > growth: g = r
g -= decay
if g < 0:
g = 0
b = 0
r -= 1
if r < 0:
r = 0
set_at((x,y),(r,g,b))
해결책
당신이 읽고 다시 쓰고 있기 때문에 모든 픽셀, 나는 당신이 Surface
.
먼저 80x60 이미지를 촬영하여 32 비트 픽셀의 일반 비트 맵 파일로 변환하는 것이 좋습니다. 그런 다음 픽셀 데이터를 파이썬으로 읽으십시오 array
물체. 이제 당신은 걸을 수 있습니다 array
객체, 값을 읽고, 새로운 값을 계산하고, 새로운 값을 최대 속도로 제자리에 찔렀습니다. 완료되면 새 비트 맵 이미지를 저장 한 다음 Surface
.
24 비트 픽셀을 사용할 수도 있지만 느려져야합니다. 32 비트 픽셀은 하나의 픽셀이 하나의 32 비트 정수 값으로 픽셀 배열을 훨씬 쉽게 색인 할 수 있습니다. 24 비트 포장 픽셀은 각 픽셀이 3 바이트이며, 이는 색인에 훨씬 성가시다는 것을 의미합니다.
나는 당신이 사용을 피하는 것 보다이 접근법에서 훨씬 더 빠른 속도를 얻을 것이라고 생각합니다. for
. 이것을 시도하면 여기에 무언가를 게시하여 얼마나 잘 작동했는지 알려주십시오. 행운을 빕니다.
편집 : 나는 그 생각을 생각했다 array
단일 색인 만 있습니다. 어떻게 두 가지 인덱스를 얻을 수 있는지 잘 모르겠습니다. 나는 당신이 다음과 같은 일을하기를 기대하고있었습니다.
def __i(x, y):
assert(0 <= x < 80)
assert(0 <= y < 60)
i = (y*80 + x) * 4
return i
def red(x, y):
return __a[__i(x, y)]
def green(x, y):
return __a[__i(x, y) + 1]
def blue(x, y):
return __a[__i(x, y) + 2]
def rgb(x, y):
i = __i(x, y)
return __a[i], __a[i + 1], __a[i + 2]
def set_rgb(x, y, r, g, b):
i = __i(x, y)
_a[i] = r
_a[i + 1] = g
_a[i + 2] = b
# example:
r, g, b = rgb(23, 33)
파이썬 이후 array
단일 유형 만 보유 할 수 있으면 유형을 "부호없는 바이트"로 설정 한 다음 내가 보여준 것처럼 색인을 설정하려고합니다.
물론 어디서 __a
실제입니다 array
변하기 쉬운.
이 중 어느 것도 도움이되지 않으면 비트 맵을 목록 또는 세 개의 목록으로 변환하십시오. 중첩 목록을 사용하여 2D 주소를 얻을 수 있습니다.
이게 도움이 되길 바란다. 그것이 도움이되지 않는다면, 나는 당신이하고있는 일을 이해하지 못합니다. 더 설명하면 답을 향상 시키려고 노력할 것입니다.
다른 팁
코드를 느리게 만드는 것은 아마도 루프가 아니며 엄청나게 빠릅니다.
코드 속도가 느리게 진행되는 것은 기능 호출 수입니다. 예를 들어
pixelR = get_at((x,y))[0]
pixelG = get_at((x,y))[1]
pixelB = get_at((x,y))[2]
~이다 많이 느린 것보다 느린 (약 3 배 추측)
r, g, b, a = get_at((x,y))
모든 get_at
, set_at
Call은 표면을 잠금하므로 사용 가능한 방법을 사용하여 픽셀에 직접 액세스하는 것이 더 빠릅니다. 가장 합리적으로 보이는 것은입니다 Surface.get_buffer
.
사용 map
인덱스가 필요하기 때문에 예에서는 작동하지 않습니다. 80 ~ 60 숫자로 사용하기가 더 빠를 수도 있습니다. range()
대신에 xrange()
.
map(do_stuff, ((x, y) for x in xrange(80) for y in xrange(60)))
어디 do_stuff
아마도 그렇게 정의 될 것입니다.
def do_stuff(coords):
r, g, b, a = get_at(coords)
# ... whatever you need to do with those ...
set_at(coords, (r, g, b))
대안 적으로 생성기 표현식 대신 목록 이해력을 두 번째 인수로 사용할 수 있습니다. map
(바꾸다 ((x, y) ...)
~와 함께 [(x, y) ...]
) 및 사용 range
대신에 xrange
. 그래도 성능에 큰 영향을 미치지 않을 것이라고 말하고 싶습니다.
편집하다: GS는 확실히 옳습니다 for
루프는 코드에서 최적화를 필요로하는 주요한 것이 아닙니다 ... 불필요한 호출을 줄입니다. get_at
더 중요합니다. 사실, 루프를 map
실제로 여기서 성능을 향상시킬 것입니다 ... map
더 읽기 쉬운 버전 (아마도 내 FP 배경 때문에 ...). ;-)