Esempio n. 1
0
    def _png_to_nximg(data, image_format):
        data_nximg = bytes()

        with BytesIO(data) as io_png:
            with BytesIO() as io_nximg:
                png = PngImageFile(io_png)
                w, h = png.width, png.height

                if image_format == IMAGE_FORMAT_1555:
                    for y in range(h):
                        for x in range(w):
                            [r, g, b, a] = png.getpixel((x, y))
                            IOHelper.write_struct(io_nximg, "<2B",
                                                  *NXColor.to_1555(r, g, b, a))
                elif image_format == IMAGE_FORMAT_4444:
                    for y in range(h):
                        for x in range(w):
                            [r, g, b, a] = png.getpixel((x, y))
                            IOHelper.write_struct(io_nximg, "<2B",
                                                  *NXColor.to_4444(r, g, b, a))
                elif image_format == IMAGE_FORMAT_8888:
                    for y in range(h):
                        for x in range(w):
                            [r, g, b, a] = png.getpixel((x, y))
                            IOHelper.write_struct(io_nximg, "<4B", b, g, r, a)
                else:
                    raise Exception('Unsupport image format: %s' %
                                    image_format)

                data_nximg = IOHelper.read_range(io_nximg)

        return data_nximg, w, h
def load_buffer(buffer_path, replay_buffer):
    file_list = os.listdir(buffer_path)
    for idx, file_name in enumerate(file_list[:-1]):
        if idx > 2000:
            print (idx)
        im = PngImageFile(os.path.join(buffer_path, file_name))
        im_next = PngImageFile(os.path.join(buffer_path, file_list[idx+1]))

        ## Change to action space, now two-dimensional
        throttle, steer, brake = float(im.text['control_throttle']), float(im.text['control_steer']), float(im.text['control_brake'])
        action = np.zeros((2,))
        action[0] = throttle if throttle > 0.0 else (-brake)
        action[1] = steer
        
        reward = np.array([float(im.text['reward'])])
        location = np.array([float(im.text['location_x']), float(im.text['location_y']), float(im.text['location_z'])])

        obs = Image.open(os.path.join(buffer_path, file_name))
        next_obs = Image.open(os.path.join(buffer_path, file_list[idx+1]))

        path = dict()
        path['observations'] = [{'image': np.array(obs).astype(np.float32) / 255.0},]
        path['next_observations'] = [{'image': np.array(next_obs).astype(np.float32) / 255.0}, ]
        path['actions'] = [action,]
        path['rewards'] = [reward,]
        path['terminals'] = [(False,),]

        print ('Location: ', idx, location)
        replay_buffer.add_path(path)
    
    print ('Replay Buffer Loaded: ', replay_buffer._size)
Esempio n. 3
0
def ex_oxygen(img):
    from PIL.PngImagePlugin import PngImageFile as Png
    coreimg = Png(img)
    w = coreimg.getbbox()[2]
    h = coreimg.getbbox()[3] // 2
    chars = [coreimg.getpixel((i, h))[0] for i in range(0, w, 7)]
    message = ''.join(map(chr, chars))
    result = ''.join(map(lambda x: chr(int(x)), message[43:-4].split(',')))
    return 7, result
Esempio n. 4
0
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()
Esempio n. 5
0
    def send_png(self, fname):
        png = PngImageFile(fname)

        png.load()  # load metadata
        with open(fname, "rb") as f:
            return self.bot.send_photo(
                chat_id=self.chat_id,
                photo=f,
                parse_mode="Markdown",
                caption=png.info.get("Comment", "`{}`".format(fname.name)),
            )
Esempio n. 6
0
 def get_colours_from_image(self, image: PngImageFile, country: str) -> Set[str]:
     # we set the colour profile to RGBA because PIL moans about transparency with RGB
     image = image.convert("RGBA")
     num_pixels = reduce(mul, image.size)
     img_colours = image.getcolors(num_pixels)
     if not img_colours:
         print(f"Could not get image colours for {country}")
         return set()
     # print(sorted(img_colours, key=lambda x: x[0], reverse=True))
     freq_colours = [FreqColour(f, Colour(c[:3])) for f, c in img_colours]
     colours = self._reduce_colours(freq_colours, num_pixels)
     return colours
