No older revisions available
No older revisions available
~cpp
#format python
#---------------- pngtest.py -------------------------------------------
# -*- coding: utf-8 -*-
import unittest
import zlib
import Image
import misc
from ThomPNG import *
class TestPngFormat(unittest.TestCase):
def setUp(self):
self.png = ThomPNG('tmp.png')
def tearDown(self):
self.png.close()
def testFirstEightBytes(self):
#eightBytesFromFile = self.png.firstEightBytes()
eightBytes = [137,80,78,71,13,10,26,10]
eightBytesFromFile = self.png.firstEightBytes()
for i in range(8):
self.assertEqual( chr(eightBytes[i]), eightBytesFromFile[i] )
def testChunkLength(self):
chunkInfo = self.png.nextChunk()
self.assertEqual(13, chunkInfo[0]) # 0 = chunkLen
def testImageIHDR(self):
im = Image.open(self.png.f.name)
chunkInfo = self.png.nextChunk()
# Width
imageWidth = ord(chunkInfo[2][0]) * (256**3)
imageWidth += ord(chunkInfo[2][1]) * (256**2)
imageWidth += ord(chunkInfo[2][2]) * (256**1)
imageWidth += ord(chunkInfo[2][3])
self.assertEqual(im.size[0], imageWidth)
# Height
imageHeight = ord(chunkInfo[2][4]) * (256**3)
imageHeight += ord(chunkInfo[2][5]) * (256**2)
imageHeight += ord(chunkInfo[2][6]) * (256**1)
imageHeight += ord(chunkInfo[2][7])
self.assertEqual(im.size[1], imageHeight)
# BitDepth
bitDepth = ord(chunkInfo[2][8])
self.assertEqual(True, bitDepth in [1,2,4,8,16])
# ColorType
colorType = ord(chunkInfo[2][9])
self.assertEqual(True, colorType in [0,2,3,4,6])
# CompressionMethod
compressionMethod = ord(chunkInfo[2][10])
# Filter Method
filterMethod = ord(chunkInfo[2][11])
# InterlaceMethod
interlaceMethod = ord(chunkInfo[2][12])
print '\nBitDepth, ColorType, CompressionMethod, FilterMethod = (%d, %d, %d, %d, %d)' \
% (bitDepth, colorType, compressionMethod, filterMethod, interlaceMethod)
def testImageDataSize(self):
self.png.nextChunk() # IHDR 부분 건너뛰기
totalSize = 0
loop=0
while True:
loop+=1
chunkInfo = self.png.nextChunk()
if chunkInfo[1] != 'IDAT':
self.assertEqual('IEND', chunkInfo[1])
self.assertEqual(0, chunkInfo[0])
break
totalSize += chunkInfo[0]
# 전체 파일 크기와 비교.
#self.assertEqual(224975, 8+4+4+13+4+(12*loop)+totalSize)
def testRGB(self):
rgb = RGB(1,1,1)
self.assertEqual((1,1,1), rgb)
# CRC 테스트
def testCRC(self):
chunkInfo = self.png.nextChunk()
fileCRC = chunkInfo[3]
computedCRC = self.png.computeCRC(chunkInfo[1]+chunkInfo[2])
expectedCRC = [(computedCRC&0xff000000)>>24,(computedCRC&0x00ff0000)>>16,(computedCRC&0x0000ff00)>>8,(computedCRC&0x000000ff)]
fileCRCInNum = [ord(fileCRC[0]), ord(fileCRC[1]), ord(fileCRC[2]), ord(fileCRC[3])]
self.assertEqual(expectedCRC, fileCRCInNum)
def testPaeth(self):
self.assertEqual(10, self.png.paethDictor(10,11,12))
self.assertEqual(87, self.png.paethDictor(0,87,0))
# PIL과 비교. scanline by scanline 으로 비교해야 한다.
def testCompareWithPIL(self):
im = Image.open(self.png.f.name)
zlibdata = self.png.getData()
base = [ord(zlibdata[1]), ord(zlibdata[2]), ord(zlibdata[3])]
rgbmap = []
for i in range(im.size[1]):
idx = self.png.getIdx(0,i)-1
scanline = self.png.makeScanline(base, i, zlibdata, rgbmap )
rgbmap.append(scanline)
self.assertTrue(len(rgbmap)>0)
for j in range(im.size[1]):
for i in range(im.size[0]) :
self.assertTrue(rgbmap[j][i].compare(im.getpixel((i,j)) ) )
if __name__=='__main__':
print '\n'
print '----------------------------------------'
print ' PNG Format TEST'
print '----------------------------------------'
print '\n'
unittest.main(argv=('', '-v'))
#---------------- ThomPNG.py -------------------------------------------
# -*- coding: utf-8 -*-
import Image
import zlib
class ThomPNG:
def __init__(self, filename):
self.f = file(filename, 'r')
self.leadingEightBytes = None
self.firstEightBytes()
self.crc = CRC()
self.data = None; self.width = None; self.height = None; self.bitDepth = None
def close(self):
self.f.close()
def firstEightBytes(self):
if self.leadingEightBytes == None:
self.leadingEightBytes = self.f.read(8)
return self.leadingEightBytes
def nextChunk(self):
buff = self.f.read(8)
chunkType = buff[4:]
chunkLen = ord(buff[0]) * (256 ** 3 )
chunkLen += ord(buff[1]) * (256 ** 2 )
chunkLen += ord(buff[2]) * (256)
chunkLen += ord(buff[3])
chunk = self.f.read(chunkLen)
if chunkType == 'IHDR':
self.makeInfo(chunk)
crc = self.f.read(4)
return [chunkLen, chunkType, chunk, crc]
def makeInfo(self, chunk): #width, height 등의 정보 얻기
# Width
self.width = ord(chunk[0]) * (256**3)
self.width += ord(chunk[1]) * (256**2)
self.width += ord(chunk[2]) * (256**1)
self.width += ord(chunk[3])
# Height
self.height = ord(chunk[4]) * (256**3)
self.height += ord(chunk[5]) * (256**2)
self.height += ord(chunk[6]) * (256**1)
self.height += ord(chunk[7])
def getData(self):
if self.data != None:
return self.data
self.nextChunk() # IHDR 읽기
# PLTE 등은 아직..
# ...
self.data = ''
while True:
chunks = self.nextChunk()
if chunks[1]!= 'IDAT':
break
self.data += chunks[2]
self.data = zlib.decompress(self.data)
return self.data
def computeCRC(self, buf):
return self.crc.getCRC(buf)
def makeScanline(self, basepixel, ypos, stream, rgbmap): # method 는 PNG filter type, stream은 zlib 으로 decomposite된 값들
idx = self.getIdx(0, ypos)-1
method = ord(stream[idx])
if method == 0:
pass
elif method == 1: # sub
return self.makeScanlineBySub(basepixel, ypos, stream)
elif method == 2:
return self.makeScanlineByUp(basepixel, ypos, stream, rgbmap)
elif method == 3:
return self.makeScanlineByAverage(basepixel, ypos, stream, rgbmap)
elif method == 4: #paeth
return self.makeScanlineByPaeth(basepixel, ypos, stream, rgbmap)
def makeScanlineBySub(self, basepixel, ypos, stream):
scanline = []
for i in range(self.width):
idx = self.getIdx(i, ypos)
filteredR = ord(stream[idx])
filteredG = ord(stream[idx+1])
filteredB = ord(stream[idx+2])
if i > 0 : # 인덱스가 0이 아닐 경우는 옆칸에서 더한다.
filteredR += scanline[i-1].r
filteredG += scanline[i-1].g
filteredB += scanline[i-1].b
rgbs = self.pureRGB(filteredR, filteredG, filteredB)
scanline.append(RGB(rgbs[0], rgbs[1], rgbs[2]) )
return scanline
def makeScanlineByUp(self, basepixel, ypos, stream, rgbmap):
scanline = []
for i in range(self.width):
idx = self.getIdx(i, ypos)
filteredR = ord(stream[idx])
filteredG = ord(stream[idx+1])
filteredB = ord(stream[idx+2])
if ypos > 0 : # y가 0 이면 윗칸에서 더할수 없지만 0보다 크면 더해야한다
filteredR += rgbmap[-1][i].r
filteredG += rgbmap[-1][i].g
filteredB += rgbmap[-1][i].b
rgbs = self.pureRGB(filteredR, filteredG, filteredB)
scanline.append(RGB(rgbs[0], rgbs[1], rgbs[2]) )
return scanline
def makeScanlineByAverage(self, basepixel, ypos, stream, rgbmap):
scanline = []
for i in range(self.width):
idx = self.getIdx(i, ypos)
filteredR = ord(stream[idx])
filteredG = ord(stream[idx+1])
filteredB = ord(stream[idx+2])
leftR = 0; leftG = 0; leftB = 0
upR = 0; upG = 0; upB = 0
if ypos > 0 :
upR = rgbmap[-1][i].r
upG = rgbmap[-1][i].g
upB = rgbmap[-1][i].b
if i > 0 :
leftR = scanline[i-1].r
leftG = scanline[i-1].g
leftB = scanline[i-1].b
filteredR += self.floor((leftR+upR)/2.0)
filteredG += self.floor((leftG+upG)/2.0)
filteredB += self.floor((leftB+upB)/2.0)
rgbs = self.pureRGB(filteredR, filteredG, filteredB)
scanline.append(RGB(rgbs[0], rgbs[1], rgbs[2]) )
return scanline
def makeScanlineByPaeth(self, basepixel, ypos, stream, rgbmap):
scanline = []
for i in range(self.width):
idx = self.getIdx(i, ypos)
filteredR = ord(stream[idx])
filteredG = ord(stream[idx+1])
filteredB = ord(stream[idx+2])
if i == 0 and ypos > 0 :
filteredR += self.paethDictor(0, rgbmap[-1][i].r, 0)
filteredG += self.paethDictor(0, rgbmap[-1][i].g, 0)
filteredB += self.paethDictor(0, rgbmap[-1][i].b, 0)
else:
if ypos == 0 :
filteredR += self.paethDictor(scanline[i-1].r, 0, 0)
filteredG += self.paethDictor(scanline[i-1].g, 0, 0)
filteredB += self.paethDictor(scanline[i-1].b, 0, 0)
else:
filteredR += self.paethDictor(scanline[i-1].r, rgbmap[-1][i].r, rgbmap[-1][i-1].r)
filteredG += self.paethDictor(scanline[i-1].g, rgbmap[-1][i].g, rgbmap[-1][i-1].g)
filteredB += self.paethDictor(scanline[i-1].b, rgbmap[-1][i].b, rgbmap[-1][i-1].b)
rgbs = self.pureRGB(filteredR, filteredG, filteredB)
scanline.append(RGB(rgbs[0], rgbs[1], rgbs[2]) )
return scanline
def pureRGB(self, r, g, b):
if r >= 256 : r -= 256
if g >= 256 : g -= 256
if b >= 256 : b -= 256
if r < 0 : r += 256
if g < 0 : g += 256
if b < 0 : b += 256
return (r, g, b)
def paethDictor(self, a, b, c):
p = a + b - c
pa = abs(p-a)
pb = abs(p-b)
pc = abs(p-c)
if pa<=pb and pa<=pc: return a
elif pb<=pc : return b
else : return c
def getIdx(self, x,y):
base = ( self.height * 3 * y ) + (y +1)
idx = base + 3 * x
return idx
def floor(self, x):
if x %1.0 == 0.0:
return int(round(x))
else:
return int(round(x+1))
class CRC:
def __init__(self):
self.crcTable = 0
self.makeCRCTable()
def makeCRCTable(self):
self.crcTable = [0L]*256
for i in range(256):
c = long(i)
for k in range(8):
if ( c & 1 ) :
c = 0xedb88320L ^ (c>>1)
else :
c = c >> 1
self.crcTable[i] = c
def updateCRC(self, buf):
c = 0xFFFFFFFFL
for ch in buf:
#c = crcTable[(c ^ ord(ch) ) & 0xff] ^ (c >> 8)
c = self.crcTable[int ((c^ord(ch))&0xff) ] ^ (c >> 8)
return long(c)
def getCRC(self, buf):
return long(self.updateCRC(buf) ^ 0xFFFFFFFFL )
class RGB:
def __init__(self, r, g, b):
self.r = r
self.g = g
self.b = b
def compare(self, rgb):
return self.r == rgb[0] and self.g == rgb[1] and self.b == rgb[2]
def __eq__(self, rgb):
return self.compare(rgb)
def __str__(self):
return str((self.r, self.g, self.b))