How can I publish PIL image binary through ROS without OpenCV? - python

I'm currently trying to write a ROS Publisher/Subscriber setup that passes image binary opened by PIL. I'd like to not have to use OpenCV due to operating restrictions, and I was wondering if there was a way to do so. This is my current code:
#!/usr/bin/env python
import rospy
from PIL import Image
from sensor_msgs.msg import Image as sensorImage
from rospy.numpy_msg import numpy_msg
import numpy
def talker():
pub = rospy.Publisher('image_stream', numpy_msg(sensorImage), queue_size=10)
rospy.init_node('image_publisher', anonymous=False)
rate = rospy.Rate(0.5)
while not rospy.is_shutdown():
im = numpy.array(Image.open('test.jpg'))
pub.publish(im)
rate.sleep()
if __name__ == '__main__'
try:
talker()
except ROSInterruptException:
pass
which on pub.publish(im) attempt throws:
TypeError: Invalid number of arguments, args should be ['header', 'height', 'width', 'encoding', 'is_bigendian', 'step', 'data'] args are (array([[[***array data here***]]], dtype=uint8),)
How would I transform the image into the right form, or is there a conversion method/different message type that supports just sending raw binary over the ROS connection?
Thanks

Indeed Mark Setchell's answer works perfectly (ignoring the alpha channel in this example):
#!/usr/bin/env python
import rospy
import urllib2 # for downloading an example image
from PIL import Image
from sensor_msgs.msg import Image as SensorImage
import numpy as np
if __name__ == '__main__':
pub = rospy.Publisher('/image', SensorImage, queue_size=10)
rospy.init_node('image_publisher')
im = Image.open(urllib2.urlopen('https://cdn.sstatic.net/Sites/stackoverflow/Img/apple-touch-icon.png'))
im = im.convert('RGB')
msg = SensorImage()
msg.header.stamp = rospy.Time.now()
msg.height = im.height
msg.width = im.width
msg.encoding = "rgb8"
msg.is_bigendian = False
msg.step = 3 * im.width
msg.data = np.array(im).tobytes()
pub.publish(msg)

I don't know anything about ROS, but I use PIL a lot, so if someone else knows better, please ping me and I will delete this "best guess" answer.
So, it seems you need to make something like this from a PIL Image. So you need:
'header',
'height',
'width',
'encoding',
'is_bigendian',
'step',
'data'
So, assuming you do this:
im = Image.open('test.jpg')
you should be able to use:
something you'll need to work out
im.height from PIL Image
im.width from PIL Image
probably const std::string RGB8 = "rgb8"
probably irrelevant because data is 8-bit
probably im.width * 3 as it's 3 bytes per pixel RGB
np.array(im).tobytes()
Before anyone marks this answer down, nobody said answers have to be complete - they can just be "hopefully helpful"!
Note that if your input image is PNG format, you should check im.mode and if it is "P" (i.e. palette mode) immediately run:
im = im.convert('RGB')
to make sure it is 3-channel RGB.
Note that if your input image is PNG format and contains an alpha channel, you should change the encoding to "rgba8" and set step = im.width * 4.

Related

Correctly converting base64 bytes into string and displaying in cv2.imshow

