mirror of
https://github.com/RekuNote/Hatenatools3.git
synced 2026-10-30 08:19:12 +00:00
Made PPM.py significantly faster with Numpy
This commit is contained in:
1 parent
13826c2ef5
commit
a2d73dcc3c
3 files changed
+840
-57
No files matched your search
@@ -1,7 +1,8 @@
|
||||
#PPM.py by pbsds for python 2.7
|
||||
#AGPL3 licensed
|
||||
#
|
||||
#PIL is required to write images to disk
|
||||
#Numpy is required
|
||||
#PIL is needed to write images to disk
|
||||
#
|
||||
#Credits:
|
||||
#
|
||||
@@ -10,10 +11,12 @@
|
||||
# -Jsafive for supplying .tmb files
|
||||
#
|
||||
import sys, wave#needs os and time aswell
|
||||
import numpy as np
|
||||
try:
|
||||
import Image
|
||||
hasPIL = True
|
||||
except ImportError:
|
||||
print "Warning: PIL not found, image extraction won't work!"
|
||||
hasPIL = False
|
||||
|
||||
#helpers:
|
||||
@@ -69,8 +72,8 @@ def AddPadding(i,pad = 0x10):#used mainly for zipaligning offsets
|
||||
# - instance.OriginalFilename: The original filename
|
||||
# - instance.CurrentFilename: The current filename
|
||||
# - instance.Date: The date in seconds since January 1'st 2000
|
||||
# - instance.GetThumbnail(): The thumbnail stored in a 2D list of uint32 RGBA values. Will decode it first if not done so already
|
||||
# - instance.GetFrame(n): Returns the specified frame as a 2D list of uint32 RGBA values. Only works if ReadFrames was true when reading the PPM file
|
||||
# - instance.GetThumbnail(): The thumbnail stored in a 2D list of uint32 RGBA values (>u4). Will decode it first if not done so already
|
||||
# - instance.GetFrame(n): Returns the specified frame as a 2D list of uint32 RGBA values (>u4). Only works if ReadFrames was true when reading the PPM file
|
||||
# - instance.Looped: A boolean telling us wether it's looped or not
|
||||
# - instance.SFXUsage: A list telling us when the different sound effects are used:
|
||||
# instance.SFXUsage[frame] = [SFX1, SFX2, SFX3] where the SFX's are booleans
|
||||
@@ -127,7 +130,7 @@ class PPM:
|
||||
|
||||
self.RawThumbnail = data[0xa0:0x6a0]
|
||||
if DecodeThumbnail:
|
||||
self.GetThumbnail()#self.Thumbnail[x][y] = uint32 RGBA
|
||||
self.GetThumbnail()#self.Thumbnail[x, y] = uint32 RGBA
|
||||
|
||||
self.Loaded[0] = True
|
||||
|
||||
@@ -147,7 +150,7 @@ class PPM:
|
||||
|
||||
#Read the Frames:
|
||||
if ReadFrames:
|
||||
self.Frames = []#self.Frames[frame] = [inverted(bool), (color1(0-2), color2(0-2)), frame[layer][x][y] = bool]
|
||||
self.Frames = []#self.Frames[frame] = [inverted(bool), (color1(0-2), color2(0-2)), frame[layer, x, y] = bool]
|
||||
for i, offset in enumerate(FrameOffsets):
|
||||
#Read frame header:
|
||||
Inverted = ord(data[offset]) & 0x01 == 0
|
||||
@@ -196,24 +199,19 @@ class PPM:
|
||||
Color1 = Palette[Colors[0]]
|
||||
Color2 = Palette[Colors[1]]
|
||||
|
||||
out = []
|
||||
for x in xrange(256):
|
||||
out.append([])
|
||||
for y in xrange(192):
|
||||
if Frame[0][x][y]:#Color 1:
|
||||
out[-1].append(Color1)
|
||||
elif Frame[1][x][y]:#Color 2:
|
||||
out[-1].append(Color2)
|
||||
else:#background:
|
||||
out[-1].append(Palette[0])
|
||||
out = np.zeros((256, 192), dtype=">u4")
|
||||
out[:] = Palette[0]
|
||||
out[Frame[1]] = Color2
|
||||
out[Frame[0]] = Color1
|
||||
|
||||
return out
|
||||
def GetThumbnail(self, force=False):
|
||||
if (not self.Thumbnail or force) and self.RawThumbnail:
|
||||
if not self.RawThumbnail:
|
||||
return False
|
||||
|
||||
out = [[0 for _ in xrange(48)] for _ in xrange(64)]
|
||||
|
||||
out = np.zeros((64, 48), dtype=">u4")
|
||||
|
||||
palette = (0xFEFEFEFF,#0
|
||||
0x4F4F4FFF,#1
|
||||
0xFFFFFFFF,#2
|
||||
@@ -238,8 +236,8 @@ class PPM:
|
||||
for x in range(0,8,2):
|
||||
#two colors stored in each byte:
|
||||
byte = ord(self.RawThumbnail[(ty*512+tx*64+y*8+x)/2])
|
||||
out[x+tx*8 ][y+ty*8] = palette[byte & 0xF]
|
||||
out[x+tx*8+1][y+ty*8] = palette[byte >> 4]
|
||||
out[x+tx*8 , y+ty*8] = palette[byte & 0xF]
|
||||
out[x+tx*8+1, y+ty*8] = palette[byte >> 4]
|
||||
|
||||
self.Thumbnail = out
|
||||
return self.Thumbnail
|
||||
@@ -253,7 +251,8 @@ class PPM:
|
||||
#Color2Frame = [[0 for i in range(192)] for i in range(256)]
|
||||
|
||||
Encoding = [[], []]
|
||||
Frame = [[[False for _ in xrange(192)] for _ in xrange(256)] for _ in xrange(2)]
|
||||
#Frame = [[[False for _ in xrange(192)] for _ in xrange(256)] for _ in xrange(2)]
|
||||
Frame = np.zeros((2, 256, 192), dtype=np.bool_)
|
||||
|
||||
#Read tags:
|
||||
NewFrame = ord(data[offset]) & 0x80 <> 0
|
||||
@@ -316,14 +315,15 @@ class PPM:
|
||||
offset += 1
|
||||
for _ in range(8):
|
||||
if not byte & 0x01:
|
||||
Frame[layer][x][y] = True
|
||||
Frame[layer, x, y] = True
|
||||
x += 1
|
||||
byte >>= 1
|
||||
else:
|
||||
x += 8
|
||||
UseByte <<= 1
|
||||
for n in range(256):
|
||||
Frame[layer][n][y] = not Frame[layer][n][y]
|
||||
#Frame[layer][n][y] = not Frame[layer][n][y]
|
||||
Frame[layer, n, y] = not Frame[layer, n, y]
|
||||
elif Encoding[layer][y] == 3:#Raw/full
|
||||
x = 0
|
||||
for _ in range(32):
|
||||
@@ -331,30 +331,27 @@ class PPM:
|
||||
offset += 1
|
||||
for _ in range(8):
|
||||
if byte & 0x01:
|
||||
Frame[layer][x][y] = True
|
||||
Frame[layer, x, y] = True
|
||||
x += 1
|
||||
byte >>= 1
|
||||
|
||||
#Merges this frame with the previous frame if NewFrame isn't true:
|
||||
if not NewFrame and PrevFrame:
|
||||
if FrameMove[0] or FrameMove[1]:#Moves the previus frame if specified:
|
||||
NewPrevFrame = [[[False for _ in xrange(192)] for _ in xrange(256)] for _ in xrange(2)]
|
||||
if not NewFrame and PrevFrame <> None:#maybe optimize this better for numpy...
|
||||
if FrameMove[0] or FrameMove[1]:#Moves the previous frame if specified:
|
||||
NewPrevFrame = np.zeros((2, 256, 192), dtype=np.bool_)
|
||||
|
||||
for y in range(192):
|
||||
for y in range(192):#this still isn't perfected
|
||||
for x in range(256):
|
||||
TempX = x+FrameMove[0]
|
||||
TempY = y+FrameMove[1]
|
||||
if 0 <= TempX < 256 and 0 <= TempY < 192:
|
||||
NewPrevFrame[0][TempX][TempY] = PrevFrame[0][x][y]
|
||||
NewPrevFrame[1][TempX][TempY] = PrevFrame[1][x][y]
|
||||
NewPrevFrame[0, TempX, TempY] = PrevFrame[0, x, y]
|
||||
NewPrevFrame[1, TempX, TempY] = PrevFrame[1, x, y]
|
||||
|
||||
PrevFrame = NewPrevFrame
|
||||
|
||||
#Merge the frames:
|
||||
for y in range(192):
|
||||
for x in range(256):
|
||||
Frame[0][x][y] = Frame[0][x][y] <> PrevFrame[0][x][y]
|
||||
Frame[1][x][y] = Frame[1][x][y] <> PrevFrame[1][x][y]
|
||||
#merge the frames:
|
||||
Frame = Frame <> PrevFrame
|
||||
|
||||
return Frame
|
||||
|
||||
@@ -379,7 +376,7 @@ class PPM:
|
||||
# - instance.OriginalFilename: The original filename
|
||||
# - instance.CurrentFilename: The current filename
|
||||
# - instance.Date: The date in seconds since January 1'st 2000
|
||||
# - instance.GetThumbnail(): The thumbnail stored in a 2D list of uint32 RGBA values. Will decode it first if not done so already
|
||||
# - instance.GetThumbnail(): The thumbnail stored in a 2D numpy array of uint32 RGBA values (>u4). Will decode it first if not done so already
|
||||
class TMB:
|
||||
def __init__(self):
|
||||
self.Loaded = False
|
||||
@@ -425,7 +422,7 @@ class TMB:
|
||||
|
||||
self.RawThumbnail = data[0xa0:0x6a0]
|
||||
if DecodeThumbnail:
|
||||
self.GetThumbnail()#self.Thumbnail[x][y] = uint32 RGBA
|
||||
self.GetThumbnail()#self.Thumbnail[x, y] = uint32 RGBA
|
||||
|
||||
self.Loaded = True
|
||||
#return the results
|
||||
@@ -470,8 +467,9 @@ class TMB:
|
||||
if (not self.Thumbnail or force) and self.RawThumbnail:
|
||||
if not self.RawThumbnail:
|
||||
return False
|
||||
|
||||
out = [[0 for _ in xrange(48)] for _ in xrange(64)]
|
||||
|
||||
out = np.zeros((64, 48), dtype=">u4")
|
||||
|
||||
palette = (0xFEFEFEFF,#0
|
||||
0x4F4F4FFF,#1
|
||||
0xFFFFFFFF,#2
|
||||
@@ -496,8 +494,8 @@ class TMB:
|
||||
for x in range(0,8,2):
|
||||
#two colors stored in each byte:
|
||||
byte = ord(self.RawThumbnail[(ty*512+tx*64+y*8+x)/2])
|
||||
out[x+tx*8 ][y+ty*8] = palette[byte & 0xF]
|
||||
out[x+tx*8+1][y+ty*8] = palette[byte >> 4]
|
||||
out[x+tx*8 , y+ty*8] = palette[byte & 0xF]
|
||||
out[x+tx*8+1, y+ty*8] = palette[byte >> 4]
|
||||
|
||||
self.Thumbnail = out
|
||||
return self.Thumbnail
|
||||
@@ -533,8 +531,8 @@ class TMB:
|
||||
for x in range(0,8,2):
|
||||
#two colors stored in each byte:
|
||||
#pos = 0xa0+(ty*512+tx*64+y*8+x)/2
|
||||
p1 = palette.index(self.Thumbnail[x+tx*8 ][y+ty*8])
|
||||
p2 = palette.index(self.Thumbnail[x+tx*8+1][y+ty*8])
|
||||
p1 = palette.index(int(self.Thumbnail[x+tx*8 , y+ty*8]))
|
||||
p2 = palette.index(int(self.Thumbnail[x+tx*8+1, y+ty*8]))
|
||||
out.append(chr(p2<<4 | p1))
|
||||
|
||||
self.RawThumbnail = "".join(out)
|
||||
@@ -545,23 +543,29 @@ class TMB:
|
||||
|
||||
#Function WriteImage:
|
||||
#
|
||||
# Writes a 2D list if uint32 RGBA values as a image files.
|
||||
# Writes a 2D numpy array of uint32 RGBA values (>u4) as a image files.
|
||||
# Designed to work with PPM().Thumbnail or PPM().GetFrame(n)
|
||||
#
|
||||
# This function requires the PIl imaging module
|
||||
def WriteImage(image, outputPath):
|
||||
if not hasPIL or not image: return False
|
||||
if not hasPIL:
|
||||
print "Error: PIL not found!"
|
||||
return False
|
||||
#if not image: return False
|
||||
|
||||
out = []
|
||||
for y in xrange(len(image[0])):
|
||||
for x in xrange(len(image)):
|
||||
out.append(DecAsc(image[x][y], 4))
|
||||
out = image.tostring("F")
|
||||
|
||||
out = Image.fromstring("RGBA", (len(image), len(image[0])), "".join(out))
|
||||
# out = []
|
||||
# for y in xrange(len(image[0])):
|
||||
# for x in xrange(len(image)):
|
||||
# out.append(DecAsc(image[x][y], 4))
|
||||
|
||||
out = Image.fromstring("RGBA", (len(image), len(image[0])), out)
|
||||
|
||||
filetype = outputPath[outputPath.rfind(".")+1:]
|
||||
out.save(outputPath, filetype)
|
||||
|
||||
|
||||
return True
|
||||
|
||||
#this is just for experimenting with sound decoding:
|
||||
@@ -586,10 +590,7 @@ def DecodeSound(outputpath, data):
|
||||
#testing:
|
||||
# p = PPM()
|
||||
# print "loading ppm..."
|
||||
# p.ReadFile("PPMtests/test.ppm", (1, 1, 1))
|
||||
|
||||
# print str(p.OriginalAuthorName), str(p.OriginalAuthorID)
|
||||
# print str(p.EditorAuthorName), str(p.EditorAuthorID)
|
||||
# p.ReadFile("PPMtests/test8.ppm", (1, 1, 1))
|
||||
|
||||
# print "Dumping Thumbnail..."
|
||||
# WriteImage(p.Thumbnail, "thumbnail.png")
|
||||
@@ -610,7 +611,7 @@ def DecodeSound(outputpath, data):
|
||||
if __name__ == '__main__':
|
||||
print " == PPM.py =="
|
||||
print " == by pbsds =="
|
||||
print " == v1.06 =="
|
||||
print " == v1.2 =="
|
||||
print
|
||||
|
||||
if len(sys.argv) < 3:
|
||||
|
||||
Reference in new issue
Block a user