Esempio n. 7
0
 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)
Esempio n. 8
0
    def load_spectrogram(cls, fname, to_nparray=True):
        '''
        Loads a .png spectrogram file,
        and returns a numpy array

        :param fname: file to load
        :type fname: str
        :param to_nparray: if True, convert torchvision. Image
            instance to a numpy array, and return that as result
        :type to_nparray: bool
        :returns tuple: the image and the .png file's possibly empty metadata dict
        :rtype: ({np.array|torchvision.Image}, {str : str})
        :raises FileNotFoundError
        '''

        if not os.path.exists(fname):
            raise FileNotFoundError(f"File {fname} does not exist.")

        png_img = PngImageFile(fname)
        try:
            info = png_img.text
        except Exception as e:
            cls.log.info(f"No available info in .png file: {repr(e)}")
            info = None

        img_obj = Image.open(fname)
        if to_nparray:
            res = np.asarray(img_obj)
        else:
            res = img_obj
        return (res, info)
Esempio n. 9
0
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)
Esempio n. 10
0
def test_custom_encode(colors):
    result, meta = encode(uid_18, "custom", colors[0], colors[1], colors[2])
    result.save("test.png", pnginfo=meta)

    with PngImageFile("test.png") as fp:
        assert decode(fp) == uid_18

    delete_test_png()
Esempio n. 11
0
    def svg_image_factory(fp, filename):
        mime_type = magic.from_buffer(fp.read(1024), mime=True)
        if mime_type != "image/svg+xml":
            raise TypeError

        fp.seek(0)
        png_data = PNGSurface.convert(fp.read(), url_fetcher=url_fetcher)
        return PngImageFile(BytesIO(png_data))
Esempio n. 12
0
    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)
Esempio n. 13
0
def get_dict_from_pnginfo(image_filename="", dict_name=""):
    dict_to_return = {}
    targetImage = PngImageFile(image_filename)
    all_settings_dict = targetImage.text
    for settings_key in all_settings_dict:
        if (settings_key == dict_name):
            dict_to_return = json.loads(all_settings_dict[settings_key])
    return dict_to_return
Esempio n. 14
0
    def _action(uid: str, color: Color = Color.red):
        result, meta = encode(uid)
        result.save("test.png", pnginfo=meta)

        with PngImageFile("test.png") as fp:
            uid_result = decode(fp)

        return result, uid_result
Esempio n. 15
0
    def __getitem__(self, index):
        """Return a data point and its metadata information.

        Parameters:
            index - - a random integer for data indexing

        Returns a dictionary that contains A, B, A_paths and B_paths
            A (tensor) - - an image in the input domain
            B (tensor) - - its corresponding image in the target domain
            A_paths (str) - - image paths
            B_paths (str) - - image paths (same as A_paths)
        """
        # read a image given a random integer index
        AB_path = self.AB_paths[index]
        # print(AB_path)
        path = AB_path.replace('train', 'train2')
        # print(index)
        AB = Image.open(AB_path)
        targetImage = PngImageFile(AB_path)
        des = int(targetImage.text['des'])


        # des = int(AB.info['des'])
        # matrix = im.info['Comment']
        # split AB image into A and B
        w, h = AB.size
        w2 = int(w / 2)
        A = AB.crop((0, 0, w2, h))
        B = AB.crop((w2, 0, w, h))
        opacity = 50
        flash = skimage.img_as_float(A)
        ambient = skimage.img_as_float(B)
        # im = A_float * opacity / 100 + B_float * (100 - opacity) / 100
        # paper version 4: from A flash 0.5 ambient 1.7 to flash 1.7 ambient 0.5
        A = flash * 1.1+ ambient * 1.1
        A = xyztorgb(A,des)
        # opacity2 = opacity + 0.7
        # if opacity2 > 2:
        #     opacity2 = 2
        B = flash * 2.2 + ambient * 1.1
        B = xyztorgb(B,des)

        # cv2.imwrite(path_AB, im_AB)
        # im = (im * 255 / np.max(im)).astype('uint8')

        # print(blended)
        # im = (im * 255 / np.max(im)).astype('uint8')
        # im = Image.fromarray(im)

        # apply the same transform to both A and B
        transform_params = get_params(self.opt, A.size)
        A_transform = get_transform(self.opt, transform_params, grayscale=(self.input_nc == 1))
        B_transform = get_transform(self.opt, transform_params, grayscale=(self.output_nc == 1))

        A = A_transform(A)
        B = B_transform(B)

        return {'A': A, 'B': B, 'A_paths': AB_path, 'B_paths': AB_path}