I am struggling on finding the solution for this:
I'm trying to create an image stream system where i can get all the frames and pass them through a neural network, but somehow I've not managed to get properly base64 image strings from my functions below.
The provided code works perfectly if i just call the decoded image from streaming instead of passing it through my functions where i convert to base64 and read them in memory and make cv2 show them properly.
My server code functions responsible to convert and decode base64 are described below:
Convert image object from stream into base64 BYTES and convert to one STRING (this is working as intended)
def convertImgBase64(image):
try:
imgString = base64.b64encode(image).decode('utf-8')
print('convertida com sucesso')
return imgString
except os.error as err :
print(f"Erro:'{err}'")
Base64 decoder that should convert to a readable cv2 compatible frame (Here is where the error begins):
def readb64(base64_string):
storage = '/home/caio/Desktop/img/'
try:
sbuf = BytesIO()
sbuf.write(base64.b64decode(str(base64_string)))
pimg = im.open(sbuf)
out = open('arq.jpeg', 'wb')
out.write(sbuf.read())
out.close()
print('leu string b64')
return cv2.cvtColor(np.array(pimg), cv2.COLOR_RGB2BGR)
except os.error as err :
print(f"Erro:'{err}'")
This is the current server i am building, but before proceeding i need to accomplish the frame capture correctly.
from io import BytesIO, StringIO
import numpy as np
import cv2
from imutils.video import FPS
import imagezmq
import base64
import darknet
import os
from PIL import Image as im
from numpy import asarray
from time import sleep
#imagezmq protocol receiver from client
image_hub = imagezmq.ImageHub()
def convertImgBase64(image):
try:
imgString = base64.b64encode(image).decode('utf-8')
return imgString
except os.error as err :
print(f"Error:'{err}'")
def readb64(base64_string):
try:
sbuf = BytesIO()
sbuf.write(base64.b64decode(str(base64_string)))
pimg = im.open(sbuf)
return cv2.cvtColor(np.array(pimg), cv2.COLOR_RGB2BGR)
except os.error as err :
print(f"Error:'{err}'")
def capture_img():
while True:
camera, jpg_buffer = image_hub.recv_jpg()
buffer = np.frombuffer(jpg_buffer, dtype='uint8')
imagedecoder = cv2.imdecode(buffer, cv2.IMREAD_COLOR)
img = im.fromarray(imagedecoder)
try:
string = convertImgBase64(imagedecoder)
cvimg = readb64(string)
#cv2.imshow(camera, cvimg) this is the line where its not working!
except os.error as err :
print(f"Error:'{err}'")
cv2.imshow(camera, imagedecoder)
cv2.waitKey(1) #cv2 wont work without this
image_hub.send_reply(b'OK') #imageZMQ needs acknowledge that its ok
Client code (raspberry pi code) is given below:
import sys
import socket
import time
import cv2
from imutils.video import VideoStream
import imagezmq
import argparse
# construct the argument parser and parse the arguments
ap = argparse.ArgumentParser()
ap.add_argument("-s", "--server-ip", required=True,
help="ip address of the server to which the client will connect")
args = vars(ap.parse_args())
# initialize the ImageSender object with the socket address of the
# server
sender = imagezmq.ImageSender(connect_to="tcp://{}:5555".format(
args["server_ip"]))
# use either of the formats below to specifiy address of display computer
# sender = imagezmq.ImageSender(connect_to='tcp://192.168.1.190:5555')
rpi_name = socket.gethostname() # send RPi hostname with each image
vs = VideoStream(usePiCamera=True, resolution=(800, 600)).start()
time.sleep(2.0) # allow camera sensor to warm up
jpeg_quality = 95 # 0 to 100, higher is better quality, 95 is cv2 default
while True: # send images as stream until Ctrl-C
image = vs.read()
ret_code, jpg_buffer = cv2.imencode(
".jpg", image, [int(cv2.IMWRITE_JPEG_QUALITY), jpeg_quality])
sender.send_jpg(rpi_name, jpg_buffer)
My error output now is like:
I have been trying solution from here and here
If you would know another better way to pass an Image Object that i can use to process inside yolo/darknet neural network it could be awesome!!
Thanks!
The answers provided by #Christoph Rackwitz are correct. The design of ImageZMQ is to send and receive OpenCV images WITHOUT any base64 encoding. The ImageSender class sends OpenCV images. The ImageHub class receives OpenCV images. Optionally, ImageZMQ can send a jpg buffer (as your Raspberry Pi client code is doing).
Your Raspberry Pi client code is based on the ImageZMQ "send jpg" example.
Your server code should therefore use the matching ImageZMQ "receive jpg" example.
The essence of the ImageZMQ "receive jpg" example code is:
import numpy as np
import cv2
import imagezmq
image_hub = imagezmq.ImageHub()
while True: # show streamed images until Ctrl-C
rpi_name, jpg_buffer = image_hub.recv_jpg()
image = cv2.imdecode(np.frombuffer(jpg_buffer, dtype='uint8'), -1)
# see opencv docs for info on -1 parameter
cv2.imshow(rpi_name, image) # 1 window for each RPi
cv2.waitKey(1)
image_hub.send_reply(b'OK')
No base64 decoding required. The variable image already contains an OpenCV image. (FYI, I am the author of ImageZMQ)

