Python converting images in TIF format to PNG - python-3.x

I am running the code below to convert files from tifs to PNGs but the converted images isn't converting properly the new files are disrupted and with lines in half of the file as can be seen in the attached image. Any idea why would this happen please?.
import os
from PIL import Image
yourpath = os.getcwd()
for root, dirs, files in os.walk(yourpath, topdown=False):
for name in files:
print(os.path.join(root, name))
if os.path.splitext(os.path.join(root, name))[1].lower() == ".tif":
if os.path.isfile(os.path.splitext(os.path.join(root, name))[0] + ".png"):
print ("A png file already exists for %s" % name)
# If a PNG is *NOT* present, create one from the tiff.
else:
outfile = os.path.splitext(os.path.join(root, name))[0] + ".png"
try:
im = Image.open(os.path.join(root, name))
print("Generating jpeg for %s" % name)
im.thumbnail(im.size)
im.save(outfile, "png", quality=100)
except Exception as e:
print (e)
Running tiffinfo gives the following details:
TIFF Directory at offset 0x8 (8)
Image Width: 1024 Image Length: 1024
Resolution: 5000, 5000 pixels/cm
Bits/Sample: 8
Sample Format: unsigned integer
Compression Scheme: LZW
Photometric Interpretation: RGB color
Samples/Pixel: 3
Rows/Strip: 1
Planar Configuration: separate image planes
ImageDescription: <?xml version="1.0" encoding="UTF-8"?><!-- Warning: this comment is an OME-XML metadata block, which contains crucial dimensional parameters and other important metadata. Please edit cautiously (if at all), and back up the original data before doing so. For more information, see the OME-TIFF web site: https://docs.openmicroscopy.org/latest/ome-model/ome-tiff/. --><OME xmlns="http://www.openmicroscopy.org/Schemas/OME/2016-06" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" Creator="OME Bio-Formats 5.9.2" UUID="urn:uuid:5d7dfdcc-e0e9-4899-9aa2-64717b405232" xsi:schemaLocation="http://www.openmicroscopy.org/Schemas/OME/2016-06 http://www.openmicroscopy.org/Schemas/OME/2016-06/ome.xsd"><Image ID="Image:0" Name="p1_Bromophenol_1mM - Mark_and_Find 001/Position026 (RGB rendering)"><Pixels BigEndian="true" DimensionOrder="XYCZT" ID="Pixels:0" PhysicalSizeX="2.0" PhysicalSizeXUnit="┬Ám" PhysicalSizeY="2.0" PhysicalSizeYUnit="┬Ám" PhysicalSizeZ="1.0" PhysicalSizeZUnit="┬Ám" SizeC="3" SizeT="1" SizeX="1024" SizeY="1024" SizeZ="1" TimeIncrement="1.0" TimeIncrementUnit="s" Type="uint8"><Channel ID="Channel:0:0" Name="red" SamplesPerPixel="3"><LightPath/></Channel><TiffData FirstC="0" FirstT="0" FirstZ="0" IFD="0" PlaneCount="1"><UUID FileName="Position026 (RGB rendering) - 1024 x 1024 x 1 x 1 - 3 ch (8 bits).tif">urn:uuid:5d7dfdcc-e0e9-4899-9aa2-64717b405232</UUID></TiffData></Pixels></Image></OME>
Software: OME Bio-Formats 5.9.2

Related

plupload, preserve_headers = false, and autorotation issue

I have a jpeg image where the EXIF Orientation flag = 6, or "Rotate 90 CW". Here's the pertinent data from exiftool:
---- ExifTool ----
ExifTool Version Number : 12.44
---- File ----
File Name : orig.jpg
Image Width : 4032
Image Height : 3024
---- EXIF ----
Orientation : Rotate 90 CW
Exif Image Width : 4032
Exif Image Height : 3024
---- Composite ----
Image Size : 4032x3024
Here's how IrfanView presents the image, with auto-rotate turned off:
Using the plupload "Getting Started" script from here, with preserve_headers = false, I get an image without EXIF headers - as expected - but rotated 180 degrees, which is unexpected. Again, the view with IrfanView:
Here's the "resize" snippet from the code:
resize: {
width: 5000,
height: 5000,
preserve_headers: false
}
Is there something I'm doing wrong? I would have expected a 90 CW rotation on upload with the EXIF stripped.
Dan
Edit: I'm using plupload V2.3.9
BUMP
I'm getting the exact same result with plupload using these exif samples on github. I chose landscape_6, because it's Orientation is the same as my example ("Rotate 90 CW", or Orientation tag value 6). Here's the before and after upload views using IrfanView with no autorotate, preserve_headers = false:
Aren't these canonical examples for demonstrating exif properties? Unless I'm missing some fundamental point, plupload is busted. I'd much rather it be the former, and someone can tell me the error of my ways.

Converting video to images using OpenCV library problem

