def test_pil_kwargs_png(): Image = pytest.importorskip("PIL.Image") from PIL.PngImagePlugin import PngInfo buf = io.BytesIO() pnginfo = PngInfo() pnginfo.add_text("Software", "test") plt.figure().savefig(buf, format="png", pil_kwargs={"pnginfo": pnginfo}) im = Image.open(buf) assert im.info["Software"] == "test"
def to_file(self, filename, *, palette=False): # assemble comment comment = self._assemble_comment() # assemble spritesheet W, H = self.width, self.height num_frames = sum(len(state.frames) for state in self.states) sqrt = math.ceil(math.sqrt(num_frames)) output = Image.new('RGBA', (sqrt * W, math.ceil(num_frames / sqrt) * H)) i = 0 for state in self.states: for frame in state.frames: output.paste(frame, ((i % sqrt) * W, (i // sqrt) * H)) i += 1 # save pnginfo = PngInfo() pnginfo.add_text('Description', comment, zip=True) if palette: output = output.convert('P') output.save(filename, 'png', optimize=True, pnginfo=pnginfo)
def print_png(self, filename_or_obj, *args, metadata=None, pil_kwargs=None, **kwargs): """ Write the figure to a PNG file. Parameters ---------- filename_or_obj : str or PathLike or file-like object The file to write to. metadata : dict, optional Metadata in the PNG file as key-value pairs of bytes or latin-1 encodable strings. According to the PNG specification, keys must be shorter than 79 chars. The `PNG specification`_ defines some common keywords that may be used as appropriate: - Title: Short (one line) title or caption for image. - Author: Name of image's creator. - Description: Description of image (possibly long). - Copyright: Copyright notice. - Creation Time: Time of original image creation (usually RFC 1123 format). - Software: Software used to create the image. - Disclaimer: Legal disclaimer. - Warning: Warning of nature of content. - Source: Device used to create the image. - Comment: Miscellaneous comment; conversion from other image format. Other keywords may be invented for other purposes. If 'Software' is not given, an autogenerated value for matplotlib will be used. For more details see the `PNG specification`_. .. _PNG specification: \ https://www.w3.org/TR/2003/REC-PNG-20031110/#11keywords pil_kwargs : dict, optional If set to a non-None value, use Pillow to save the figure instead of Matplotlib's builtin PNG support, and pass these keyword arguments to `PIL.Image.save`. If the 'pnginfo' key is present, it completely overrides *metadata*, including the default 'Software' key. """ from matplotlib import _png if metadata is None: metadata = {} metadata = { "Software": f"matplotlib version{__version__}, http://matplotlib.org/", **metadata, } if pil_kwargs is not None: from PIL import Image from PIL.PngImagePlugin import PngInfo buf, size = self.print_to_buffer() # Only use the metadata kwarg if pnginfo is not set, because the # semantics of duplicate keys in pnginfo is unclear. if "pnginfo" not in pil_kwargs: pnginfo = PngInfo() for k, v in metadata.items(): pnginfo.add_text(k, v) pil_kwargs["pnginfo"] = pnginfo pil_kwargs.setdefault("dpi", (self.figure.dpi, self.figure.dpi)) (Image.frombuffer("RGBA", size, buf, "raw", "RGBA", 0, 1) .save(filename_or_obj, format="png", **pil_kwargs)) else: FigureCanvasAgg.draw(self) renderer = self.get_renderer() with cbook._setattr_cm(renderer, dpi=self.figure.dpi), \ cbook.open_file_cm(filename_or_obj, "wb") as fh: _png.write_png(renderer._renderer, fh, self.figure.dpi, metadata=metadata)
def generateComics( self, instance ): image = Image.new( mode="1", size=(0, 0) ) for self.generatedComicNumber in range( self.numberOfComics ): try: if self.commandLineComicID is None: wordBubbleFileName = random.choice( os.listdir( self.wordBubblesDir ) ) else: wordBubbleFileName = os.path.join( self.wordBubblesDir, self.commandLineComicID + ".tsv" ) except IndexError as error: six.print_( error, file=sys.stderr ) exit( EX_NOINPUT ) if not self.silence: six.print_( "wordBubbleFileName:", wordBubbleFileName ) if self.commandLineComicID is None: comicID = os.path.splitext( wordBubbleFileName )[ 0 ] else: comicID = self.commandLineComicID wordBubbleFileName = os.path.join( self.wordBubblesDir, wordBubbleFileName ) if not self.silence: six.print_( "Loading word bubbles from", wordBubbleFileName ) try: wordBubbleFile = open( wordBubbleFileName, mode="rt" ) except OSError as error: six.print_( error, file=sys.stderr ) exit( EX_NOINPUT ) if not idChecker.checkFile( wordBubbleFile, wordBubbleFileName, self.commentMark ): six.print_( "Error: Word bubble file", wordBubbleFileName, "is not in the correct format." ) exit( EX_DATAERR ) lookForSpeakers = True speakers = [] while lookForSpeakers: line = wordBubbleFile.readline() if len( line ) > 0: line = line.partition( self.commentMark )[0].strip() if len( line ) > 0: speakers = line.upper().split( "\t" ) if len( speakers ) > 0: lookForSpeakers = False else: lookForSpeakers = False; #End of file reached, no speakers found if len( speakers ) == 0: six.print_( "Error: Word bubble file", wordBubbleFileName, "contains no speakers." ) exit( EX_DATAERR ) if not self.silence: six.print_( "These characters speak:", speakers ) for speaker in speakers: if speaker not in self.generators: if not self.silence: six.print_( "Now building a Markov graph for character", speaker, "..." ) newGenerator = Generator( charLabel = speaker, cm = self.commentMark, randomizeCapitals = self.randomizeCapitals ) newGenerator.buildGraph( self.inDir ) if not self.silence: newGenerator.showStats() self.generators[ speaker ] = newGenerator if not self.silence: six.print_( comicID ) inImageFileName = os.path.join( self.imageDir, comicID + ".png" ) try: image = Image.open( inImageFileName ).convert() #Text rendering looks better if we ensure the image's mode is not palette-based. Calling convert() with no mode argument does this. except IOError as error: six.print_( error, file=sys.stderr ) exit( EX_NOINPUT ) transcript = str( comicID ) + "\n" previousBox = ( int( -1 ), int( -1 ), int( -1 ), int( -1 ) ) #For detecting when two characters share a speech bubble; don't generate text twice. for line in wordBubbleFile: line = line.partition( self.commentMark )[ 0 ].strip() if len( line ) > 0: line = line.split( "\t" ) character = line[ 0 ].rstrip( ":" ).strip().upper() try: generator = self.generators[ character ] except: six.print_( "Error: Word bubble file", wordBubbleFileName, "does not list", character, "in its list of speakers.", file=sys.stderr ) exit( EX_DATAERR ) topLeftX = int( line[ 1 ] ) topLeftY = int( line[ 2 ] ) bottomRightX = int( line[ 3 ] ) bottomRightY = int( line[ 4 ] ) box = ( topLeftX, topLeftY, bottomRightX, bottomRightY ) if box != previousBox: previousBox = box text = "" nodeList = generator.generateSentences( 1 )[ 0 ] for node in nodeList: text += node.word + " " text.rstrip() oneCharacterTranscript = character + ": " oneCharacterTranscript += self.stringFromNodes( nodeList ) if not self.silence: six.print_( oneCharacterTranscript ) oneCharacterTranscript += "\n" transcript += oneCharacterTranscript wordBubble = image.crop( box ) draw = ImageDraw.Draw( wordBubble ) width = bottomRightX - topLeftX if width <= 0: #Width must be positive width = 1 height = bottomRightY - topLeftY if height <= 0: height = 1 size = int( height * 1.2 ) #Contrary to the claim by PIL's documentation, font sizes are apparently in pixels, not points. The size being requested is the height of a generic character; the actual height of any particular character will be approximately (not exactly) the requested size. We will try smaller and smaller sizes in the while loop below. The 1.2, used to account for the fact that real character sizes aren't exactly the same as the requested size, I just guessed an appropriate value. normalFont = ImageFont.truetype( self.normalFontFile, size = size ) boldFont = ImageFont.truetype( self.boldFontFile, size = size ) listoflists = self.rewrap_nodelistlist( nodeList, normalFont, boldFont, width, fontSize = size ) margin = 0 offset = originalOffset = 0 goodSizeFound = False while not goodSizeFound: goodSizeFound = True totalHeight = 0 for line in listoflists: lineWidth = 0 lineHeight = 0 for node in line: wordSize = normalFont.getsize( node.word + " " ) lineWidth += wordSize[ 0 ] lineHeight = max( lineHeight, wordSize[ 1 ] ) lineWidth -= normalFont.getsize( " " )[ 0 ] totalHeight += lineHeight if lineWidth > width: goodSizeFound = False if totalHeight > height: goodSizeFound = False if not goodSizeFound: size -= 1 try: normalFont = ImageFont.truetype( self.normalFontFile, size = size ) boldFont = ImageFont.truetype( self.boldFontFile, size = size ) except IOError as error: six.print_( error, "\nUsing default font instead.", file=sys.stderr ) normalFont = ImageFont.load_default() boldFont = ImageFont.load_default() listoflists = self.rewrap_nodelistlist( nodeList, normalFont, boldFont, width, fontSize = size ) midX = int( wordBubble.size[ 0 ] / 2 ) midY = int( wordBubble.size[ 1 ] / 2 ) try: #Choose a text color that will be visible against the background backgroundColor = ImageStat.Stat( wordBubble ).mean #wordBubble.getpixel( ( midX, midY ) ) textColorList = [] useIntegers = False useFloats = False if wordBubble.mode.startswith( "1" ): bandMax = 1 useIntegers = True elif wordBubble.mode.startswith( "L" ) or wordBubble.mode.startswith( "P" ) or wordBubble.mode.startswith( "RGB" ) or wordBubble.mode.startswith( "CMYK" ) or wordBubble.mode.startswith( "YCbCr" ) or wordBubble.mode.startswith( "LAB" ) or wordBubble.mode.startswith( "HSV" ): bandMax = 255 useIntegers = True elif wordBubble.mode.startswith( "I" ): bandMax = 2147483647 #max for a 32-bit signed integer useIntegers = True elif wordBubble.mode.startswith( "F" ): bandMax = float( "infinity" ) useFloats = True else: #I've added all modes currently supported according to Pillow documentation; this is for future compatibility bandMax = max( ImageStat.Stat( image ).extrema ) for c in backgroundColor: d = bandMax - ( c * 1.5 ) if d < 0: d = 0 if useIntegers: d = int( d ) elif useFloats: d = float( d ) textColorList.append( d ) if wordBubble.mode.endswith( "A" ): #Pillow supports two modes with alpha channels textColorList[ -1 ] = bandMax textColor = tuple( textColorList ) except ValueError: textColor = "black" offset = originalOffset for line in listoflists: xOffset = 0 yOffsetAdditional = 0 for node in line: usedFont = node.font draw.text( ( margin + xOffset, offset ), node.word + " ", font = usedFont, fill = textColor ) tempSize = usedFont.getsize( node.word + " " ) xOffset += tempSize[ 0 ] yOffsetAdditional = max( yOffsetAdditional, tempSize[ 1 ] ) node.unselectStyle() offset += yOffsetAdditional image.paste( wordBubble, box ) wordBubbleFile.close() if self.numberOfComics > 1: oldOutTextFileName = self.outTextFileName temp = os.path.splitext(self.outTextFileName ) self.outTextFileName = temp[ 0 ] + str( self.generatedComicNumber ) + temp[ 1 ] #---------------------------Split into separate function try: #os.makedirs( os.path.dirname( outTextFileName ), exist_ok = True ) outFile = open( self.outTextFileName, mode="wt" ) except OSError as error: six.print_( error, "\nUsing standard output instead", file=sys.stderr ) outFile = sys.stdout if self.numberOfComics > 1: self.outTextFileName = oldOutTextFileName six.print_( transcript, file=outFile ) outFile.close() if self.numberOfComics > 1: oldOutImageFileName = self.outImageFileName temp = os.path.splitext( self.outImageFileName ) outImageFileName = temp[ 0 ] + str( self.generatedComicNumber ) + temp[ 1 ] if self.topImageFileName != None: try: topImage = Image.open( self.topImageFileName ).convert( mode=image.mode ) except IOError as error: six.print_( error, file=sys.stderr ) exit( EX_NOINPUT ) oldSize = topImage.size size = ( max( topImage.size[ 0 ], image.size[ 0 ] ), topImage.size[ 1 ] + image.size[ 1 ] ) newImage = Image.new( mode=image.mode, size=size ) newImage.paste( im=topImage, box=( 0, 0 ) ) newImage.paste( im=image, box=( 0, oldSize[ 1 ] ) ) image = newImage originalURL = None URLFile = open( os.path.join( self.inDir, "sources.tsv" ), "rt" ) for line in URLFile: line = line.partition( self.commentMark )[ 0 ].strip() if len( line ) > 0: line = line.split( "\t" ) if comicID == line[ 0 ]: originalURL = line[ 1 ] break; URLFile.close() transcriptWithURL = transcript + "\n" + originalURL #The transcript that gets embedded into the image file should include the URL. The transcript that gets uploaded to blogs doesn't need it, as the URL gets sent anyway. infoToSave = PngInfo() encodingErrors = "backslashreplace" #If we encounter errors during text encoding, I feel it best to replace unencodable text with escape sequences; that way it may be possible for reader programs to recover the original unencodable text. #According to the Pillow documentation, key names should be "latin-1 encodable". I take this to mean that we ourselves don't need to encode it in latin-1. key = "transcript" keyUTF8 = key.encode( "utf-8", errors=encodingErrors ) #uncomment the following if using Python 3 #transcriptISO = transcriptWithURL.encode( "iso-8859-1", errors=encodingErrors ) #transcriptUTF8 = transcriptWithURL.encode( "utf-8", errors=encodingErrors ) #python 2: tempencode = transcriptWithURL.decode( 'ascii', errors='replace' ) # I really don't like using this ascii-encoded intermediary called tempencode, but i couldn't get the program to work when encoding directly to latin-1 transcriptISO = tempencode.encode( "iso-8859-1", errors='replace' ) transcriptUTF8 = tempencode.encode( "utf-8", errors='replace' ) infoToSave.add_itxt( key=key, value=transcriptUTF8, tkey=keyUTF8 ) infoToSave.add_text( key=key, value=transcriptISO ) #GIMP only recognizes comments key = "Comment" keyUTF8 = key.encode( "utf-8", errors=encodingErrors ) infoToSave.add_text( key=key, value=transcriptISO ) infoToSave.add_itxt( key=key, value=transcriptUTF8, tkey=keyUTF8 ) try: #os.makedirs( os.path.dirname( outImageFileName ), exist_ok = True ) if self.saveForWeb: image = image.convert( mode = "P", palette="ADAPTIVE", dither=False ) #Try turning dithering on or off. image.save( self.outImageFileName, format="PNG", optimize=True, pnginfo=infoToSave ) else: image.save( self.outImageFileName, format="PNG", pnginfo=infoToSave ) except IOError as error: six.print_( error, file = sys.stderr ) exit( EX_CANTCREAT ) except OSError as error: six.print_( error, file = sys.stderr ) exit( EX_CANTCREAT ) if not self.silence: six.print_( "Original comic URL:", originalURL ) for blog in self.blogUploaders: blog.upload( postStatus = "publish", inputFileName = outImageFileName, shortComicTitle = self.shortName, longComicTitle = self.longName, transcript = transcript, originalURL = originalURL, silence = self.silence ) if self.numberOfComics > 1: outImageFileName = oldOutImageFileName #end of loop: for generatedComicNumber in range( numberOfComics ): #---------------------------It's display time! if image.mode != "RGB": image = image.convert( mode = "RGB" ) self.gui.comicArea.texture = Texture.create( size = image.size, colorfmt = 'rgb' ) self.gui.comicArea.texture.blit_buffer( pbuffer = image.transpose( Image.FLIP_TOP_BOTTOM ).tobytes(), colorfmt = 'rgb' )
import sys from PIL import Image from PIL.PngImagePlugin import PngInfo png = Image.open('original.png') pixels = png.load() new_pixels = [] with open(sys.argv[1], "r") as kg: new_pixels = kg.read() print "setting %d new pixels" % len(new_pixels) k = 0 for i in range(png.size[0]): # for every pixel: if k >= len(new_pixels): break for j in range(png.size[1]): r, g, b = list(pixels[i, j]) b = new_pixels[k] # change the blue pixel ;) pixels[i, j] = r, g, ord(b) k += 1 if k >= len(new_pixels): break info = PngInfo() info.add_text("flag", "not here ...but john loves that song ;)") png.save('Ub7XL8T.png', 'PNG', compress_level=9, pnginfo=info)
def add_to_pnginfo(image_filename="", data_name="", data_dict={}): targetImage = PngImageFile(image_filename) metadata = PngInfo() metadata.add_text(data_name, json.dumps(data_dict)) targetImage.save(image_filename, pnginfo=metadata)
def insert_instrument_settings(filename="", instrument_dict=[]): isimage = False if (filename.endswith('PNG') or filename.endswith('png')): targetImage = PngImageFile(filename) metadata = PngInfo() isimage = True else: outfile = open(filename, 'w') settings_dict = {} rm = pyvisa.ResourceManager() for key in instrument_dict: if (key.startswith("HAMEG,HMP4040")): hmp4040 = hmp4040_power_supply( pyvisa_instr=rm.open_resource(instrument_dict[key])) hmp4040_unique_scpi = hmp4040.get_unique_scpi_list() if (isimage): metadata.add_text("hmp4040_unique_scpi", json.dumps(hmp4040_unique_scpi)) else: settings_dict['hmp4040_unique_scpi'] = hmp4040_unique_scpi if (key.startswith("KEITHLEY INSTRUMENTS INC.,MODEL 2308")): k2308 = keithley_2308( pyvisa_instr=rm.open_resource(instrument_dict[key])) k2308_unique_scpi = k2308.get_unique_scpi_list() if (isimage): metadata.add_text("k2308_unique_scpi", json.dumps(k2803_unique_scpi)) else: settings_dict['k2308_unique_scpi'] = k2308_unique_scpi if (key.startswith("KEITHLEY INSTRUMENTS INC.,MODEL 2460")): k2460 = keithley_2460( pyvisa_instr=rm.open_resource(instrument_dict[key])) k2460_unique_scpi = k2460.get_unique_scpi_list() if (isimage): metadata.add_text("k2460_unique_scpi", json.dumps(k2460_unique_scpi)) else: settings_dict['k2460_unique_scpi'] = k2460_unique_scpi if (key.startswith("TEKTRONIX,AFG3102")): tek_afg3000 = tektronics_afg3000( pyvisa_instr=rm.open_resource(instrument_dict[key])) tek_afg3000_unique_scpi = tek_afg3000.get_unique_scpi_list() if (isimage): metadata.add_text("tek_afg3000_unique_scpi", json.dumps(tek_afg3000_unique_scpi)) else: settings_dict[ 'tek_afg3000_unique_scpi'] = tek_afg3000_unique_scpi if (key.startswith("KIKUSUI,PLZ164WA,")): plz4w = kikusui_plz4w( pyvisa_instr=rm.open_resource(instrument_dict[key])) plz4w_unique_scpi = plz4w.get_unique_scpi_list() if (isimage): metadata.add_text("plzw4_unique_scpi", json.dumps(plz4w_unique_scpi)) else: settings_dict['plz4w_unique_scpi'] = plz4w_unique_scpi if (key.startswith("Agilent Technologies,33250A")): key_33250a = keysight_33250a( pyvisa_instr=rm.open_resource(instrument_dict[key])) key_33250a_unique_scpi = key_33250a.get_unique_scpi_list() if (isimage): metadata.add_text("key_33250a_unique_scpi", json.dumps(key_33250a_unique_scpi)) else: settings_dict[ 'key_33250a_unique_scpi'] = key_33250a_unique_scpi targetImage.save(filename, pnginfo=metadata) if (not isimage): json.dump(settings_dict, outfile)
def print_png(self, filename_or_obj, *args, metadata=None, pil_kwargs=None, **kwargs): """ Write the figure to a PNG file. Parameters ---------- filename_or_obj : str or PathLike or file-like object The file to write to. metadata : dict, optional Metadata in the PNG file as key-value pairs of bytes or latin-1 encodable strings. According to the PNG specification, keys must be shorter than 79 chars. The `PNG specification`_ defines some common keywords that may be used as appropriate: - Title: Short (one line) title or caption for image. - Author: Name of image's creator. - Description: Description of image (possibly long). - Copyright: Copyright notice. - Creation Time: Time of original image creation (usually RFC 1123 format). - Software: Software used to create the image. - Disclaimer: Legal disclaimer. - Warning: Warning of nature of content. - Source: Device used to create the image. - Comment: Miscellaneous comment; conversion from other image format. Other keywords may be invented for other purposes. If 'Software' is not given, an autogenerated value for matplotlib will be used. For more details see the `PNG specification`_. .. _PNG specification: \ https://www.w3.org/TR/2003/REC-PNG-20031110/#11keywords pil_kwargs : dict, optional Keyword arguments passed to `PIL.Image.Image.save`. If the 'pnginfo' key is present, it completely overrides *metadata*, including the default 'Software' key. """ if metadata is None: metadata = {} if pil_kwargs is None: pil_kwargs = {} metadata = { "Software": f"matplotlib version{mpl.__version__}, http://matplotlib.org/", **metadata, } FigureCanvasAgg.draw(self) # Only use the metadata kwarg if pnginfo is not set, because the # semantics of duplicate keys in pnginfo is unclear. if "pnginfo" not in pil_kwargs: pnginfo = PngInfo() for k, v in metadata.items(): pnginfo.add_text(k, v) pil_kwargs["pnginfo"] = pnginfo pil_kwargs.setdefault("dpi", (self.figure.dpi, self.figure.dpi)) (Image.fromarray(np.asarray(self.buffer_rgba())).save(filename_or_obj, format="png", **pil_kwargs))
def main(self): message_broker_host = local.env.get("MESSAGE_BROKER_HOST") message_broker_exchange_name = local.env.get( "MESSAGE_BROKER_EXCHANGE_NAME", "observed_cam_events") rtsp_endpoint = local.env.get("RTSP_ENDPOINT") yolo_host = local.env.get("YOLO_HOST") yolo_port = local.env.get("YOLO_PORT", 8080) # If specified, will save a png of the capture for any detection, # with the detections included, json encoded as png metadata. image_save_path = local.env.get("IMAGE_SAVE_PATH") if image_save_path: image_save_path = local.path(image_save_path) image_save_path.mkdir() threshold = local.env.get("THRESHOLD", "0.25") # Include image specifies whether to include a cropped image in the message for a detection include_image = local.env.get("INCLUDE_IMAGE") if include_image in FALSEY_VALUES: include_image = False yolo_endpoint = f"http://{yolo_host}:{yolo_port}/detect" with broker.MessageBrokerConnection(message_broker_host) as con: con.channel.exchange_declare(exchange=message_broker_exchange_name, exchange_type="fanout") cam = Camera(rtsp_endpoint) try: logging.info("Capturing frames") while True: frame = cam.getFrame() dt = datetime.datetime.now(datetime.timezone.utc) if frame is None: # No new frame, sleeping time.sleep(0.1) continue with cam.lock: enc_success, encoded = cv2.imencode(".png", frame) if not enc_success: logging.error("Failed to encode frame") continue response = requests.post( yolo_endpoint, files={"image_file": encoded}, data={"threshold": threshold}, ) try: decoded_response = json.loads(response.text) except Exception as e: logging.exception( f"Failed to parse response response {response.text}" ) continue formatted_detections_no_image = [] for detection in decoded_response: logging.info(f"got detection: {detection}") body = { "stream": rtsp_endpoint, "time": dt.isoformat(), "detection": detection, } formatted_detections_no_image.append(body.copy()) if include_image: xcenter, ycenter, width, height = detection[2] w2 = width / 2 h2 = height / 2 subframe = frame[max(int(ycenter - h2), 0):int(ycenter + h2), max(int(xcenter - w2), 0):int(xcenter + w2), ] enc_success, subframe_encoded = cv2.imencode( ".png", subframe) body["image"] = base64.b64encode( subframe_encoded).decode("ascii") con.channel.basic_publish( exchange=message_broker_exchange_name, routing_key="", body=json.dumps(body), ) # Save image to disk with detection metadata if asked for and we had detections if image_save_path and formatted_detections_no_image: pif = PngImageFile(io.BytesIO(encoded)) metadata = PngInfo() metadata.add_text( "detections", json.dumps(formatted_detections_no_image)) target_folder_name = make_safe_for_filename( rtsp_endpoint) folder = (image_save_path / target_folder_name / str(dt.year) / str(dt.month) / str(dt.day)) folder.mkdir() pif.save( folder / f"{dt.hour}-{dt.minute}-{dt.second}-{dt.microsecond}.png", pnginfo=metadata, ) time.sleep(0.5) finally: cam.close()
def print_png(self, filename_or_obj, *args, metadata=None, pil_kwargs=None, **kwargs): """ Write the figure to a PNG file. Parameters ---------- filename_or_obj : str or PathLike or file-like object The file to write to. metadata : dict, optional Metadata in the PNG file as key-value pairs of bytes or latin-1 encodable strings. According to the PNG specification, keys must be shorter than 79 chars. The `PNG specification`_ defines some common keywords that may be used as appropriate: - Title: Short (one line) title or caption for image. - Author: Name of image's creator. - Description: Description of image (possibly long). - Copyright: Copyright notice. - Creation Time: Time of original image creation (usually RFC 1123 format). - Software: Software used to create the image. - Disclaimer: Legal disclaimer. - Warning: Warning of nature of content. - Source: Device used to create the image. - Comment: Miscellaneous comment; conversion from other image format. Other keywords may be invented for other purposes. If 'Software' is not given, an autogenerated value for matplotlib will be used. For more details see the `PNG specification`_. .. _PNG specification: \ https://www.w3.org/TR/2003/REC-PNG-20031110/#11keywords pil_kwargs : dict, optional If set to a non-None value, use Pillow to save the figure instead of Matplotlib's builtin PNG support, and pass these keyword arguments to `PIL.Image.save`. If the 'pnginfo' key is present, it completely overrides *metadata*, including the default 'Software' key. """ if metadata is None: metadata = {} metadata = { "Software": f"matplotlib version{__version__}, http://matplotlib.org/", **metadata, } if pil_kwargs is not None: from PIL import Image from PIL.PngImagePlugin import PngInfo buf, size = self.print_to_buffer() # Only use the metadata kwarg if pnginfo is not set, because the # semantics of duplicate keys in pnginfo is unclear. if "pnginfo" not in pil_kwargs: pnginfo = PngInfo() for k, v in metadata.items(): pnginfo.add_text(k, v) pil_kwargs["pnginfo"] = pnginfo pil_kwargs.setdefault("dpi", (self.figure.dpi, self.figure.dpi)) (Image.frombuffer("RGBA", size, buf, "raw", "RGBA", 0, 1).save(filename_or_obj, format="png", **pil_kwargs)) else: FigureCanvasAgg.draw(self) renderer = self.get_renderer() with cbook._setattr_cm(renderer, dpi=self.figure.dpi), \ cbook.open_file_cm(filename_or_obj, "wb") as fh: _png.write_png(renderer._renderer, fh, self.figure.dpi, metadata=metadata)
# buffered = io.BytesIO() # targetImage = PngImageFile("embed-test.png") # metadata = PngInfo() # metadata.add_text("MyNewString", "A string") # metadata.add_text("MyNewInt", str(1234)) # targetImage.save(, pnginfo=metadata) # imgdata = pybase64.b64decode(buffered.getvalue()) # hostImage = PngImageFile(imgdata) # # targetImage = PngImageFile("NewPath.png") # print(hostImage.text) from PIL.PngImagePlugin import PngImageFile, PngInfo import json targetImage = PngImageFile("embed-test.png") metadata = PngInfo() metadata.add_text("MyNewString", "A string") metadata.add_text("MyNewInt", str(1234)) targetImage.save("NewPath.png", pnginfo=metadata) targetImage = PngImageFile("NewPath.png") targetImageSize = targetImage.size print('targetImage', targetImage, 'targetImageSize', targetImageSize) a = {'name': 'wang', 'age': 29} print('nameExist', 'name' in a) b = json.dumps(a) print('b', b)
def load_skin(uuid, conf): file = os.path.join(conf["output_dir"], "static", "markers", uuid + ".png") if not os.path.isfile(file) or time.time() - os.path.getmtime(file) >= 86400: r = requests.get('https://sessionserver.mojang.com/session/minecraft/profile/' + uuid) if r.status_code == requests.codes.ok: data = r.json() data = data["properties"][0]["value"] data = json5.loads(base64.b64decode(data)) if "SKIN" in data["textures"]: # do not download and create the icon again if the skin stayed the same if os.path.isfile(file): old_img = Image.open(file) if old_img.info.get("SKIN", "") == data["textures"]["SKIN"]["url"]: return None ri = requests.get(data["textures"]["SKIN"]["url"]) if ri.status_code == requests.codes.ok: img = Image.open(BytesIO(ri.content)) width, height = img.size skin = Image.new('RGBA', (16, 32), (0, 0, 0, 0)) big = 0 if "metadata" in data["textures"]["SKIN"] else 1 # inverted to paste the arm with spacing small = 1 if "metadata" in data["textures"]["SKIN"] else 0 # New Texture Format if height == 64: head = img.crop((8, 8, 16, 16)) head_o = img.crop((40, 8, 48, 16)) legr = img.crop((4, 20, 8, 32)) legr_o = img.crop((4, 36, 8, 48)) body = img.crop((20, 20, 28, 32)) body_o = img.crop((20, 36, 28, 48)) armr = img.crop((44, 20, 47+big, 32)) armr_o = img.crop((44, 36, 47+big, 48)) legl = img.crop((20, 52, 24, 64)) legl_o = img.crop((4, 52, 8, 64)) arml = img.crop((36, 52, 39+big, 64)) arml_o = img.crop((52, 52, 55+big, 64)) skin.paste(head, (4, 0, 12, 8)) skin.paste(head_o, (4, 0, 12, 8), head_o) skin.paste(legr, (4, 20, 8, 32)) skin.paste(legr_o, (4, 20, 8, 32), legr_o) skin.paste(body, (4, 8, 12, 20)) skin.paste(body_o, (4, 8, 12, 20), body_o) skin.paste(armr, (0+small, 8, 4, 20)) skin.paste(armr_o, (0+small, 8, 4, 20), armr_o) skin.paste(legl, (8, 20, 12, 32)) skin.paste(legl_o, (8, 20, 12, 32), legl_o) skin.paste(arml, (12, 8, 15+big, 20)) skin.paste(arml_o, (12, 8, 15+big, 20), arml_o) elif height == 32: head = img.crop((8, 8, 16, 16)) head_o = img.crop((40, 8, 48, 16)) body = img.crop((20, 20, 28, 32)) legr = img.crop((4, 20, 8, 32)) armr = img.crop((44, 20, 48, 32)) legl = ImageOps.mirror(legr) arml = ImageOps.mirror(armr) skin.paste(head, (4, 0, 12, 8)) skin.paste(head_o, (4, 0, 12, 8), head_o) skin.paste(legr, (4, 20, 8, 32)) skin.paste(body, (4, 8, 12, 20)) skin.paste(armr, (0, 8, 4, 20)) skin.paste(legl, (8, 20, 12, 32)) skin.paste(arml, (12, 8, 16, 20)) metadata = PngInfo() metadata.add_text("SKIN", data["textures"]["SKIN"]["url"]) skin.save(file, pnginfo=metadata) return None if os.path.isfile(file): return None if is_alex(uuid): copyfile(os.path.join(sys.path[0], "alex.png"), file) else: copyfile(os.path.join(sys.path[0], "steve.png"), file) return None return None