svg 2 png without saving [duplicate]

How do I convert an svg to png, in Python? I am storing the svg in an instance of StringIO. Should I use the pyCairo library? How do I write that code?
Here is what I did using cairosvg:
from cairosvg import svg2png
svg_code = """
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="#000" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<circle cx="12" cy="12" r="10"/>
<line x1="12" y1="8" x2="12" y2="12"/>
<line x1="12" y1="16" x2="12" y2="16"/>
</svg>
"""
svg2png(bytestring=svg_code,write_to='output.png')
And it works like a charm!
See more: cairosvg document
The answer is "pyrsvg" - a Python binding for librsvg.
There is an Ubuntu python-rsvg package providing it. Searching Google for its name is poor because its source code seems to be contained inside the "gnome-python-desktop" Gnome project GIT repository.
I made a minimalist "hello world" that renders SVG to a cairo
surface and writes it to disk:
import cairo
import rsvg
img = cairo.ImageSurface(cairo.FORMAT_ARGB32, 640,480)
ctx = cairo.Context(img)
## handle = rsvg.Handle(<svg filename>)
# or, for in memory SVG data:
handle= rsvg.Handle(None, str(<svg data>))
handle.render_cairo(ctx)
img.write_to_png("svg.png")
Update: as of 2014 the needed package for Fedora Linux distribution is: gnome-python2-rsvg. The above snippet listing still works as-is.
Install Inkscape and call it as command line:
${INKSCAPE_PATH} -z -f ${source_svg} -w ${width} -j -e ${dest_png}
You can also snap specific rectangular area only using parameter -j, e.g. co-ordinate "0:125:451:217"
${INKSCAPE_PATH} -z -f ${source_svg} -w ${width} -j -a ${coordinates} -e ${dest_png}
If you want to show only one object in the SVG file, you can specify the parameter -i with the object id that you have setup in the SVG. It hides everything else.
${INKSCAPE_PATH} -z -f ${source_svg} -w ${width} -i ${object} -j -a ${coordinates} -e ${dest_png}
I'm using Wand-py (an implementation of the Wand wrapper around ImageMagick) to import some pretty advanced SVGs and so far have seen great results! This is all the code it takes:
with wand.image.Image( blob=svg_file.read(), format="svg" ) as image:
png_image = image.make_blob("png")
I just discovered this today, and felt like it was worth sharing for anyone else who might straggle across this answer as it's been a while since most of these questions were answered.
NOTE: Technically in testing I discovered you don't even actually have to pass in the format parameter for ImageMagick, so with wand.image.Image( blob=svg_file.read() ) as image: was all that was really needed.
EDIT: From an attempted edit by qris, here's some helpful code that lets you use ImageMagick with an SVG that has a transparent background:
from wand.api import library
import wand.color
import wand.image
with wand.image.Image() as image:
with wand.color.Color('transparent') as background_color:
library.MagickSetBackgroundColor(image.wand,
background_color.resource)
image.read(blob=svg_file.read(), format="svg")
png_image = image.make_blob("png32")
with open(output_filename, "wb") as out:
out.write(png_image)
I did not find any of the answers satisfactory. All the mentioned libraries have some problem or the other like Cairo dropping support for python 3.6 (they dropped Python 2 support some 3 years ago!). Also, installing the mentioned libraries on the Mac was a pain.
Finally, I found the best solution was svglib + reportlab. Both installed without a hitch using pip and first call to convert from svg to png worked beautifully! Very happy with the solution.
Just 2 commands do the trick:
from svglib.svglib import svg2rlg
from reportlab.graphics import renderPM
drawing = svg2rlg("my.svg")
renderPM.drawToFile(drawing, "my.png", fmt="PNG")
Are there any limitations with these I should be aware of?
Try this: http://cairosvg.org/
The site says:
CairoSVG is written in pure python and only depends on Pycairo. It is
known to work on Python 2.6 and 2.7.
Update November 25, 2016:
2.0.0 is a new major version, its changelog includes:
Drop Python 2 support
Another solution I've just found here How to render a scaled SVG to a QImage?
from PySide.QtSvg import *
from PySide.QtGui import *
def convertSvgToPng(svgFilepath,pngFilepath,width):
r=QSvgRenderer(svgFilepath)
height=r.defaultSize().height()*width/r.defaultSize().width()
i=QImage(width,height,QImage.Format_ARGB32)
p=QPainter(i)
r.render(p)
i.save(pngFilepath)
p.end()
PySide is easily installed from a binary package in Windows (and I use it for other things so is easy for me).
However, I noticed a few problems when converting country flags from Wikimedia, so perhaps not the most robust svg parser/renderer.
A little extension on the answer of jsbueno:
#!/usr/bin/env python
import cairo
import rsvg
from xml.dom import minidom
def convert_svg_to_png(svg_file, output_file):
# Get the svg files content
with open(svg_file) as f:
svg_data = f.read()
# Get the width / height inside of the SVG
doc = minidom.parse(svg_file)
width = int([path.getAttribute('width') for path
in doc.getElementsByTagName('svg')][0])
height = int([path.getAttribute('height') for path
in doc.getElementsByTagName('svg')][0])
doc.unlink()
# create the png
img = cairo.ImageSurface(cairo.FORMAT_ARGB32, width, height)
ctx = cairo.Context(img)
handler = rsvg.Handle(None, str(svg_data))
handler.render_cairo(ctx)
img.write_to_png(output_file)
if __name__ == '__main__':
from argparse import ArgumentParser
parser = ArgumentParser()
parser.add_argument("-f", "--file", dest="svg_file",
help="SVG input file", metavar="FILE")
parser.add_argument("-o", "--output", dest="output", default="svg.png",
help="PNG output file", metavar="FILE")
args = parser.parse_args()
convert_svg_to_png(args.svg_file, args.output)
Here is a another solution without using rsvg(which is currently not available for windows).Only install cairosvg using pip install CairoSVG
svg2png.py
from cairosvg import svg2png
svg_code = open("input.svg", 'rt').read()
svg2png(bytestring=svg_code,write_to='output.png')
SVG scaling and PNG rendering
Using pycairo and librsvg I was able to achieve SVG scaling and rendering to a bitmap. Assuming your SVG is not exactly 256x256 pixels, the desired output, you can read in the SVG to a Cairo context using rsvg and then scale it and write to a PNG.
main.py
import cairo
import rsvg
width = 256
height = 256
svg = rsvg.Handle('cool.svg')
unscaled_width = svg.props.width
unscaled_height = svg.props.height
svg_surface = cairo.SVGSurface(None, width, height)
svg_context = cairo.Context(svg_surface)
svg_context.save()
svg_context.scale(width/unscaled_width, height/unscaled_height)
svg.render_cairo(svg_context)
svg_context.restore()
svg_surface.write_to_png('cool.png')
RSVG C binding
From the Cario website with some minor modification. Also a good example of how to call a C-library from Python
from ctypes import CDLL, POINTER, Structure, byref, util
from ctypes import c_bool, c_byte, c_void_p, c_int, c_double, c_uint32, c_char_p
class _PycairoContext(Structure):
_fields_ = [("PyObject_HEAD", c_byte * object.__basicsize__),
("ctx", c_void_p),
("base", c_void_p)]
class _RsvgProps(Structure):
_fields_ = [("width", c_int), ("height", c_int),
("em", c_double), ("ex", c_double)]
class _GError(Structure):
_fields_ = [("domain", c_uint32), ("code", c_int), ("message", c_char_p)]
def _load_rsvg(rsvg_lib_path=None, gobject_lib_path=None):
if rsvg_lib_path is None:
rsvg_lib_path = util.find_library('rsvg-2')
if gobject_lib_path is None:
gobject_lib_path = util.find_library('gobject-2.0')
l = CDLL(rsvg_lib_path)
g = CDLL(gobject_lib_path)
g.g_type_init()
l.rsvg_handle_new_from_file.argtypes = [c_char_p, POINTER(POINTER(_GError))]
l.rsvg_handle_new_from_file.restype = c_void_p
l.rsvg_handle_render_cairo.argtypes = [c_void_p, c_void_p]
l.rsvg_handle_render_cairo.restype = c_bool
l.rsvg_handle_get_dimensions.argtypes = [c_void_p, POINTER(_RsvgProps)]
return l
_librsvg = _load_rsvg()
class Handle(object):
def __init__(self, path):
lib = _librsvg
err = POINTER(_GError)()
self.handle = lib.rsvg_handle_new_from_file(path.encode(), byref(err))
if self.handle is None:
gerr = err.contents
raise Exception(gerr.message)
self.props = _RsvgProps()
lib.rsvg_handle_get_dimensions(self.handle, byref(self.props))
def get_dimension_data(self):
svgDim = self.RsvgDimensionData()
_librsvg.rsvg_handle_get_dimensions(self.handle, byref(svgDim))
return (svgDim.width, svgDim.height)
def render_cairo(self, ctx):
"""Returns True is drawing succeeded."""
z = _PycairoContext.from_address(id(ctx))
return _librsvg.rsvg_handle_render_cairo(self.handle, z.ctx)
Here is an approach where Inkscape is called by Python.
Note that it suppresses certain crufty output that Inkscape writes to the console (specifically, stderr and stdout) during normal error-free operation. The output is captured in two string variables, out and err.
import subprocess # May want to use subprocess32 instead
cmd_list = [ '/full/path/to/inkscape', '-z',
'--export-png', '/path/to/output.png',
'--export-width', 100,
'--export-height', 100,
'/path/to/input.svg' ]
# Invoke the command. Divert output that normally goes to stdout or stderr.
p = subprocess.Popen( cmd_list, stdout=subprocess.PIPE, stderr=subprocess.PIPE )
# Below, < out > and < err > are strings or < None >, derived from stdout and stderr.
out, err = p.communicate() # Waits for process to terminate
# Maybe do something with stdout output that is in < out >
# Maybe do something with stderr output that is in < err >
if p.returncode:
raise Exception( 'Inkscape error: ' + (err or '?') )
For example, when running a particular job on my Mac OS system, out ended up being:
Background RRGGBBAA: ffffff00
Area 0:0:339:339 exported to 100 x 100 pixels (72.4584 dpi)
Bitmap saved as: /path/to/output.png
(The input svg file had a size of 339 by 339 pixels.)
Try this python script:
Don't forget to install cairosvg: pip3 install cairosvg
#!/usr/bin/env python3
import os
import cairosvg
for file in os.listdir('.'):
if os.path.isfile(file) and file.endswith(".svg"):
name = file.split('.svg')[0]
cairosvg.svg2png(url=name+'.svg',write_to=name+'.png')
Try using Gtk.Image and Gdk.Pixbuf
import gi
gi.require_version('Gtk', '3.0')
gi.require_version('Gdk', '3.0')
from gi.repository import Gdk, Gtk
from PIL import Image
image = Gtk.Image()
image.set_from_file("path/to/image.svg")
pb = image.get_pixbuf()
pb.savev("path/to/convented/image.jpeg","jpeg",[],[])
im = Image.open("path/to/convented/image.jpeg")
pix = im.load()
print(pix[1,1])
Actually, I did not want to be dependent of anything else but Python (Cairo, Ink.., etc.)
My requirements were to be as simple as possible, at most, a simple pip install "savior" would suffice, that's why any of those above didn't suit for me.
I came through this (going further than Stackoverflow on the research).
https://www.tutorialexample.com/best-practice-to-python-convert-svg-to-png-with-svglib-python-tutorial/
Looks good, so far. So I share it in case anyone in the same situation.
All the answer's here are great, but I figure I'll mention that I have made a simple library that loads SVG's files as pillow Image instances which can then be exported. It uses inkscape like in blj's answer, but renders to stdout so that no temporary files are made. There's some basic usage stuff in the README.
https://github.com/jlwoolf/pillow-svg
EDIT:
As suggested, here's a brief explanation, since the link could become invalid:
The library uses inkscape's command line interface to convert the image to a png of a specific size or dpi using the python subprocess library. By setting --export-filename to -, inkscape redirects the output to the stdout. The first two lines are discarded, and the remaining output is passed to PIL.Image.open, converting it to pillow image instance.
import subprocess
from PIL import Image
options = ["inkscape", "--export-filename=-", "--export-type=png", "file.svg"]
pipe = subprocess.Popen(options, stdout=subprocess.PIPE)
pipe.stdout.readline()
pipe.stdout.readline()
img = Image.open(pipe.stdout)
From there you can do whatever pillow image operations you need (like export as a jpg, resize, crop, etc).
EDIT 2:
Just added support for skia-python (haven't fully tested it, but seems to work so far). This way you can convert an svg to png with only a single pip install (no need to use inkscape).
Here is an explanation of how the library uses skia-python:
First, the svg file is loaded into a skia.SVGDOM. From there you can grab the SVGDOM's dimensions, using containerSize. Then a skia.Surface of the desired image output size is made. The canvas is scaled to fit the svg to the surface, and then the svg is rendered. From there, an image snapshot can be made, which can then be fed to PIL.Image.open.
import skia
from PIL import Image
skia_stream = skia.Stream.MakeFromFile("file.svg")
skia_svg = skia.SVGDOM.MakeFromStream(skia_stream)
svg_width, svg_height = skia_svg.containerSize()
surface_width, surface_height = 512, 512
surface = skia.Surface(surface_width, surface_height)
with surface as canvas:
canvas.scale(surface_width / svg_width, surface_height / svg_height)
skia_svg.render(canvas)
with io.BytesIO(surface.makeImageSnapshot().encodeToData()) as f:
img = Image.open(f)
img.load()
Edit 3:
I have fleshed out the library much much more. There is a command line utility now for easy svg conversion, along with more documentation explaining usage. Hope it helps!
Posting my code from this StackOverflow answer. It's a workaround to svglib+reportlib not supporting a transparent background and no scaling (see #sarang's answer and #ualter-jr's answer as well as these Github issues on scaling not working and this one on transparency)
This uses pyMuPDF to render an intermediate pdf from reportlab to PNG.
The big advantage is that it doesn't need any external libraries as pymupdf comes with precompiled wheels for Windows, Linux and MacOS.
The whole thing is as easy as
pip install pymupdf svglib
and then executing the following lines
import fitz
from svglib import svglib
from reportlab.graphics import renderPDF
# Convert svg to pdf in memory with svglib+reportlab
# directly rendering to png does not support transparency nor scaling
drawing = svglib.svg2rlg(path="input.svg")
pdf = renderPDF.drawToString(drawing)
# Open pdf with fitz (pyMuPdf) to convert to PNG
doc = fitz.Document(stream=pdf)
pix = doc.load_page(0).get_pixmap(alpha=True, dpi=300)
pix.save("output.png")

Gtk+3 Gdk Pixbuf.new_from_data gives "Segmentation fault (core dumped)" error 139

In my application I have a GtkImage that must show the processed image from a choosed file. So, in the handlers section I have:
import numpy as np
from PIL import Image , ImageDraw
from gi.repository import Gtk, GdkPixbuf
. . .
. . .
def on_fitchooserdialog_response(self, menuitem, data=None):
if data == 1:
self.fitlist = self.fitchooser.get_filenames()
# get data from 1st file:
_, self.data = Getdata(self.fitlist[0])
# convert from Fits to 2D array:
pngarray = Fit2png(self.data)
# rescale:
size = tuple(x/2 for x in pngarray.shape)
im = Image.fromarray(pngarray)
im.thumbnail((size[1],size[0]), Image.BICUBIC)
Up to here, all is OK. If we do:
im.save("../tmp/tmp.png")
pixbuf = GdkPixbuf.Pixbuf.new_from_file('../tmp/tmp.png')
self.imagen.set_property("pixbuf", pixbuf)
the expected image is pasted on the GtkImage widget.
But that is the ugly way, isnt it?
So Im trying:
im = im.convert("RGB")
arr = np.array(im).flatten()
pixbuf = GdkPixbuf.Pixbuf.new_from_data(arr,
GdkPixbuf.Colorspace.RGB, False, 8, size[1], size[0], 3*size[1])
But the result is "Error 139, Segmentation fault (core dumped)"
What am I missing?
This seems to be related to this gdk bug: https://bugzilla.gnome.org/show_bug.cgi?id=721497
Basically it is a use after free bug in the python wrapper of gdk which can result in image distortions and/or segfaults as it did for you.
See: https://stackoverflow.com/a/24070152/3528174
You can find an example of such image distortions in this question: How to correctly covert 3d array into continguous rgb bytes

paint decoded jpeg to cairo surface

I am trying to paint a decoded jpeg to a Cairo surface...
However I am kinda stuck and I have no idea how to move forward from
import cairo
import Image
path_to_jpeg = "/home/seif/Pictures/prw.jpg"
surface = cairo.PDFSurface ("out.pdf", 1000, 1000)
ctx = cairo.Context (surface)
image = cairo.ImageSurface(cairo.FORMAT_ARGB32, 1000, 1000)
dt = Image.open(path_to_jpeg)
dimage = dt.load()
Any help would be very appreciated...
this should do the trick. You have to convert the image to a png first it seems to be the only format that you can create surfaces with. thats what a good chunk of the code below is doing. I recommend you look at this question which helped me a great deal in creating the code below.
import Image, StringIO
from cairo import PDFSurface, Context, ImageSurface
pdf = PDFSurface("out.pdf", 1000, 1000)
cr = Context(pdf)
im = Image.open("/home/seif/Pictures/prw.jpg")
buffer = StringIO.StringIO()
im.save(buffer, format="PNG")
buffer.seek(0)
cr.save()
cr.set_source_surface(ImageSurface.create_from_png(buffer))
cr.paint()
If using cairocffi instead of pycairo (the API is compatible), the cairocffi.pixbuf module provides integration with GDK-PixBuf for loading all kinds of images formats into cairo.
https://cairocffi.readthedocs.io/en/stable/pixbuf.html
Example:
from cairocffi import ImageSurface, pixbuf
def get_image(image_data: bytes) -> ImageSurface:
return pixbuf.decode_to_image_surface(image_data)[0]
def load_image(image_file_path: str) -> ImageSurface:
with open(str(image_file_path), 'rb') as file:
return get_image(file.read())

Take screenshot in Python on Mac OS X

ImageGrab from PIL would have been ideal. I'm looking for similar functionality, specifically the ability to define the screenshot's bounding box. I've been looking for a library to do so on Mac OS X but haven't had any luck. I also wasn't able to find any sample code to do it (maybe pyobjc?).
While not exactly what you want, in a pinch you might just use:
os.system("screencapture screen.png")
Then open that image with the Image module. I'm sure a better solution exists though.
Here's how to capture and save a screenshot with PyObjC, based on my answer here
You can capture the entire screen, or specify a region to capture. If you don't need to do that, I'd recommend just calling the screencapture command (more features, more robust, and quicker - the initial PyObjC import alone can take around a second)
import Quartz
import LaunchServices
from Cocoa import NSURL
import Quartz.CoreGraphics as CG
def screenshot(path, region = None):
"""region should be a CGRect, something like:
>>> import Quartz.CoreGraphics as CG
>>> region = CG.CGRectMake(0, 0, 100, 100)
>>> sp = ScreenPixel()
>>> sp.capture(region=region)
The default region is CG.CGRectInfinite (captures the full screen)
"""
if region is None:
region = CG.CGRectInfinite
# Create screenshot as CGImage
image = CG.CGWindowListCreateImage(
region,
CG.kCGWindowListOptionOnScreenOnly,
CG.kCGNullWindowID,
CG.kCGWindowImageDefault)
dpi = 72 # FIXME: Should query this from somewhere, e.g for retina displays
url = NSURL.fileURLWithPath_(path)
dest = Quartz.CGImageDestinationCreateWithURL(
url,
LaunchServices.kUTTypePNG, # file type
1, # 1 image in file
None
)
properties = {
Quartz.kCGImagePropertyDPIWidth: dpi,
Quartz.kCGImagePropertyDPIHeight: dpi,
}
# Add the image to the destination, characterizing the image with
# the properties dictionary.
Quartz.CGImageDestinationAddImage(dest, image, properties)
# When all the images (only 1 in this example) are added to the destination,
# finalize the CGImageDestination object.
Quartz.CGImageDestinationFinalize(dest)
if __name__ == '__main__':
# Capture full screen
screenshot("/tmp/testscreenshot_full.png")
# Capture region (100x100 box from top-left)
region = CG.CGRectMake(0, 0, 100, 100)
screenshot("/tmp/testscreenshot_partial.png", region=region)
While I do understand that this thread is close to five years old now, I'm answering this in the hope that it helps people in future.
Here's what worked for me, based on an answer in this thread (credit goes to ponty ) : Take a screenshot via a python script. [Linux]
https://github.com/ponty/pyscreenshot
Install:
easy_install pyscreenshot
Example:
import pyscreenshot
# fullscreen
screenshot=pyscreenshot.grab()
screenshot.show()
# part of the screen
screenshot=pyscreenshot.grab(bbox=(10,10,500,500))
screenshot.show()
# save to file
pyscreenshot.grab_to_file('screenshot.png')
Pillow has since added ImageGrab support for macOS!
However it's not in v2.9 (as of right now the latest) so I just added this file to my local module.
The code is as below:
#
# The Python Imaging Library
# $Id$
#
# screen grabber (macOS and Windows only)
#
# History:
# 2001-04-26 fl created
# 2001-09-17 fl use builtin driver, if present
# 2002-11-19 fl added grabclipboard support
#
# Copyright (c) 2001-2002 by Secret Labs AB
# Copyright (c) 2001-2002 by Fredrik Lundh
#
# See the README file for information on usage and redistribution.
#
from . import Image
import sys
if sys.platform not in ["win32", "darwin"]:
raise ImportError("ImageGrab is macOS and Windows only")
if sys.platform == "win32":
grabber = Image.core.grabscreen
elif sys.platform == "darwin":
import os
import tempfile
import subprocess
def grab(bbox=None):
if sys.platform == "darwin":
fh, filepath = tempfile.mkstemp('.png')
os.close(fh)
subprocess.call(['screencapture', '-x', filepath])
im = Image.open(filepath)
im.load()
os.unlink(filepath)
else:
size, data = grabber()
im = Image.frombytes(
"RGB", size, data,
# RGB, 32-bit line padding, origin lower left corner
"raw", "BGR", (size[0]*3 + 3) & -4, -1
)
if bbox:
im = im.crop(bbox)
return im
def grabclipboard():
if sys.platform == "darwin":
fh, filepath = tempfile.mkstemp('.jpg')
os.close(fh)
commands = [
"set theFile to (open for access POSIX file \""+filepath+"\" with write permission)",
"try",
"write (the clipboard as JPEG picture) to theFile",
"end try",
"close access theFile"
]
script = ["osascript"]
for command in commands:
script += ["-e", command]
subprocess.call(script)
im = None
if os.stat(filepath).st_size != 0:
im = Image.open(filepath)
im.load()
os.unlink(filepath)
return im
else:
debug = 0 # temporary interface
data = Image.core.grabclipboard(debug)
if isinstance(data, bytes):
from . import BmpImagePlugin
import io
return BmpImagePlugin.DibImageFile(io.BytesIO(data))
return data
from subprocess import call
import time
from time import gmtime, strftime
# Take screenshot every 10 seconds and store in the folder where the
# code file is present on disk. To stop the script press Cmd+Z/C
def take_screen_shot():
# save screen shots where
call(["screencapture", "Screenshot" + strftime("%Y-%m-%d %H:%M:%S", gmtime()) + ".jpg"])
def build_screen_shot_base():
while True:
take_screen_shot()
time.sleep(10)
build_screen_shot_base()
I found that using webkit2png was the most convenient solution for me on OS X.
brew install webkit2png
webkit2png http://stackoverflow.com

Categories

Resources