Esempio n. 16
0
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)
Esempio n. 17
0
    def setUp(self):
        self.mock_button_repository = AbstractButtonRepository(None)
        self.directory = os.path.dirname(os.path.abspath(__file__))
        self.new_tab_template = PngImageFile(
            os.path.join(self.directory,
                         'data/buttons/chrome/new_tab_template_chrome.png'))
        self.new_tab_template = convert_picture_to_grayscale(
            self.new_tab_template)
        self.new_tab_template = convert_picture_to_numpy_array(
            self.new_tab_template)

        self.new_win_template = PngImageFile(
            os.path.join(
                self.directory,
                'data/buttons/chrome/new_windows_template_chrome.png'))
        self.new_win_template = convert_picture_to_grayscale(
            self.new_win_template)
        self.new_win_template = convert_picture_to_numpy_array(
            self.new_win_template)
Esempio n. 18
0
 def __binarizing(self, img: PngImageFile, threshold):
     pixdata = img.load()
     w, h = img.size
     for y in range(h):
         for x in range(w):
             if pixdata[x, y] < threshold:
                 pixdata[x, y] = 0
             else:
                 pixdata[x, y] = 255
     return img
Esempio n. 19
0
    def decode_from_img(self, file: BytesIO) -> str:
        """Decodes secrets from png squares

        Parameters
        ___________
        file: bytes
            file object produced by opening in 'rb' mode or other means"""

        img = PngImageFile(fp=file)
        try:
            edge = img.text["edge"]
        except KeyError:  # img was not upscaled
            pass
        else:  # img was upscaled so we scale it down as prescribed by metadata
            edge = int(edge)
            img = img.resize((edge, edge), resample=Image.NEAREST)

        # asarray creates readonly hence the np.array
        return self.decode(np.array(img))
Esempio n. 20
0
def load_buffer(buffer_path, replay_buffer):
    file_list = os.listdir(buffer_path)
    for idx, file_name in enumerate(file_list[:-1]):
        if idx % 1000 == 0:
            print(idx)
        im = PngImageFile(os.path.join(buffer_path, file_name))
        im_next = PngImageFile(os.path.join(buffer_path, file_list[idx + 1]))
        action = np.array([
            float(im.text['control_throttle']),
            float(im.text['control_steer']),
            float(im.text['control_brake'])
        ])
        reward = np.array([float(im.text['reward'])])

        obs = Image.open(os.path.join(buffer_path, file_name))
        next_obs = Image.open(os.path.join(buffer_path, file_list[idx + 1]))

        # Since we are using obs_dict replay buffer, we can only
        # call add_sample
        path = dict()
        path['observations'] = [
            {
                'image': np.array(obs).astype(np.float32) / 255.0
            },
        ]
        path['next_observations'] = [
            {
                'image': np.array(next_obs).astype(np.float32) / 255.0
            },
        ]
        path['actions'] = [
            action,
        ]
        path['rewards'] = [
            reward,
        ]
        path['terminals'] = [
            (False, ),
        ]
        replay_buffer.add_path(path)

    print('Replay Buffer Loaded: ', replay_buffer._size)