I have this code which converts .mov videos to images with the specified frequency (e.g. every 30 seconds here).
import cv2
# the path to take the video from
vidcap = cv2.VideoCapture(r"C:\Users\me\Camera_videos\Images_30sec\PIR-206_7.MOV")
def getFrame(sec):
vidcap.set(cv2.CAP_PROP_POS_MSEC,sec*1000)
hasFrames,image = vidcap.read()
if hasFrames:
cv2.imwrite("image"+str(count)+".jpg", image) # save frame as JPG file
return hasFrames
sec = 0
frameRate = 30 #//it will capture image every 30 seconds
count= 1
success = getFrame(sec)
while success:
count = count + 1
sec = sec + frameRate
sec = round(sec, 2)
success = getFrame(sec)
I have no problem with smaller files. A 5min long .mov file for example, produces 11 images as expected (5 x 60 seconds / 30 seconds = about 10 images with the first image taken at 0 seconds).
However, when I tried a bigger file, which is 483 MB and is about 32mins long, I have encountered a problem.
It is expected to generate some 32 x 60/30 = 64 images.
However, it runs and runs generating some 40'000 images until I manually stop the program. it seems to be stuck at one of the last images??
I have uploaded both .mov files to my google drive, if anyone wants to have a look.
small file
https://drive.google.com/file/d/1guKtLgM-vwt-5fG3_suJrhVbtwMSjMQe/view?usp=sharing
large file
https://drive.google.com/file/d/1V_HVRM29qwlsU0vCyWiOuBP-tkjdokul/view?usp=sharing
Can somebody advise on what's going on here?

How to read binary data in pyspark

I'm reading binary file http://snap.stanford.edu/data/amazon/productGraph/image_features/image_features.b
using pyspark.
import array
from io import StringIO
img_embedding_file = sc.binaryRecords("s3://bucket/image_features.b", 4106)
def mapper(features):
a = array.array('f')
a.frombytes(features)
return a.tolist()
def byte_mapper(bytes):
return str(bytes)
decoded_embeddings = img_embedding_file.map(lambda x: [byte_mapper(x[:10]), mapper(x[10:])])
When just product_id is selected from the rdd using
decoded_embeddings = img_embedding_file.map(lambda x: [byte_mapper(x[:10]), mapper(x[10:])])
The output for product_id is
["b'1582480311'", "b'\\x00\\x00\\x00\\x00\\x88c-?\\xeb\\xe2'", "b'7#\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00'", "b'\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00'", "b'\\xec/\\x0b?\\x00\\x00\\x00\\x00K\\xea'", "b'\\x00\\x00c\\x7f\\xd9?\\x00\\x00\\x00\\x00'", "b'L\\xa6\\n>\\x00\\x00\\x00\\x00\\xfe\\xd4'", "b'\\x00\\x00\\x00\\x00\\x00\\x00\\xe5\\xd0\\xa2='", "b'\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00'", "b'\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00'"]
The file is hosted on s3.
The file in each row has first 10 bytes for product_id next 4096 bytes as image_features
I'm able to extract all the 4096 image features but facing issue when reading the first 10 bytes and converting it into proper readable format.
EDIT:
Finally, the problem comes from the recordLength. It's not 4096 + 10 but 4096*4 + 10. Chaging to :
img_embedding_file = sc.binaryRecords("s3://bucket/image_features.b", 16394)
Should work.
Actually you can find this in the provided code from the web site you downloaded the binary file:
for i in range(4096):
feature.append(struct.unpack('f', f.read(4))) # <-- so 4096 * 4
Old answer:
I think the issue comes from your byte_mapper function.
That's not the correct way to convert bytes to string. You should be using decode:
bytes = b'1582480311'
print(str(bytes))
# output: "b'1582480311'"
print(bytes.decode("utf-8"))
# output: '1582480311'
If you're getting the error:
UnicodeDecodeError: 'utf-8' codec can't decode byte 0x88 in position 4: invalid start byte
That means product_id string contains non-utf8 characters. If you don't know the input encoding, it's difficult to convert into strings.
However, you may want to ignore those characters by adding option ignore to decode function:
bytes.decode("utf-8", "ignore")

what type of array is being returned by tiff.imread()?

