How to draw image from raw bytes using ReportLab? - python

All the examples I encounter in the internet is loading the image from url (either locally or in the web). What I want is to draw the image directly to the pdf from raw bytes.
UPDATE:
#georgexsh Here is my code based on my understanding of your comment below:
def PDF_view(request):
response = HttpResponse(content_type='application/pdf')
...
page = canvas.Canvas(response, pagesize=A4)
page.setTitle("Sample PDF")
image = StringIO(raw_image_bytes) # raw_image_bytes is from external source
image.seek(0)
page.drawImage(image, 100, 100 )
filename = 'document.pdf'
page.showPage()
page.save()
return response

from report lab Image object source code, filelike obj is acceptable, so you could wrap image data with StringIO/io.BytesIO, pass it as filename.
updated:
as you're using drawImage method, it needs a ImageReader obj:
from reportlab.lib.utils import ImageReader
import io
image = ImageReader(io.BytesIO(raw_image_bytes))
page.drawImage(image, ...)

Related

Not able to save Image by downloading in python

I need to download a png file from a website and save the same in local directory .
The code is as below :
import pytesseract
from PIL import Image
from pathlib import Path
k = requests.get('https://somewebsite.com/somefile.png',stream =True)
Img=Image.open(k) # <----
Img.save("/new.png")
while executing it in JupyterNotebook
If I execute, i always get an error "response object has no attribute seek"
On the other hand , if I change the code to
Img= Image.open(k.raw), it works fine
I need to understand why it is so
You can save image data from a link using open() and write() functions:
import requests
URL = "https://images.unsplash.com/photo-1574169207511-e21a21c8075a?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=880&q=80"
name = "IMG.jpg" #The name of the image once saved
Picture_request = requests.get(URL)
if Picture_request.status_code == 200:
with open(name, 'wb') as f:
f.write(Picture_request.content)
Per pillow the docs:
:param fp: A filename (string), pathlib.Path object or a file object.
The file object must implement file.read,
file.seek, and file.tell methods,
and be opened in binary mode.
response itself is just the response object. Using response.raw implements read, seek, and tell.
However, you should use response.content to get the raw bytes of the image. If you want to open it, then use io.BytesIO (quick explanation here).
import requests
from PIL import Image
from io import BytesIO
URL = "whatever"
name = "image.jpg"
response = requests.get(URL)
mybytes = BytesIO()
mybytes.write(response.content) # write the bytes into `mybytes`
mybytes.seek(0) # set pointer back to the beginning
img = Image.open(mybytes) # now pillow reads from this io and gets all the bytes we want
# do things to img

How to convert image(ndarray object) to image object so that it can be JSON serialized?