Esempio n. 21
0
def indexes_from_image(image: PngImageFile, sprite_idx: int) -> list:
    assert (0 <= sprite_idx <= 255)
    sprite_x = (sprite_idx % 16) * 8
    sprite_y = (sprite_idx // 16) * 8
    assert (0 <= sprite_x <= (128 - 8))
    assert (0 <= sprite_y <= (128 - 8))
    ret = [0 for _ in range(0, 64)]
    for y in range(0, 8):
        for x in range(0, 8):
            ret[y * 8 + x] = image.getpixel((x + sprite_x, y + sprite_y))
    assert (len(ret) == 64)
    return ret
Esempio n. 22
0
def cli_decode(path: str, color: str = "red"):
    """Decode a snowflake image at the given file PATH"""
    try:
        set_color = Color[color]
    except KeyError:
        raise ColorError("Invalid color passed.")

    try:
        with PngImageFile(path) as fp:
            print(decode(fp, set_color))
    except Exception as e:
        logging.error(f"Error: {e}")
Esempio n. 23
0
    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()
Esempio n. 24
0
def test_no_metadata(caplog):
    caplog.set_level(logging.WARNING)

    with PngImageFile("src/tests/no_meta.png") as fp:
        decode(fp)

        assert (
            "Warning: Unable to fetch image metadata, using default value (Red).\n"
            in caplog.text)

        result = decode(fp, Color.orange)
        assert result == uid_18
    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)
Esempio n. 26
0
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 paint_bolnur_katskhuri_cross():
    """Save an image of a Bolnur-Kathskuri cross, as seen on the flag of Georgia"""

    # Make the rounded bar with transparent background available
    rounded_bar_file = paint_rounded_bar()

    # Open the rounded bars as images
    rounded_bar_horizontal = PainterUtils.read_flag_drawing(rounded_bar_file)
    rounded_bar_vertical = PainterUtils.read_flag_drawing(rounded_bar_file)
    # In principle, it is not necessary to import PngImageFile explicitly, and call rotate with the
    # class explicitly and an instance as first parameter, but when you do so, you can navigate
    # more easily to the source code of the rotate function.
    rounded_bar_vertical = PngImageFile.rotate(rounded_bar_vertical, angle=90,
                                               resample=Image.BICUBIC, expand=True)
    # When rotating, the size can change up to 1 pixel. E.g. a bar of (2757, 995) has size
    # (996, 2757) after rotation with expand=True, regardless of resample parameter. To
    # counteract this, we define the size of the vertical bar to exact the same size as the
    # size of the horizontal bar, but width and height interchanged.
    rounded_bar_vertical = rounded_bar_vertical.resize(
        size=(rounded_bar_horizontal.size[1], rounded_bar_horizontal.size[0]),
        resample=Image.NEAREST)

    # Create the canvas
    W = rounded_bar_horizontal.size[0]
    H = rounded_bar_vertical.size[1]
    cross = Image.new("RGBA", (W, H), (255, 255, 255, 0))

    # Determine where to position the bars
    D = rounded_bar_horizontal.size[1]
    left = int(round(W / 2 - D / 2))
    upper = int(round(H / 2 - D / 2))
    right = int(round(W / 2 + D / 2))
    lower = int(round(H / 2 + D / 2))

    if False:  # Set to true for debug mode
        print('W:{W}, H:{H}, D:{D}, left:{left}, upper:{upper}, '
              'right:{right}, lower:{lower}, width: {width}, height: {height}'.
              format(W=W, H=H, D=D, left=left, upper=upper,
                     right=right, lower=lower, width=right - left, height=lower - upper))

    # Paste the bars
    cross.paste(im=rounded_bar_horizontal, box=(0, upper, W, lower), mask=rounded_bar_horizontal)
    cross.paste(im=rounded_bar_vertical, box=(left, 0, right, H), mask=rounded_bar_vertical)
    # cross.paste(im=rounded_bar_horizontal, box=(0, upper, W, lower), mask=None)
    # cross.paste(im=rounded_bar_vertical, box=(left, 0, right, H), mask=None)

    # Save the image
    PainterUtils.write_flag_drawing(cross, 'bolnur_katskhuri_cross')
