def tag_sof(self, tag): # 0xFFC1~0xFFC7 0xFFC9~0xFFCF Start Of Frame length = self.read_uint16() self.encodeType = 'sofx' self.bitsPerPixel = self.fileObject.read_uint8() self.height = self.read_uint16() self.width = self.read_uint16() if self.fileObject.read(1) != '\x03': LOGGER.error('[0x%x] Color type must be YCrCb(0x03) in JFIF.' % self.fileObject.cur()) comp = self.fileObject.read(9) return self.find_tag('SOFx')
def find_tag(self, tagName): if self.fileObject.read(1) != '\xFF': curPos = '[0x%x]' % self.fileObject.cur() LOGGER.error('%s Can\'t find 0xFF in end of %s.' % (curPos, tagName)) data = [] d = self.fileObject.read(1) while d != '\xFF': data.append(d) d = self.fileObject.read(1) LOGGER.log(CustomLoggingLevel.EXTRA_DATA, '%s> %s' % (curPos, ''.join(data))) return '\xff' + self.fileObject.read(1)
def start(self): if self.fileObject.read(2) == '\xff\xd8': # start of JPEG file tag = self.fileObject.read(2) while self.scanFlag == False and tag != None: try: tag = self.tagMap[tag](tag) except KeyError: tag = self.tag_unknown(tag) LOGGER.log( CustomLoggingLevel.IMAGE_INFO, 'JPEG (ver %d.%d): %d*%dpx , channel: %d, fileLength: 0x%x b.' % (self.version >> 8, self.version & 0xff, self.width, self.height, self.channel, self.fileObject.size)) else: LOGGER.error('JPEG file start mark 0xFFD8 check failed.')
def tag_sos(self, tag): # 0xFFDA Start Of Scan self.scanFlag = True length = self.read_uint16() - 2 if self.fileObject.read(1) != '\x03': LOGGER.error('[0x%x] Color type must be YCrCb(0x03) in JFIF.' % self.fileObject.cur()) comp = self.fileObject.read(3) for i in range(3): self.scanQuantization[i] = { 'DC': ord(comp[i]) >> 4, 'AC': ord(comp[i]) & 0xf } self.scanSs = self.fileObject.read(1) self.scanSe = self.fileObject.read(1) self.scanAh = ord(self.fileObject.read(1)) self.scanAl = self.scanAh & 0xf self.scanAh = self.scanAh >> 4 self.fileObject.read(3)
def tag_sof0(self, tag): # 0xFFC0 Start Of Frame length = self.read_uint16() self.encodeType = 'sof0' self.dctTransform = self.fileObject.read_uint8() self.bitsPerPixel = 8 self.height = self.read_uint16() self.width = self.read_uint16() if self.fileObject.read(1) != '\x03': LOGGER.error('[0x%x] Color type must be YCrCb(0x03) in JFIF.' % self.fileObject.cur()) comp = self.fileObject.read(9) for i in range(3): self.colorQuantization[ord(comp[3 * i])] = { 'Horz': ord(comp[3 * i + 1]) >> 4, 'Vert': ord(comp[3 * i + 1]) & 0xf, 'TableID': ord(comp[3 * i + 2]) } return self.find_tag('SOF0')
def decode_scandata(self): # init decode varible horzY = self.colorQuantization[1]['Horz'] horzCr = self.colorQuantization[2]['Horz'] horzCb = self.colorQuantization[3]['Horz'] vertY = self.colorQuantization[1]['Vert'] vertCr = self.colorQuantization[2]['Vert'] vertCb = self.colorQuantization[3]['Vert'] scanY = horzY * vertY scanCr = horzCr * vertCr scanCb = horzCb * vertCb LOGGER.log( CustomLoggingLevel.IMAGE_DEBUG, "scanY: %d, scanCr: %d, scanCb: %d." % (scanY, scanCr, scanCb)) self.baseY = 0 self.baseCr = 0 self.baseCb = 0 rowData = [[0, 0, 0] for i in range(self.width * self.height)] if vertCb != 1 or vertCr != 1 or horzCb != 1 or horzCr != 1: LOGGER.error( 'Error in decode scan data, ONLY support vertCb==vertCr==horzCb==horzCr==1!' ) else: LOGGER.log(CustomLoggingLevel.IMAGE_DEBUG, 'Start to decode scan data.') # calc block number hBlock = self.width / (horzY * 8) if self.width % (horzY * 8) != 0: hBlock += 1 vBlock = self.height / (vertY * 8) if self.height % (vertY * 8) != 0: vBlock += 1 widthIndex = 0 heightIndex = 0 unsualDataFlagRight = False unsualDataFlagBotton = False for vb in range(vBlock): for hb in range(hBlock): dataY = [] dataCr = [] dataCb = [] [dataY, dataCr, dataCb] = self.read_mcu_block(scanY, scanCr, scanCb, dataY, dataCr, dataCb) for i in range(vertY): heightIndex = (vb * vertY + i) * 8 for j in range(horzY): widthIndex = (hb * horzY + j) * 8 for k in range(8): if heightIndex + k < self.height: for l in range(8): if widthIndex + l < self.width: y = dataY[i * horzY + j][k * 8 + l] cr = dataCr[0][k * 8 + l] cb = dataCb[0][k * 8 + l] r = self.round(y + 1.402 * cr + 128) b = self.round(y + 1.772 * cb + 128) g = self.round(y - 0.34414 * cb - 0.71414 * cr + 128) rowData[(heightIndex + k) * self.width + widthIndex + l] = [r, g, b] LOGGER.info('Please wait, decoding ... (%d/%d)' % (vb, vBlock)) self.clean_bitstream_remainder() if self.scanDataIndex < self.scanDataLength: self.showextradata(''.join(self.scanData[self.scanDataIndex:]), self.scanDataPos + self.scanDataIndex) return rowData
def __init__(self, file_object): # file_object.addHandler(logging.StreamHandler()) self.fileObject = file_object if file_object.read(3) != 'GIF': LOGGER.error("File is not a gif file") self.type = "GIF" self.version = file_object.read(3) if self.version != '87a' and self.version != '89a': LOGGER.log(CustomLoggingLevel.OTHER_DATA, "Invalid version") else: LOGGER.log(CustomLoggingLevel.BASIC_DEBUG, "version is " + self.version) self.logicScreenWidth = file_object.read_uint16() self.logicScreenHeight = file_object.read_uint16() mask = file_object.read_uint8() self.pixel = mask & 0b111 mask >>= 3 self.sortFlag = mask & 0b1 mask >>= 1 self.colorResolution = mask & 0b111 mask >>= 3 self.globalColorTableFlag = mask & 0b1 if self.version == "89a": self.backgroundColorIndex = file_object.read_uint8() self.pixelAspectRatio = file_object.read_uint8() # self.globalColorTable = [[0, 0, 0]] * (2 ** (self.pixel + 1)) if self.globalColorTableFlag: self.globalColorTable = [[0, 0, 0] for _ in range(2**(self.pixel + 1))] else: self.globalColorTable = [] LOGGER.log(CustomLoggingLevel.OTHER_DATA, "global table size is %d" % len(self.globalColorTable)) for i in range(len(self.globalColorTable)): for j in range(3): # 0 red 1 green 2 blue self.globalColorTable[i][j] = file_object.read_uint8() self.images = [] image = {} while True: tag = file_object.read_uint8() if tag == 0x3b: LOGGER.log(CustomLoggingLevel.OTHER_DATA, "gif end") break # end of gif if tag == 0x2c: # start of a image descriptor # LOGGER.info("image descriptor") LOGGER.log(CustomLoggingLevel.IMAGE_DEBUG, "image descriptor") image["xOffset"] = file_object.read_uint16() image["yOffset"] = file_object.read_uint16() image["width"] = file_object.read_uint16() image["height"] = file_object.read_uint16() if image["xOffset"] + image["width"] > self.logicScreenWidth or \ image["yOffset"] + image["height"] > self.logicScreenHeight: LOGGER.log( CustomLoggingLevel.OTHER_DATA, "some part out of logic screen at image %d" % len(self.images) + 1) mask = file_object.read_uint8() image["pixel"] = mask & 0b111 mask >>= 3 image["reserved"] = mask & 0b11 if image["reserved"] != 0: LOGGER.log( CustomLoggingLevel.OTHER_DATA, "[0x%x] reserved data should be 0" % self.fileObject.cur()) mask >>= 2 image["sortFlag"] = mask & 0b1 mask >>= 1 image["interlaceFlag"] = mask & 0b1 mask >>= 1 image["localColorTableFlag"] = mask & 0b1 if image["localColorTableFlag"]: image["localColorTable"] = [ [0, 0, 0] for _ in xrange((2**(image["pixel"] + 1))) ] for i in range(len(image["localColorTable"])): for j in range(3): # 0 red 1 green 2 blue image["localColorTable"][i][ j] = file_object.read_uint8() elif tag == 0x21: if self.version != "89a": LOGGER.log(CustomLoggingLevel.OTHER_DATA, "not version 89a but has extension segment.") sub_tag = file_object.read_uint8() if sub_tag == 0xF9: # Graphic Control Extension. LOGGER.log(CustomLoggingLevel.IMAGE_DEBUG, "Graphic Control Extension") block_size = file_object.read_uint8() if block_size != 4: LOGGER.log( CustomLoggingLevel.OTHER_DATA, "block size is not 4 in Graphic Control Extension") control = {} mask = file_object.read_uint8() control["transparentFlag"] = mask & 0b1 mask >>= 1 control["userInputFlag"] = mask & 0b1 mask >>= 1 control["disposalMethod"] = mask & 0b111 # 0 - No disposal specified. The decoder is # not required to take any action. # 1 - Do not dispose. The graphic is to be left # in place. # 2 - Restore to background color. The area used by the # graphic must be restored to the background color. # 3 - Restore to previous. The decoder is required to # restore the area overwritten by the graphic with # what was there prior to rendering the graphic. # 4-7 - To be defined. control["delayTime"] = file_object.read_uint16() control["TransparentColonrIndex"] = file_object.read_uint8( ) terminator = file_object.read_uint8() if terminator != 0: LOGGER.log( CustomLoggingLevel.OTHER_DATA, "[0x%x] terminator in block Graphic Control Extension is not 0" % self.fileObject.cur()) image["control"] = control elif sub_tag == 0xFE: # Comment Extension. LOGGER.log(CustomLoggingLevel.IMAGE_DEBUG, "Comment Extension.") if "comment" not in image: image["comment"] = "" while True: tmp = file_object.read(1) if tmp == '\0': break image["comment"] += tmp LOGGER.log(CustomLoggingLevel.ASCII_DATA, image["comment"]) elif sub_tag == 0x01: # plain text Extension LOGGER.log(CustomLoggingLevel.IMAGE_DEBUG, "plain text Extension") block_size = file_object.read_uint8() if block_size != 12: LOGGER.warning("block size is not 12 in plain text") text = { "gridLeftPosition": file_object.read_uint16(), "gridTopPosition": file_object.read_uint16(), "textGridWidth": file_object.read_uint16(), "textGridHeight": file_object.read_uint16(), "characterCellWidth": file_object.read_uint8(), "characterCellHeight": file_object.read_uint8(), "textForegroundColorIndex": file_object.read_uint8(), "textBackgroundColorIndex": file_object.read_uint8(), "data": "" } while True: tmp = file_object.read(1) if tmp == '\0': break text["data"] += tmp if "text" in image: LOGGER.log(CustomLoggingLevel.OTHER_DATA, "text already in image") image["text"] = text LOGGER.log(CustomLoggingLevel.ASCII_DATA, image["text"]) elif sub_tag == 0xFF: # Application Extension. LOGGER.log(CustomLoggingLevel.IMAGE_DEBUG, "Application Extension.") block_size = file_object.read_uint8() if block_size != 11: LOGGER.log( CustomLoggingLevel.OTHER_DATA, "[0x%x] block size is not 11 in application extension" % self.fileObject.cur()) application = { "identifier": file_object.read(8), "authenticationCode": file_object.read(3) } data_size = file_object.read_uint8() application["data"] = file_object.read(data_size) if "application" in image: LOGGER.log(CustomLoggingLevel.OTHER_DATA, "application Extension already in image") image["application"] = application terminator = file_object.read_uint8() if terminator != 0: LOGGER.log( CustomLoggingLevel.OTHER_DATA, "terminator is not 0 in Application Extension") else: LOGGER.log( CustomLoggingLevel.IMAGE_DEBUG, "[0x%x] unknown extension at" % self.fileObject.cur()) else: # DATA # LOGGER.info("DATA") LOGGER.log(CustomLoggingLevel.IMAGE_DEBUG, "DATA") image["LZWMinimumCodeSize"] = tag image["data"] = [] while True: data_size = file_object.read_uint8() if data_size == 0: break data = file_object.read(data_size) image["data"] += data self.images.append(image) image = {}
def start(self): self.version = struct.unpack('h', self.fileObject.read(2))[0] if self.version == 0: # ver 1 # read file header self.version = 1 self.width, self.height, self.rowDataLength = struct.unpack( '3h', self.fileObject.read(6)) self.channel = ord(struct.unpack('b', self.fileObject.read(1))[0]) self.bitsPerPixel = ord( struct.unpack('b', self.fileObject.read(1))[0]) self.headerLength = 10 elif self.version == 0x4D42: # ver 2 3 4 # read file header self.length = struct.unpack('i', self.fileObject.read(4))[0] if self.fileObject.read( 4) != '\x00\x00\x00\x00': # reserved always 0 LOGGER.log( CustomLoggingLevel.OTHER_DATA, '[0x%x] File header reserved options is not 0' % (self.fileObject.cur() - 4)) bitmapOffset = struct.unpack('i', self.fileObject.read(4))[0] # read bitmap header bitmapHeaderLength = struct.unpack('l', self.fileObject.read(4))[0] if bitmapHeaderLength == 12: # ver 2 self.version = 2 self.width, self.height, self.channel, self.bitsPerPixel = struct.unpack( '4h', self.fileObject.read(8)) self.headerLength = 26 elif bitmapHeaderLength == 40: # ver 3 self.version = 3 self.width, self.height, self.channel, self.bitsPerPixel = struct.unpack( '2l2h', self.fileObject.read(12)) self.compressionMethod, self.bitmapLength = struct.unpack( '2L', self.fileObject.read(8)) self.fileObject.read(16) # skip useless header self.headerLength = 53 elif bitmapHeaderLength == 108: # ver 4 self.version = 4 else: LOGGER.error('[0x%x] Unknown BMP file version.' % (self.fileObject.cur() - 4)) if self.version != 0x4D42: # calculate number of entries if self.bitsPerPixel < 24: self.numberOfEntries = 1 << self.bitsPerPixel else: self.numberOfEntries = 0 # read color palette self.colorPalette = [] if self.version == 3 and self.compressionMethod == 3: self.fileObject.read(12 * self.numberOfEntries) else: for i in range(self.numberOfEntries): self.headerLength += 4 t = self.fileObject.read(4) self.colorPalette.append(t[2] + t[1] + t[0] + t[3]) if t[3] != '\x00': LOGGER.log( CustomLoggingLevel.OTHER_DATA, '[0x%x] Color palette reserved option(alpha channel) is not 0, is 0x%x!' % (self.fileObject.cur() - 4, ord(t[3]))) else: LOGGER.error('Magic value BM check failed.') self.padding = self.width * self.bitsPerPixel % 32 if self.padding != 0: self.padding = 32 - self.padding self.rowDataLength = (self.width * self.bitsPerPixel + self.padding) * self.height / 8 LOGGER.log( CustomLoggingLevel.IMAGE_INFO, 'BMP(ver %d): %d*%dpx , channel: %d, fileLength: 0x%x(0x%x) b, headerLength: %d b, rowDataLength: %d b' % (self.version, self.width, self.height, self.channel, self.fileObject.size, self.headerLength + self.rowDataLength, self.headerLength, self.rowDataLength)) if self.channel != 1: LOGGER.log(CustomLoggingLevel.IMAGE_INFO, 'Warning: bmpfile channel is NOT 1!')
def rowdata_ver23(self): rowData = [] if self.compressionMethod != 0: # decompress bitmap data according to compression method if self.bitmapLength == 0: LOGGER.warning( 'BitmapLength shouldn\'t be 0 in bitmap header! There may have some extra data in end of the file.' ) tdata = self.fileObject.read(self.fileObject.size - self.headerLength) else: tdata = self.fileObject.read(self.bitmapLength) # decompress data = [] if self.compressionMethod == 1: specialFlag = -1 for i in range(len(tdata)): if specialFlag < 0: if specialFlag == -1: if tdata[i] == '\x00': pass # end of line elif tdata == '\x01': break # end of RLE data elif tdata[i] == '\x02': data.append('\x00' * (ord(tdata[i + 1]) + self.width * ord(tdata[i + 2])) * self.bitsPerPixel / 8) else: specialFlag = ord(tdata[i]) + 1 specialFlag -= 1 if specialFlag == -3: specialFlag = 0 elif specialFlag == 0: if tdata[i] == '\x00': specialFlag = -1 elif specialFlag > 1: data.append(tdata[i]) specialFlag -= 1 else: specialFlag -= 1 if i < len(tdata) - 1: self.showextradata(tdata[i:len(tdata) - 1], self.headerLength + i) data = ''.join(data) elif self.compressionMethod == 2: LOGGER.error( 'Compress method RLE4 of BMP file version 3 is not surported.' ) return elif self.compressionMethod == 3: LOGGER.error( 'Compress method using RGB mask of BMP file version 3 is not surported.' ) return else: data = self.fileObject.read(self.rowDataLength) if self.compressionMethod == 0 and self.bitmapLength != 0: LOGGER.warning( 'BitmapLength should be 0 in bitmap header! Image pixel may be processed with wrong compress method!' ) if self.bitsPerPixel in [1, 4, 8, 24, 32]: # return row data from stream if self.bitsPerPixel == 24: self.channel = 3 else: self.channel = 4 return self.decode_rgb_data(data) else: LOGGER.error( 'BMP file bits per pixel is not in (1, 4, 8, 24, 32).')
def rowdata_ver4(self): LOGGER.error('BMP file version 4 is not surported.')
def rowdata_ver1(self): if self.fileObject.size - self.rowDataLength - 10 > 10 * ( 1 - detectSensitive): LOGGER.log(CustomLoggingLevel.EXTRA_DATA, 'Some extra data may in end of the file.') LOGGER.error('BMP file version 1 is not surported.')