I am new to python and programming world. I have a code which converts image into numpy array. I want to learn how to reverse it i.e. convert the numpy array into image.
I have an rest api code, which takes an image from post method, convert it to numpy array, do some processing and return some results. However, I am trying to modify the code, so that, I can take two image as input from post method, convert it to numpy array, combine those images as one and send that final image as json response.
I have successfully modified the code, so that, it accepts two images as input. I will add later the Code for combining two image into one. Currently, I am trying to send image as json response. For that, I am just trying to send the image I got from post method, as it is. But I am receiving an error of
"Object of type 'ndarray' is not JSON serializable".
So, I thought, I should convert the ndarray object(Previously created) must be convert back into image so that it can be json serialized. How to do that?
# import the necessary packages
from django.views.decorators.csrf import csrf_exempt
from django.http import JsonResponse
import numpy as np
import urllib.request
import json
import cv2
import os
#csrf_exempt
def detect(request):
# initialize the data dictionary to be returned by the request
data = {"success":False}
# check to see if this is a post request
if request.method == "POST":
# check to see if an image was uploaded
if request.FILES.get("image1", None) and request.FILES.get("image2", None) is not None:
# grab the uploaded image
image1 = _grab_image1(stream=request.FILES["image1"])
image2 = _grab_image2(stream=request.FILES["image2"])
# otherwise, assume that a URL was passed in
else:
# grab the URL from the request
url = request.POST.get("url", None)
# if the URL is None, then return an error
if url is None:
data["error"] = "No URL provided."
return JsonResponse(data)
# load the image and convert
image1 = _grab_image1(url=url)
image2 = _grab_image2(url=url)
# Code for combining two image
data.update({"final1": image1,"final2": image2, "success": True})
# return a JSON response
return JsonResponse(data)
def _grab_image1(path=None, stream=None, url=None):
# if the path is not None, then load the image from disk
if path is not None:
image1 = cv2.imread(path) #loads the image
# otherwise, the image does not reside on disk
else:
# if the URL is not None, then download the image
if url is not None:
resp = urllib.request.urlopen(url)
data = resp.read()
# if the stream is not None, then the image has been uploaded
elif stream is not None:
data = stream.read()
# convert the image to a NumPy array and then read it into
# OpenCV format
image1 = np.asarray(bytearray(data), dtype="uint8")
image1 = cv2.imdecode(image1, cv2.IMREAD_COLOR)
# return the image
return image1
def _grab_image2(path=None, stream=None, url=None):
# if the path is not None, then load the image from disk
if path is not None:
image2 = cv2.imread(path) #loads the image
# otherwise, the image does not reside on disk
else:
# if the URL is not None, then download the image
if url is not None:
resp = urllib.request.urlopen(url)
data = resp.read()
# if the stream is not None, then the image has been uploaded
elif stream is not None:
data = stream.read()
# convert the image to a NumPy array and then read it into
# OpenCV format
image2 = np.asarray(bytearray(data), dtype="uint8")
image2 = cv2.imdecode(image2, cv2.IMREAD_COLOR)
# return the image
return image2
Convert image(ndarray object) so that, it could be json serialized.
I don't think it's possible to do what you are trying to do in that way... maybe yo could try two things:
Store it somewhere in the server and serialize its URL.
Encode the image, put the coded image in the JSON and decoded it later. You could try base64 python's library.
If choosing the second option, simply encode the ndarray like this:
coded_image = base64.b64encode(image)
And for decoding:
decoded_image = base64.decodestring(coded_image)

Convert PDF page to image with pyPDF2 and BytesIO

I have a function that gets a page from a PDF file via pyPdf2 and should convert the first page to a png (or jpg) with Pillow (PIL Fork)
from PyPDF2 import PdfFileWriter, PdfFileReader
import os
from PIL import Image
import io
# Open PDF Source #
app_path = os.path.dirname(__file__)
src_pdf= PdfFileReader(open(os.path.join(app_path, "../../../uploads/%s" % filename), "rb"))
# Get the first page of the PDF #
dst_pdf = PdfFileWriter()
dst_pdf.addPage(src_pdf.getPage(0))
# Create BytesIO #
pdf_bytes = io.BytesIO()
dst_pdf.write(pdf_bytes)
pdf_bytes.seek(0)
file_name = "../../../uploads/%s_p%s.png" % (name, pagenum)
img = Image.open(pdf_bytes)
img.save(file_name, 'PNG')
pdf_bytes.flush()
That results in an error:
OSError: cannot identify image file <_io.BytesIO object at 0x0000023440F3A8E0>
I found some threads with a similar issue, (PIL open() method not working with BytesIO) but I cannot see where I am wrong here, as I have pdf_bytes.seek(0) already added.
Any hints appreciated
Per document:
write(stream) Writes the collection of pages added to this object out
as a PDF file.
Parameters: stream – An object to write the file to. The object must
support the write method and the tell method, similar to a file
object.
So the object pdf_bytes contains a PDF file, not an image file.
The reason why there are codes like above work is: sometimes, the pdf file just contains a jpeg file as its content. If your pdf is just a normal pdf file, you can't just read the bytes and parse it as an image.
And refer to as a more robust implementation: https://stackoverflow.com/a/34116472/334999
[![enter image description here][1]][1]
import glob, sys, fitz
# To get better resolution
zoom_x = 2.0 # horizontal zoom
zoom_y = 2.0 # vertical zoom
mat = fitz.Matrix(zoom_x, zoom_y) # zoom factor 2 in each dimension
filename = "/xyz/abcd/1234.pdf" # name of pdf file you want to render
doc = fitz.open(filename)
for page in doc:
pix = page.get_pixmap(matrix=mat) # render page to an image
pix.save("/xyz/abcd/1234.png") # store image as a PNG
Credit
[Convert PDF to Image in Python Using PyMuPDF][2]
https://towardsdatascience.com/convert-pdf-to-image-in-python-using-pymupdf-9cc8f602525b

Upload Image To Imgur After Resizeing In PIL