Esempio n. 28
0
    def process(self, image: PngImageFile) -> Image.Image:
        width, height = image.width, image.height
        current_aspect = width / height

        # Crop the image to a 16:9 aspect ratio
        if current_aspect > self.aspect:
            # Crop the left and right edges
            new_width = int(self.aspect * height)
            offset = (width - new_width) / 2
            resize = (offset, 0, width - offset, height)
        else:
            # Crop the top and bottom
            new_height = int(width / self.aspect)
            offset = (height - new_height) / 2
            resize = (0, offset, width, height - offset)
        return image.crop(resize)
Esempio n. 29
0
    def load_undistorted_segmentation(self, image: str) -> np.ndarray:
        """Load an undistorted image segmentation."""
        segmentation_file = self._undistorted_segmentation_file(image)
        with self.io_handler.open(segmentation_file, "rb") as fp:
            with PngImageFile(fp) as png_image:
                # TODO: We do not write a header tag in the metadata. Might be good safety check.
                data = np.array(png_image)
                if data.ndim == 2:
                    return data
                elif data.ndim == 3:
                    return data[:, :, 0]

                    # TODO we can optionally return also the instances and scores:
                    # instances = (
                    #     data[:, :, 1].astype(np.int16) + data[:, :, 2].astype(np.int16) * 256
                    # )
                    # scores = data[:, :, 3].astype(np.float32) / 256.0
                else:
                    raise IndexError
Esempio n. 30
0
def restore_instrument_settings(filename="", instrument_dict=[]):
    isimage = False
    if (filename.endswith('PNG') or filename.endswith('png')):
        targetImage = PngImageFile(filename)
        all_settings_dict = targetImage.text
    else:
        infile = open('filename', )
        all_settings_dict = json.load(infile)

    rm = pyvisa.ResourceManager()
    supported_inst_dict = {
        'k2308_unique_scpi': "KEITHLEY INSTRUMENTS INC.,MODEL 2308",
        'k2460_unique_scpi': "KEITHLEY INSTRUMENTS,MODEL 2460",
        'hmp4040_unique_scpi': "HAMEG,HMP4040",
        'tek_afg3000_unique_scpi': "TEKTRONIX,AFG3102",
        'plz4w_unique_scpi': "KIKUSUI,PLZ164WA,",
        'key_33250a_unique_scpi': "Agilent Technologies,33250A"
    }

    for settings_key in all_settings_dict:  # loop thru all settings inside image
        if (settings_key
                in supported_inst_dict):  # only process supported instruments
            if (isimage):
                unique_scpi = json.loads(
                    all_settings_dict[settings_key])  # json method needed
            else:
                unique_scpi = all_settings_dict[
                    settings_key]  # json method not needed
            for instrument_key in instrument_dict:  # loop thru all connected instruments
                if (
                        instrument_key.startswith(
                            supported_inst_dict[settings_key])
                ):  # only process connected instruments that match supported instruments
                    instrument = rm.open_resource(
                        instrument_dict[instrument_key])
                    instrument.write('*RST')
                    time.sleep(2)
                    for scpi_cmd in unique_scpi:
                        instrument.write(scpi_cmd)
    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)
Esempio n. 32
0
 def _get_EXIF_DateTimeOriginal(self, file_path):
     """ try to get the recording date from the EXIF in PNG file """
     try:
         image = PngImageFile(file_path)
         metadata = PngInfo()
         exif_array = []
         for i in image.text:
             compile = i, str(image.text[i])
             exif_array.append(compile)
         if len(exif_array) > 0:
             header = exif_array[0][0]
             if header.startswith("XML"):
                 xml = exif_array[0][1]
                 for line in xml.splitlines():
                     if 'DateCreated' in line:
                         idx1 = line.find('>')
                         idx2 = line.rfind('<')
                         if (idx1 != -1) and (idx2 != -1):
                             dt = line[idx1 + 1:idx2]
                             return dt
     except Exception as err:
         pass  # returns None
     return None