def encrypt_png(self, in_path: PathType, out_path: PathType) -> None: image = PngImageFile(in_path) # convert pixels to bytes in order to encrypt them im_bytes = bytearray(self.__get_pixels(image)) # get random IV and calculate MAC iv = get_random_bytes(CryptoAES.block_size) h = HMAC.new(self.__key, digestmod=SHA256) h.update(im_bytes) # create metadata object in order to save IV and MAC to image metadata = PngInfo() metadata.add_text('iv', iv.hex()) metadata.add_text('mac', h.hexdigest()) print(f'writing IV = {iv.hex()} and MAC = {h.hexdigest()} to image metadata') # encrypt image cipher = CryptoAES.new(self.__key, CryptoAES.MODE_ECB) enc_data = cipher.encrypt(im_bytes) # write image to file with metadata image.frombytes(enc_data) image.save(out_path, pnginfo=metadata)
def add_tags_to_png_file(fpath): try: info = create_file_info(fpath) png_image = PngImageFile(open(fpath, 'rb')) png_info = PngInfo() for k, v in info.items(): png_info.add_text(k, v) png_image.save(fpath, pnginfo=png_info) except (Exception, OSError): print("WARNING: Could not add debug info to file '{}'.".format(fpath)) traceback.print_exc()
def savemeta(*args, **kwargs): path = args[1] if path.endswith(".fig"): import pickle as pkl pkl.dump(args[0], open(path, 'wb')) else: mpl_savefig(*args, **kwargs) #fig = args[0] if path.endswith(".png"): targetImage = PngImageFile(path) metadata = PngInfo() metadata.add_text("Description", str(meta)) targetImage.save(path, pnginfo=metadata)
def write_png_metadata(filename, settings): targetImage = PngImageFile(filename) metadata = PngInfo() for (k, v) in settings.items(): if type(v) == list: value = "" for item in v: value += str(item) + " " v = value if type(v) == bool: v = str(v) if v is None: continue else: metadata.add_text(k, str(v)) targetImage.save(filename, pnginfo=metadata)
def sample_image_path(self): """Copy the sample image to to a unique file name and return the path. Returns: tuple of (the filename, the new sample image path) """ filename = self.generate_alphanumeric() + ".png" new_file = self.tmp_file_path(filename) shutil.copy(self.original_sample_image_path, new_file) # make the image content unique image_file = PngImageFile(open(new_file, "r")) info = PngInfo() info.add_text('Comment', self.generate_alphanumeric(length=30)) image_file.save(new_file, pnginfo=info) self.temp_files.append(new_file) return (filename, new_file)
def decrypt_png(self, in_path: PathType, out_path: PathType) -> None: image = PngImageFile(in_path) iv: Optional[str] = None mac: Optional[str] = None # try to get IV from metadata try: iv = image.text['iv'] print(f'found IV = {iv}') except KeyError: print('IV was not found in file') # try to get MAC from metadata try: mac = image.text['mac'] print(f'found MAC = {mac}') except KeyError: print('MAC was not found in file') # convert pixels to bytes in order to decrypt them im_bytes = bytearray(self.__get_pixels(image)) # decrypt image cipher = CryptoAES.new(self.__key, CryptoAES.MODE_ECB) dec: bytes = cipher.decrypt(im_bytes) # try to verify MAC try: self.__hmac.update(dec) self.__hmac.verify(bytes.fromhex(mac)) print('MAC is valid') except ValueError: print('MAC is invalid') # don't forget about metadata metadata = PngInfo() metadata.add_text('iv', iv) metadata.add_text('mac', mac) # save decrypted image to file image.frombytes(dec) image.save(out_path, pnginfo=metadata)
def replace_meta(filename, fields): metaname = get_dumpfile(filename) with open(metaname) as json_file: meta = json.load(json_file) if fields and fields[0] != '*': print(f"overwriting metadata[{fields}] in {filename} from {metaname}") newmeta = {} for f in fields: newmeta[f] = meta[f] else: print(f"overwriting metadata in {filename} from {metaname}") newmeta = meta newmeta['Metadata Modification Time'] = f"{datetime.now()}" img = PngImageFile(filename) metadata = PngInfo() for f in newmeta: metadata.add_text(f, newmeta[f]) img.save(filename, pnginfo=metadata)
def on_save_clicked(self, button): current_path = self.entry.get_text() # write metadata metadata = PngInfo() metadata.add_text("screenshat", self.entryy.get_text()) img = PngImageFile(img_path) img.save(img_path, pnginfo=metadata) # Update config file pathlib.Path(config_path).write_text(current_path) # in doubt do mkdir -p new directory pathlib.Path(current_path).mkdir(parents=True, exist_ok=True) # move file shutil.move(img_path, current_path) self.destroy()
def optimize_png(img: PngImageFile, tmp_file: Any, quality: int = 95) -> PngImageFile: output_path = "/tmp/" + str(uuid.uuid4()) if quality >= 100: return img if quality % 10 == 0: speed = int(quality / 10) # quality is inversely related to pngquant speed else: # each interval of 10 is related to one speed: # [90 - 99] --> speed 9 speed = int(floor((quality - (quality % 10)) / 10)) command = [ PNGQUANT_PATH, "--strip", "--force", "--output", output_path, "-s" + str(speed), tmp_file.name, ] try: img.save(tmp_file.name, format=img.format) subprocess.check_output(command, stderr=subprocess.STDOUT) img = Image.open(output_path) if Path(output_path).exists(): Path(output_path).unlink() except (OSError, subprocess.CalledProcessError) as error: raise ITSTransformError("ITSTransform Error: " + str(error)) return img
def rgbChannels(inFile: PIL.PngImagePlugin.PngImageFile, message: str="", quantizationWidths: list=[], traversalOrder: list=[], outFile="./IO/outColor.png", verify: bool=False, verbose: bool=False): """ This function takes takes an image of the form [ [<RGB 1>, <RGB 2>, <RGB 3>, ... ], [<RGB a>, <RGB a+1>, <RGB a+2>, ... ], [<RGB b>, <RGB b+1>, <RGB b+2>, ... ], ... ] where RGB <index> is of the form [R, G, B] (if there is an Alpha channel present, it is ignored) And utilizes a modified version Wu and Tsai's algorithm to encode a message into this nested array structure. Because this image is RGB, an order of traversal is needed to ensure the correct encoding/retrieval order while traversing the structure. Define a general pair of RGB pixels as [[R1, G1, B1], [R2, G2, B2]] and flatten it into [R1, G1, B1, R2, G2, B2] The traversal order is an array of that maps the corresponding value to a location it should be sorted to. After mapping and sorting the pixel values, pair adjacent pixels For example, a possible traversal order is the standard [1, 3, 5, 2, 4, 6] Applying this traversal order concept to the RGB pixel pair [[185, 75, 250], [255, 80, 200]] results in these encodable groups of values: [[185, 255], [75, 80], [250, 200]] """ # Verify image data if verify: print("Beginning verification...") if message == "": try: verificationData = inFile.text["png:fingerprint"].split(":") except: raise Exception(f"No verification data found.") # Retrieve verifiable data from image properties imageWidth, imageHeight = rosenburgStrongPairing(int(verificationData[0]), reverse=True) bitLength, messageHash = retrieveLength(verificationData[1]) # Image dimensions are incorrect if inFile.size[0] != imageWidth or inFile.size[1] != imageHeight: raise Exception(f"Image verification failed. Image dimensions don't match encoded verification data.") # Execute function without verifying data option retrievedBinary = rgbChannels(inFile, message, quantizationWidths, traversalOrder, outFile, verbose=verbose) # Ensure entire message was encoded if len(retrievedBinary) >= bitLength: retrievedBinary = retrievedBinary[:bitLength] # Ensure hashes match if hashlib.sha256(retrievedBinary.encode()).hexdigest() == messageHash: print("\nVerified.") return retrievedBinary else: raise Exception(f"Message verification failed. Hash of retrieved binary doesn't match encoded verification data.") raise Exception("Message verification failed. Length of retrieved message binary doesn't match encoded verification data.") else: # Get binary of message if sorted(set(message)) == ["0", "1"]: messageBinary = message else: messageBinary = "0" + str(bin(int.from_bytes(message.encode(), "big")))[2:] returnValue = rgbChannels(inFile, messageBinary, quantizationWidths, traversalOrder, outFile, verbose=verbose) # Build verification data to place in loaded image properties verificationBuilder = "" verificationBuilder += f"{str(rosenburgStrongPairing([inFile.size[0], inFile.size[1]]))}:" verificationBuilder += f"{embedLength(str(len(messageBinary)), messageBinary)}" # Edit PNG metadata to include fingerprint of this PVD algorithm modifyMetadata = PngImageFile(outFile) metadata = PngInfo() metadata.add_text("png:fingerprint", f"{verificationBuilder}") modifyMetadata.save(outFile, pnginfo=metadata) print("\nVerified.") return returnValue print() if message == "": if verbose: print("Verbose message: no message given, assuming retrieval of message") else: # Get binary of message if sorted(set(message)) == ["0", "1"]: messageBinary = message if verbose: print("Verbose message: message contains only binary values, assuming binary message") else: messageBinary = "0" + str(bin(int.from_bytes(message.encode(), "big")))[2:] if verbose: print("Verbose message: message contains non-binary values, assuming ascii message") quantizationWidths = validateQuantization(quantizationWidths, verbose) traversalOrder = validateTraversal(traversalOrder, verbose) print() # If there is an Alpha channel present in the image, it is ignored pixelPairs = pixelArrayToZigZag(inFile, 3, 2) # If function is run without message, assume retrieval of message if message == "": print(f"Retrieving binary from file \"{inFile.filename}\"") print() # Retrieval function messageBinary = "" currentPairCounter = 0 for pixelPair in pixelPairs: currentPairCounter += 1 if len(pixelPair) == 2: # Flatten pixel pair array into un-nested list pixelArray = [pixel for pair in pixelPair for pixel in pair] # Sort pixel array given traversal order and group into calculation ready pairs pixelIndicesDict = dict(sorted(dict(zip(traversalOrder, pixelArray)).items())) traversedPixelPairs = list(groupImagePixels(list(pixelIndicesDict.values()), 2)) currentTraversedCounter = 0 for traversedPixelPair in traversedPixelPairs: currentTraversedCounter += 1 # d value difference = traversedPixelPair[1] - traversedPixelPair[0] # Determine number of bits storable between pixels for width in quantizationWidths: if width[0] <= abs(difference) <= width[1]: lowerBound = width[0] upperBound = width[1] break # Falling-off-boundary check; ensure 0 < calculated pixel value < 255 testingPair = pixelPairEncode(traversedPixelPair, upperBound, difference) if testingPair[0] < 0 or testingPair[1] < 0 or testingPair[0] > 255 or testingPair[1] > 255: # One of the values "falls-off" the range from 0 to 255 and hence is invalid if verbose == True: print(f"Verbose message: channel pair number {currentTraversedCounter} in pixel pair number {currentPairCounter} has the possibility of falling off, skipping") else: # Passes the check, continue with decoding # Number of storable bits between two pixels storableCount = int(math.log(upperBound - lowerBound + 1, 2)) # Extract encoded decimal retrievedDecimal = difference - lowerBound if difference >= 0 else - difference - lowerBound retrievedBinary = bin(retrievedDecimal).replace("0b", "") # Edge case in which embedded data began with 0's if storableCount > len(retrievedBinary): retrievedBinary = "0" * (storableCount-len(retrievedBinary)) + retrievedBinary messageBinary += retrievedBinary return messageBinary else: print(f"Encoding binary \"{messageBinary}\" into file \"{inFile.filename}\"") print() # Encoding function newPixels = [] currentMessageIndex = 0 currentPairCounter = 0 for pixelPair in pixelPairs: currentPairCounter += 1 if len(pixelPair) == 2 and currentMessageIndex < len(messageBinary) - 1: # Flatten pixel pair array into un-nested list pixelArray = [pixel for pair in pixelPair for pixel in pair] # Sort pixel array given traversal order and group into calculation ready pairs traversalIndiceDict = list(zip(traversalOrder, [0,1,2,3,4,5])) pixelIndicesDict = dict(sorted(dict(zip(traversalIndiceDict, pixelArray)).items())) traversedPixelPairs = list(groupImagePixels(list(pixelIndicesDict.values()), 2)) postEncodingValues = [] currentTraversedCounter = 0 for traversedPixelPair in traversedPixelPairs: currentTraversedCounter += 1 # d value difference = traversedPixelPair[1] - traversedPixelPair[0] # Determine number of bits storable between pixels for width in quantizationWidths: # Only need to check upper bound because widths are sorted if abs(difference) <= width[1]: lowerBound = width[0] upperBound = width[1] break # Falling-off-boundary check; ensure 0 < calculated pixel value < 255 testingPair = pixelPairEncode(traversedPixelPair, upperBound, difference) if testingPair[0] < 0 or testingPair[1] < 0 or testingPair[0] > 255 or testingPair[1] > 255: # One of the values "falls-off" the range from 0 to 255 and hence is invalid # Append original pixel pair and skip encoding postEncodingValues += traversedPixelPair if verbose: print(f"Verbose message: channel pair number {currentTraversedCounter} in pixel pair number {currentPairCounter} has the possibility of falling off, skipping") else: # Passes the check, continue with encoding # Number of storable bits between two pixels storableCount = int(math.log(upperBound - lowerBound + 1, 2)) # Ensure haven't already finished encoding entire message if currentMessageIndex + storableCount <= len(messageBinary): # Encode as normal storableBits = messageBinary[currentMessageIndex:currentMessageIndex+storableCount] currentMessageIndex += storableCount else: if currentMessageIndex == len(messageBinary): # Finished encoding entire message postEncodingValues += traversedPixelPair continue else: # Can encode more bits than available, encode what's left storableBits = messageBinary[currentMessageIndex:] # Ensure last bit doesn't get corrupted, fill empty space with 0's storableBits += "0" * (storableCount - len(messageBinary[currentMessageIndex:])) currentMessageIndex = len(messageBinary) # Get value of the chunk of message binary storableBitsValue = int(storableBits, 2) # d' value differencePrime = lowerBound + storableBitsValue if difference >= 0 else -(lowerBound + storableBitsValue) # Calculate new pixel pair newPixelPair = pixelPairEncode(traversedPixelPair, differencePrime, difference) postEncodingValues += newPixelPair # Un-sort pixel array given traversal order and group into calculation original RGB channels pixelIndicesDict = dict(sorted(dict(zip([ key[1] for key in pixelIndicesDict.keys() ], postEncodingValues)).items())) reversedPaired = list(groupImagePixels([pixel for pixel in pixelIndicesDict.values()], 3)) newPixels += reversedPaired else: # For case in which there's an odd number of pixels; append lone pixel value newPixels += pixelPair returnValue = True if currentMessageIndex != len(messageBinary): print(f"Warning: only encoded {len(messageBinary[0:currentMessageIndex])} of {len(messageBinary)} bits ({round(100*len(messageBinary[0:currentMessageIndex])/len(messageBinary), 2)}%)") returnValue = False # Verbose errors if verbose == True: # Underline section of binary that was encoded # Get max printable width in current terminal width = os.get_terminal_size()[0] if len(messageBinary) > width * 5: print("Unable to print verbose warning, return binary exceeds maximum length") # Create array groupings of message lines and underlinings printableMessageLines = list(groupImagePixels(messageBinary, width)) printableUnderlinings = list(groupImagePixels("~"*len(messageBinary[0:currentMessageIndex]), width)) # Zip and print print("\nVerbose warning: only encoded underlined section of message:") for printableMessageLine, printableUnderlining in itertools.zip_longest(printableMessageLines, printableUnderlinings, fillvalue=""): print(f"{printableMessageLine}") if printableUnderlining: print(f"{printableUnderlining}") # Create new image structure, save file newPixels = list(groupImagePixels(newPixels, inFile.size[0])) newPixels = pixelArrayToZigZag(newPixels, 1, inFile.size[0], inFile.size[0], inFile.size[1]) array = np.array(newPixels, dtype=np.uint8) savedImage = PIL.Image.fromarray(array) savedImage.save(outFile) return returnValue
for file in files: if imghdr.what(file) in imageTypes: imageFiles.append(file) # Include directories only if they're readable readableDirectories = [] for directory in directories: if os.access(directory, os.R_OK) == True: readableDirectories.append(directory) # Display neatly print( f"Supported images in current directory \"{currentWorkingDirectory}\":" ) for imageFile in imageFiles: print(imageFile) print() return False from PIL.PngImagePlugin import PngImageFile, PngInfo # Edit PNG metadata to include fingerprint of this PVD algorithm modifyMetadata = PngImageFile("outCopy.png") metadata = PngInfo() metadata.add_text("46", "209") modifyMetadata.save("outCopy.png", pnginfo=metadata) testEncoded = PngImageFile("./outCopy.png") print(testEncoded.text)
from PIL.PngImagePlugin import PngImageFile, PngInfo import numpy as np import cv2 #create a dummy image img = np.array([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]], dtype="float32") #png file save as U8 or I16, U16, RGB type cv2.imwrite("infer.png", img.astype("int8")) targetImage = PngImageFile("infer.png") metadata = PngInfo() metadata.add_text("Rawfloat32", img.newbyteorder().byteswap().tobytes()) targetImage.save("infer.png", pnginfo=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 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 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 remove_all_pnginfo(image_filename=""): targetImage = PngImageFile(image_filename) metadata = "" targetImage.save(image_filename, pnginfo=metadata)
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()