Each tiff file has 4 images in it. I do not wish to extract and save them if possible, I would just like to use a for loop to look at each of them. (Like look at the pixel [0,0] )and depending on what color it is in all 4 I will do something accordingly.
Is this possible using PIL?
If not what should I use.
Rather than looping until an EOFError, one can iterate over the image pages using PIL.ImageSequence (which effectively is equivalent as seen on the source code).
from PIL import Image, ImageSequence
im = Image.open("multipage.tif")
for i, page in enumerate(ImageSequence.Iterator(im)):
page.save("page%d.png" % i)
You can use the "seek" method of a PIL image to have access to the different pages of a tif (or frames of an animated gif).
from PIL import Image
img = Image.open('multipage.tif')
for i in range(4):
try:
img.seek(i)
print img.getpixel( (0, 0))
except EOFError:
# Not enough frames in img
break
Had to do the same thing today,
I followed #stochastic_zeitgeist's code, with an improvement (don't do manual loop to read per-pixel) to speed thing up.
from PIL import Image
import numpy as np
def read_tiff(path):
"""
path - Path to the multipage-tiff file
"""
img = Image.open(path)
images = []
for i in range(img.n_frames):
img.seek(i)
images.append(np.array(img))
return np.array(images)
Here's a method that reads a multipage tiff and returns the images as a numpy array
from PIL import Image
import numpy as np
def read_tiff(path, n_images):
"""
path - Path to the multipage-tiff file
n_images - Number of pages in the tiff file
"""
img = Image.open(path)
images = []
for i in range(n_images):
try:
img.seek(i)
slice_ = np.zeros((img.height, img.width))
for j in range(slice_.shape[0]):
for k in range(slice_.shape[1]):
slice_[j,k] = img.getpixel((j, k))
images.append(slice_)
except EOFError:
# Not enough frames in img
break
return np.array(images)
Thanks to the answers on this thread I wrote this python module for reading and operating on multipage tiff files: https://github.com/mpascucci/multipagetiff
It also allows to color-code the image stack "depth-wise" and make z-projections.
Hope it can help
Related
I am trying to convert 8 bit images to 10 bit. I thought it would be as easy as changing the bin values. I've tried to pillow and cv-python:
from PIL import Image
from numpy import asarray
import cv2
path = 'path/to/image'
img = Image.open(path)
data = asarray(img)
newdata = (data/255)*1023 #2^10 is 1024
img2 = Image.fromarray(newdata) #this fails
cv2.imwrite('path/newimage.png, newdata)
While cv2.imwrite successfully writes the new file, it is still encoded as an 8bit image even though bin goes up to 1023.
$ file newimage.png
newimage.png: PNG Image data, 640 x 480, 8-bit/color RGB, non-interlaced
Is there another way in either python or linux that can convert 8-bit to 10-bit?
Lots of things going wrong here.
You are mixing OpenCV (cv2.imwrite) with PIL (Image.open) for no good reason. Don't do that, you will confuse yourself as they use different RGB/BGR orderings and conventions,
You are trying to store 10-bit numbers in 8-bit vectors,
You are trying to hold 3 16-bit RGB pixels in a PIL Image which will not work as RGB images must be 8-bit in PIL.
I would suggest:
import cv2
import numpy as np
# Load image
im = cv2.imread(IMAGE, cv2.IMREAD_COLOR)
res = im.astype(np.uint16) * 4
cv2.imwrite('result.png', res)
I found a solution using pgmagick wrapper for python
import pgmagick as pgm
imagePath = 'path/to/image.png'
saveDir = '/path/to/save'
img = pgm.Image(imagePath)
img.depth(10) #sets to 10 bit
save_path = os.path.join(saveDir,'.'.join([filename,'dpx']))
img.write(save_path)
I am using following code to draw rectangle on an image text for matching date pattern and its working fine.
import re
import cv2
import pytesseract
from PIL import Image
from pytesseract import Output
img = cv2.imread('invoice-sample.jpg')
d = pytesseract.image_to_data(img, output_type=Output.DICT)
keys = list(d.keys())
date_pattern = '^(0[1-9]|[12][0-9]|3[01])/(0[1-9]|1[012])/(19|20)\d\d$'
n_boxes = len(d['text'])
for i in range(n_boxes):
if int(d['conf'][i]) > 60:
if re.match(date_pattern, d['text'][i]):
(x, y, w, h) = (d['left'][i], d['top'][i], d['width'][i], d['height'][i])
img = cv2.rectangle(img, (x, y), (x + w, y + h), (0, 255, 0), 2)
cv2.imshow('img', img)
cv2.waitKey(0)
img.save("sample.pdf")
Now, at the end I am getting a PDF with rectangle on matched date pattern.
I want to give this program scanned PDF as input instead of image above.
It should first convert PDF into image format readable by opencv for same processing as above.
Please help.
(Any workaround is fine. I need a solution in which I can convert PDF to image and use it directly instead of saving on disk and read them again from there. As I have lot of PDFs to process.)
There is a library named pdf2image. You can install it with pip install pdf2image. Then, you can use the following to convert pages of the pdf to images of the required format:
from pdf2image import convert_from_path
pages = convert_from_path("pdf_file_to_convert")
for page in pages:
page.save("page_image.jpg", "jpg")
Now you can use this image to apply opencv functions.
You can use BytesIO to do your work without saving the file:
from io import BytesIO
from PIL import Image
with BytesIO() as f:
page.save(f, format="jpg")
f.seek(0)
img_page = Image.open(f)
From PDF to opencv ready array in two lines of code. I have also added the code to resize and view the opencv image. No saving to disk.
# imports
from pdf2image import convert_from_path
import cv2
import numpy as np
# convert PDF to image then to array ready for opencv
pages = convert_from_path('sample.pdf')
img = np.array(pages[0])
# opencv code to view image
img = cv2.resize(img, None, fx=0.5, fy=0.5)
cv2.imshow("img", img)
cv2.waitKey(0)
cv2.destroyAllWindows()
Remember if you do not have poppler in your Windows PATH variable you can provide the path to convert_form_path
poppler_path = r'C:\path_to_poppler'
pages = convert_from_path('sample.pdf', poppler_path=poppler_path)
You can use the library pdf2image. Install with this command: pip install pdf2image. You can then convert the file into one or multiple images readable by cv2. The next sample of code will convert the PIL Image into something readable by cv2:
Note: The following code requires numpy pip install numpy.
from pdf2image import convert_from_path
import numpy as np
images_of_pdf = convert_from_path('source2.pdf') # Convert PDF to List of PIL Images
readable_images_of_pdf = [] # Create a list for thr for loop to put the images into
for PIL_Image in images_of_pdf:
readable_images_of_pdf.append(np.array(PIL_Image)) # Add items to list
The next bit of code can convert the pdf into one big image readable by cv2:
import cv2
import numpy as np
from pdf2image import convert_from_path
image_of_pdf = np.concatenate(tuple(convert_from_path('/path/to/pdf/source.pdf')), axis=0)
The pdf2image library's convert_from_path() function returns a list containing each pdf page in the PIL image format. We convert the list into a tuple for the numpy concatenate function to stack the images on top of each other. If you want them side by side you could change the axis integer to 1 signifying you want to concatenate the images along the y-axis. This next bit of code will show the image on the screen:
cv2.imshow("Image of PDF", image_of_pdf)
cv2.waitKey(0)
This will probably create a window on the screen that is too big. To resize the image for the screen you'll use the following code that uses cv2's built-in resize function:
import cv2
from pdf2image import convert_from_path
import numpy as np
image_of_pdf = np.concatenate(tuple(convert_from_path('source2.pdf')), axis=0)
size = 0.15 # 0.15 is equal to 15% of the original size.
resized = cv2.resize(image_of_pdf, (int(image_of_pdf.shape[:2][1] * size), int(image_of_pdf.shape[:2][0] * size)))
cv2.imshow("Image of PDF", resized)
cv2.waitKey(0)
On a 1920x1080 monitor, a size of 0.15 can comfortably display a 3-page document. The downside is that the quality is reduced dramatically. If you want to have the pages separated you can just use the original convert_from_path() function. The following code shows each page individually, to go to the next page press any key:
import cv2
from pdf2image import convert_from_path
import numpy
images_of_pdf = convert_from_path('source2.pdf') # Convert PDF to List of PIL Images
count = 0 # Start counting which page we're on
while True:
cv2.imshow(f"Image of PDF Page {count + 1}", numpy.array(images_of_pdf[count])) # Display the page with it's number
cv2.waitKey(0) # Wait until key is pressed
cv2.destroyWindow(f"Image of PDF Page {count + 1}") # Destroy the following window
count += 1 # Add to the counter by 1
if count == len(images_of_pdf):
break # Break out of the while loop before you get an "IndexError: list index out of range"
I want to read all the images in a folder and convert them into negatives of the same image
# Import library to work with Images
from PIL import Image
# Make negative pixel
def negatePixel(pixel):
return tuple([255-x for x in pixel])
#img_dir = "" # Enter Directory of all images
for i in range(1,130):
# Original Image
img = []
img = Image.open(str(i) + '.jpg')
# New clear image
new_img = Image.new('RGB', img.size)
# Get pixels from Image
data = img.getdata()
# Create map object consists of negative pixels
new_data = map(negatePixel, data)
# Put negative pixels into the new image
new_img.putdata(list(new_data))
# Save negative Image
new_img.save(str(i) + 'neg.jpg')
print ('saved image' + str(i))
I'm getting this error :
Traceback (most recent call last):
File "2.py", line 23, in <module>
new_img.putdata(list(new_data))
File "2.py", line 6, in negatePixel
return tuple([255-x for x in pixel])
TypeError: 'int' object is not iterable
I wrote the above programme to perform what I wanted it to, but it is striking an error. I'm new to programming and is there any idea how to solve this?
Your approach is not ideal. Firstly, you can do that much more simply with ImageMagick which is included in most Linux distros and is available for macOS and Windows. Just in Terminal, this will invert all files in the current directory:
magick mogrify -negate *.jpg
Or, if you want them saved in a directory called results:
mkdir results
magick mogrify -path results -negate *.jpg
If you want to stick to Python and PIL/Pillow, there is already a invert() function in its ImageOps module here:
#!/usr/local/bin/python3
from PIL import Image, ImageOps
# Load image
im = Image.open('image.jpg')
# Invert
result = ImageOps.invert(im)
# Save
result.save('result.jpg')
If you don't want to use the built-in invert(), you will be much better off using the point() function here:
#!/usr/local/bin/python3
from PIL import Image
# Load image
im = Image.open('image.jpg')
# Negate
result = im.point(lambda p: 255 -p)
# Save
result.save('result.jpg')
Note: In general, as soon as you start using a for loop, or getdata() with an image in Python, you have probably already gone wrong. You should use built-in library functions or Numpy really, else everything will be slo-o-o-o-o-o-w.
After cropping and saving images, I found that there are many full black images (RGB = 0,0,0). I want to delete these images.
The followings are the codes I have tried:
import os, glob
from PIL import image
def CleanUp_images():
for filename in glob.glob('/Users/Xin/Desktop/TestFolder/*.jpg'):
im = Image.open(filename)
pix = list(im.getdata())
if pix == [(0,0,0)]:
os.remove(im)
CleanUp_images()
However, the above codes didn't work out
Can anyone give me a help?
With os.remove(im) you're passing an image object to os.remove which only accepts strings when you just have to do:
os.remove(filename)
filename is the absolute path to your file (thanks to glob), so it will work.
Also if pix == [(0,0,0)]: this isn't going to work because the list has more than 1 element, even if all elements are black pixels. What works is creating a set of rgb tuples. If there are only black pixels the set has a size of 1:
if set(pix) == {(0,0,0)}:
I'm coding in Python 2.7 and I need to implement a process where I will read a PDF then obtain the image of the first page of the document, then from that image that contains two barcodes obtain the values of both. As of now these are the two functions I've been working on so far (I need to do a lot of polishing before I move this to an environment):
Python process to obtain the image from the PDF from a Tutorial:
from wand.image import Image as wi
pdf = wi(filename="test.pdf", resolution=300)
pdfImageTest = pdf.convert("png")
i=1
for img in pdfImage.sequence:
page = wi
(image = img)
page.save(filename="test"+str(i)+".png")
i+=1
Python process to read the barcodes from an image:
from pyzbar.pyzbar import decode
from PIL import Image
import cv2
import numpy
decodedObjects = decode(Image.open('test2.png'))
obj = decodedObjects
print(obj)
decodedObjects = decode(cv2.imread('test2.png'))
print(obj)
According to the documentation for decode function in pyzbar, the function will scan all the barcodes contained in the image but as of now for both cases I've used, I'm only obtaining the first barcode in the image. Is there a way to force the function to keep scanning the image or pointing it into a specific location of the image after finishing the process for the first image?
You should use obj.data and iterate over all objects.
Here's an example:
from pyzbar.pyzbar import decode
from PIL import Image
import cv2
import numpy
decodedObjects = decode(Image.open('test2.png'))
obj = decodedObjects
for bar in obj:
print(bar.data)
By the way, the print statement is replaced with print() function in Python 3. So if you strictly want to use Python 2.7, you should use e.g. print bar.data.