How to convert PDF into image readable by opencv-python? - python

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"

Related

How to read a RAW image file format into OpenCV without any loss of resolution?

I am trying to import a Nikon '.NEF' file into OpenCV. '.NEF' is the file extension for a RAW file format for pictures captured by Nikon cameras. When I open the file in Preview on a Mac, I see that the resolution is 6000 by 4000, and the picture is extremely clear. However, when I import it into OpenCV, I see only 120 by 160 (by 3 for RGB channels) data points, and this leads to a big loss in resolution.
My understanding is that there are 120 by 160 pixels in the NumPy array storing the information about pixels for OpenCV. I tried using -1 for the IMREAD_UNCHANGED flag, but many pixels were left out and image quality was greatly affected.
For your reference, here is my code:
# first Jupyter block
img = cv2.imread('DSC_1051.NEF', -1)
img.shape
Performing img.shape returns (120, 160, 3).
# second Jupyter block
cv2.namedWindow("Resize", cv2.WINDOW_NORMAL)
cv2.resizeWindow("Resize", 1000, 700)
# Displaying the image
cv2.imshow("Resize", img)
cv2.waitKey(0)
cv2.destroyAllWindows()
Summary of problem:
Original image shape is (6000, 4000)
Open CV imports (120, 160), leading to a big loss in resolution
Using the IMREAD_UNCHANGED flag did not lead to OpenCV importing all the pixels in the image, leading to a loss in quality of the image upon performing cv2.imshow().
My question: how can I use OpenCV to import the desired number of pixels? Is there a specific function that I can use? Am I missing an argument to be passed?
If you want to manipulate RAW images without losing resolution with python you'd need to check on a specialized library like rawpy
import rawpy
with rawpy.imread('filename.NEF') as raw:
raw_image = raw.raw_image
You can check the rawpy documentation for more information
Notes:
To install rawpy, Python<=3.7 is required
If you explain a little bit more what do u need to do with the image I could help you with that
Example 1: how to save .NEF as .jpg
Option A: rawpy + Pillow (you need to install Pillow too)
import rawpy
from PIL import Image
with rawpy.imread('filename.NEF') as raw:
rgb = raw.postprocess(use_camera_wb=True)
Image.fromarray(rgb).save('image.jpg', quality=90, optimize=True)
Option B: rawpy + cv2
import rawpy
import cv2
with rawpy.imread('filename.NEF') as raw:
rgb = raw.postprocess(use_camera_wb=True)
bgr = cv2.cvtColor(rgb, cv2.COLOR_RGB2BGR)
cv2.imwrite("image.jpg",bgr)
Quality comparison
I test the code with this 19.2mb .NEF image and I got these results:
Method
.jpg output size
Dimensions
PIL
9kb
320x212
cv2
14kb
320x212
rawpy + PIL
1.4mb
4284 × 2844
rawpy + cv2
2.5mb
4284 × 2844
Example 2: show .NEF with cv2
import rawpy
import cv2
with rawpy.imread('filename.NEF') as raw:
rgb = raw.postprocess(use_camera_wb=True)
bgr = cv2.cvtColor(rgb, cv2.COLOR_RGB2BGR)
cv2.imshow('image', bgr)
cv2.waitKey(0)
cv2.destroyAllWindows()

Python: OpenCV: Creating an image carousel

I have 5 images, namely im1, im2, im3, im4 and im5 which are all in JPG format.
I want to create an image carousel using these images.
I've started with the following code:
from time import sleep
import cv2
imagelist = ["im1.jpg", "im2.jpg", "im3.jpg", "im4.jpg", "im5.jpg"]
for image in imagelist:
img = cv2.imread(image, 1)
cv2.namedWindow("SCREEN")
cv2.imshow("SCREEN", img)
sleep(0.2)
cv2.destroyAllWindows()
Problem: It actually creates a cv2 window every 0.2 seconds and
displays the image. But I want it to display the image in the same
opened window without closing and creating multiple windows.
Kindly help me doing this task.
Thank you
You don't need sleep, you need to use cv2.waitKey(). I tested it and this should work fine.
waitKey function takes an int for delay in ms but it also records a keypress as an ordinal which you can then use to set up keypress commands eg, quit when pressing q. If you leave it empty it advances a step with any keypress.
I just used glob to grab all the .jpg in the folder but replacing it with the images manually in a list like you did will work fine.
import cv2
import glob
imagelist = glob.glob("*.jpg")
for image in imagelist:
img = cv2.imread(image)
cv2.imshow("SCREEN", img)
cv2.waitKey(20)

Apply resize on multiple images at one time in openCV python

I have read a folder containing pictures using glob and imread. Now my I want to resize all of those pictures using for loop in cv2.resize.
following is my code but the output is not correct--
import cv2
import glob
path = glob.glob("C:/Users/RX-91-9/Desktop/prescriptions/*.jpg")
for file in (path):
img=cv2.imread(file)
cv2.imshow("Image", img)
cv2.cv2.waitKey(3)
cv2.destroyAllWindows()
for i in img:
resized_image = cv2.resize(i, (1600,1600))
cv2.imshow('resized_image', resized_image)
cv2.waitKey(3)
cv2.destroyAllWindows()
I don't know why the last for loop is not giving the expected output, i want all the images in 'img' to be resized. Please help if you find what is wrong in my for last for loop.
I assume that you have a list of images in some folder and you to resize all of them. You can run
import cv2
import glob
for filename in glob.glob('images/*.jpg'): # path to your images folder
print(filename)
img=cv2.imread(filename)
rl=cv2.resize(img, (500,500))
cv2.imwrite(f'{filename}resized.jpg', rl)

Can't read second barcode in the same image file using pyzbar

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.

Python PIL For Loop to work with Multi-image TIFF

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

Categories

Resources