I am trying to get the RGB value of pixels from the TIFF image. So, what I did is:
import tifffile as tiff
a = tiff.imread("a.tif")
print (a.shape) #returns (1295, 1364, 4)
print(a) #returns [[[205 269 172 264]...[230 357 304 515]][[206 270 174 270] ... [140 208 183 286]]]
But since we know pixel color ranges from (0,255) for RGB. So, I don't understand what are these array returning, as some values are bigger than 255 and why are there 4 values?
By the way array size is 1295*1364 i.e size of image.
The normal reasons for a TIFF (or any other image) to be 4-bands are that it is:
RGBA, i.e. it contains Red, Green and Blue channels plus an alpha/transparency channel, or
CMYK, i.e. it contains Cyan, Magenta, Yellow and Black channels - this is most common in the print industry where "separations" are used in 4-colour printing, see here, or
that it is multi-band imagery, such as satellite images with Red, Green, Blue and Near Infra-red bands, e.g. Landsat MSS (Multi Spectral Scanner) or somesuch.
Note that some folks use TIFF files for topographic information, bathymetric information, microscopy and other purposes.
The likely reason for the values to be greater than 256, is that it is 16-bit data. Though it could be 10-bit, 12-bit, 32-bit, floats, doubles or something else.
Without access to your image, it is not possible to say much more. With access to your image, you could use ImageMagick at the command-line to find out more:
magick identify -verbose YourImage.TIF
Sample Output
Image: YourImage.TIF
Format: TIFF (Tagged Image File Format)
Mime type: image/tiff
Class: DirectClass
Geometry: 1024x768+0+0
Units: PixelsPerInch
Colorspace: CMYK <--- check this field
Type: ColorSeparation <--- ... and this one
Endianess: LSB
Depth: 16-bit
Channel depth:
Cyan: 16-bit <--- ... and this
Magenta: 1-bit <--- ... this
Yellow: 16-bit <--- ... and this
Black: 16-bit
Channel statistics:
...
...
You can scale the values like this:
from tifffile import imread
import numpy as np
# Open image
img = imread('image.tif')
# Convert to numpy array
npimg = np.array(img,dtype=np.float)
npimg[:,:,0]/=256
npimg[:,:,1]/=256
npimg[:,:,2]/=256
npimg[:,:,3]/=65535
print(np.mean(npimg[:,:,0]))
print(np.mean(npimg[:,:,1]))
print(np.mean(npimg[:,:,2]))
print(np.mean(npimg[:,:,3]))

Image Processing: Merging images with PIL.paste

i have a 2 list of png images, list _c and list _v. I want to paste _v on _c using a code like:
from PIL import Image
background = [Image.open(path, 'r') for path in glob.glob(list_c_path)]
foreground = [Image.open(path, 'r') for path in glob.glob(list_v_path)]
for im in range(len(background)):
pasted = background[im].paste(foreground[im], (0, 0), foreground[im])
This code won't work but it will give you and idea of what i want. I also need to have the images read in grayscale format before they are pasted.
Here's a sample of a background image:
Here's a sample of a foreground image:
And this is the desired result:
I pasted this images using this code:
background = Image.open('1000_c.png')
foreground = Image.open('1000_v.png')
background.paste(foreground, (0, 0), foreground)
background.save('example.png')
How can i achieve this??
Thanks in advance
Mmmm... your result images are identical to your foreground images because although the foreground images have an alpha/transparency layer, they are fully opaque and completely conceal your backgrounds. You need to have a rethink!
You can use ImageMagick in the Terminal to inspect your images. So, let's look at your foreground image:
identify -verbose fg.png
Sample Output
Image: fg.png
Format: PNG (Portable Network Graphics)
Mime type: image/png
Class: DirectClass
Geometry: 118x128+0+0
Units: Undefined
Colorspace: sRGB
Type: PaletteAlpha <--- Image does have alpha/transparency layer
Base type: Undefined
Endianess: Undefined
Depth: 8-bit
Channel depth:
Red: 8-bit
Green: 8-bit
Blue: 8-bit
Alpha: 1-bit
Channel statistics:
Pixels: 15104
Red:
min: 30 (0.117647)
...
...
Alpha:
min: 255 (1) <--- ... but alpha layer is fully opaque
max: 255 (1)
mean: 255 (1)
standard deviation: 0 (0)
kurtosis: 8.192e+51
skewness: 1e+36
entropy: 0
So there is no point pasting a fully opaque image over a background as it will fully conceal it.
If we punch a transparent hole in your foreground image with ImageMagick:
convert fg.png -region 100x100+9+14 -alpha transparent fg.png
It now looks like this:
And if we then run your code:
#!/usr/local/bin/python3
from PIL import Image
background = Image.open('bg.png')
foreground = Image.open('fg.png')
background.paste(foreground, (0, 0), foreground)
background.save('result.png')
It works:
So the moral of the story is that your foreground image either needs some transparency to allow the background to show through, or you need to use some blending mode to choose one or the other of the foreground and background images at each location, or to choose some combination - e.g. the average of the two, or the brighter of the two.
If you want to average the two images, or in fact, do any other blending mode, you could consider using Pillow's ImageChops module - documentation here. So, an average would look like this:
#!/usr/local/bin/python3
from PIL import Image, ImageChops
bg = Image.open('bg.png')
fg = Image.open('fg.png')
# Average the two images, i.e. add and divide by 2
result = ImageChops.add(bg, fg, scale=2.0)
result.save('result.png')

Resources