I am writing a script which will get an image from a link. Then the image will be resized using the PIL module and the uploaded to Imgur using pyimgur. I dont want to save the image on disk, instead manipulate the image in memory and then upload it from memory to Imgur.
The Script:
from pyimgur import Imgur
import cStringIO
import requests
from PIL import Image
LINK = "http://pngimg.com/upload/cat_PNG106.png"
CLIENT_ID = '29619ae5d125ae6'
im = Imgur(CLIENT_ID)
def _upload_image(img, title):
uploaded_image = im.upload_image(img, title=title)
return uploaded_image.link
def _resize_image(width, height, link):
#Retrieve our source image from a URL
fp = requests.get(link)
#Load the URL data into an image
img = cStringIO.StringIO(fp.content)
im = Image.open(img)
#Resize the image
im2 = im.resize((width, height), Image.NEAREST)
#saving the image into a cStringIO object to avoid writing to disk
out_im2 = cStringIO.StringIO()
im2.save(out_im2, 'png')
return out_im2.getvalue()
When I run this script I get this error: TypeError: file() argument 1 must be encoded string without NULL bytes, not str
Anyone has a solution in mind?
It looks like the same problem as this, and the solution is to use StringIO.
A common tip for searching such issues is to search using the generic part of the error message/string.

Generate in-memory image for Django testing

Is it possible to generate an in-memory image for testing purposes?
Here is my current code:
def test_issue_add_post(self):
url = reverse('issues_issue_add')
image = 'cover.jpg'
data = {
'title': 'Flying Cars',
'cover': image,
}
response = self.client.post(url, data)
self.assertEqual(response.status_code, 302)
To generate a 200x200 test image of solid red:
import Image
size = (200,200)
color = (255,0,0,0)
img = Image.new("RGBA",size,color)
To convert it to a file-like object, then:
import StringIO
f = StringIO.StringIO(img.tostring())
http://effbot.org/imagingbook/image.htm
Jason's accepted answer is not working for me in Django 1.5
Assuming the generated file is to be saved to a model's ImageField from within a unit test, I needed to take it a step further by creating a ContentFile to get it to work:
from PIL import Image
from StringIO import StringIO
from django.core.files.base import ContentFile
image_file = StringIO()
image = Image.new('RGBA', size=(50,50), color=(256,0,0))
image.save(image_file, 'png')
image_file.seek(0)
django_friendly_file = ContentFile(image_file.read(), 'test.png')
So if client.post is expecting a file like object, you could create an example image (if you want to visually check result after tests) or just make a 1px png and read it out from console
open('1px.png', 'rb').read()
which in my case dumped out
image_data = '\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x01\x00\x00\x00\x01\x08\x02\x00\x00\x00\x90wS\xde\x00\x00\x00\x01sRGB\x00\xae\xce\x1c\xe9\x00\x00\x00\tpHYs\x00\x00\x0b\x13\x00\x00\x0b\x13\x01\x00\x9a\x9c\x18\x00\x00\x00\x07tIME\x07\xdb\x0c\x17\x020;\xd1\xda\xcf\xd2\x00\x00\x00\x0cIDAT\x08\xd7c\xf8\xff\xff?\x00\x05\xfe\x02\xfe\xdc\xccY\xe7\x00\x00\x00\x00IEND\xaeB`\x82'
then you can use StringIO which acts as a file like object, so above, image would be
from StringIO import StringIO
def test_issue_add_post(self):
...
image = StringIO(image_data)
...
and you'll have a file like object with the image data
In Python 3
from io import BytesIO
from PIL import Image
image = Image.new('RGBA', size=(50, 50), color=(155, 0, 0))
file = BytesIO(image.tobytes())
file.name = 'test.png'
file.seek(0)
# + + + django_friendly_file = ContentFile(file.read(), 'test.png') # year 2019, django 2.2.1 -works
Thanks to help from Eduardo, I was able to get a working solution.
from StringIO import StringIO
import Image
file = StringIO()
image = Image.new("RGBA", size=(50,50), color=(256,0,0))
image.save(file, 'png')
file.name = 'test.png'
file.seek(0)
Have you used the PIL module? It lets you manipulate images - and should allow creation as well.
In fact, here's a blog entry with some code that does it
http://bradmontgomery.blogspot.com/2008/07/django-generating-image-with-pil.html
Dont know whether you test machine has an internet connection, but you could also pull down random images from google to vary the test data?

Categories