Python strip all GPS metadata from image - image

Is there a simple way, on windows and linux (Ubuntu Linux and Windows 7, both 64-bit with Python 2.7), to strip all (not just EXIF) GPS metadata on all images in a directory, and leave the rest of the metadata intact? It only needs to work for JPGs and PNGs.

It's not exactly what I want, but I wrote a script using PyExiv2 which obscures some GPS EXIF data. Here is the script.
#!/usr/bin/python2.7
from pyexiv2 import ImageMetadata, ExifTag
from fractions import Fraction
import argparse, os
parser = argparse.ArgumentParser(description='Strip GPS metadata.')
parser.add_argument('dir', metavar='DIRECTORY',
help='The directory to process.')
args = parser.parse_args()
files = os.listdir(args.dir)
for tiname in files:
iname = args.dir+tiname
image = ImageMetadata(iname)
image.read()
image["Exif.GPSInfo.GPSLatitude"] = Fraction(1,1)
image["Exif.GPSInfo.GPSLongitude"] = Fraction(1,1)
image.write()
EDIT: This apparently (at least on windows) does not strip the latitude and longitude.

Related

How to effectively convert a POSIX path to Windows path with Python in Cygwin?

Problem
Imagine you are writing a Python script which runs on cygwin and calls an external C# executable which requires as input a path.
Assume you cannot change the C# executable in any way.
As you send the path you want to the executable, it rejects all cygwin paths.
So if you pass the path /cygdrive/c/location/of/file.html as a POSIX path, it will fail as the executable requires a Windows path like C:\location\of\file.html
Example:
Message location = os.path.dirname(os.path.realpath(__file__))
os.system('./cSharpScript.exe ' + message_location)
Will result in:
File for the content (/cygdrive/c/location/of/file.html) not found.
Things I've tried so far:
PATH = /cygdrive/c/location/of/file.html
1) path = PATH.replace('/','\\')
Result: File for the content (cygdriveclocationoffile.html) not found.
2) path = os.path.abspath(PATH)
Result: File for the content (/cygdrive/c/location/of/file.html) not found.
os.path.realpath has the same results
I'm probably going in a completely wrong direction with my solutions so far... How would you handle it?
According to [Cygwin]: cygpath:
cygpath - Convert Unix and Windows format paths, or output system path information
...
-w, --windows print Windows form of NAMEs (C:\WINNT)
Example:
[cfati#cfati-5510-0:/cygdrive/e/Work/Dev/StackOverflow/q054237800]> cygpath.exe -w /cygdrive/c/location/of/file.html
C:\location\of\file.html
Translated into Python (this is a rough version, only for demonstrating purposes):
>>> import subprocess
>>>
>>>
>>> def get_win_path(cyg_path):
... return subprocess.check_output(["cygpath", "-w", cyg_path]).strip(b"\n").decode()
...
>>>
>>> print(get_win_path("/cygdrive/c/location/of/file.html"))
C:\location\of\file.html

PIL Issue, OSError: cannot open resource

I'm attempting to write a program that places text onto an image, I'm trying to get my head round PIL and have run into the error: OSError: cannot open resource. This is my first python program so apologies if the error is obvious.
from PIL import Image
from PIL import ImageDraw
from PIL import ImageFont
im = Image.open("example.jpg")
font_type = ImageFont.truetype("Arial.ttf", 18)
draw = ImageDraw.Draw(im)
draw.text(xy=(50, 50), text= "Text One", fill =(255,69,0), font = font_type)
im.show()
I get the error:
Traceback (most recent call last):
File "C:\Users\laurence.maskell\Desktop\attempt.py", line 7, in <module>
font_type = ImageFont.truetype("Arial.ttf", 18)
File "C:\Python34\lib\site-packages\PIL\ImageFont.py", line 259, in truetype
return FreeTypeFont(font, size, index, encoding, layout_engine)
File "C:\Python34\lib\site-packages\PIL\ImageFont.py", line 143, in __init__
self.font = core.getfont(font, size, index, encoding,
layout_engine=layout_engine)
OSError: cannot open resource
I fixed the problem by using default font.
font = ImageFont.load_default()
If you just need to put some text (font style is not matter to you) then this might be a simple solution.
font = ImageFont.load_default()
draw = ImageDraw.Draw(pil_img)
draw.text(( 20, 32), "text_string", (255,0,0), font=font)
from PIL import Image, ImageDraw, ImageFont
im = Image.open("mak.png")
font_type = ImageFont.truetype("arial.ttf", 18)
draw = ImageDraw.Draw(im)
draw.text(xy=(120, 120), text= "download font you want to use", fill=(255,69,0), font=font_type)
im.show()
Its "arial.ttf" not "Arial.ttf"
Here is the link to download arial.ttf font.
I have also met this issue on Windows 10 Pro with PIL 5.3.0.
On my machine, the error is caused by non-ASCII font file names. If I change the the font name to only contain ASCII characters, I can open the font without any error.
Edit (2019-07-29): this is indeed a bug with ImageFont.truetype() method and it has been fixed in this pull request.
For Linux I used:
$ locate .ttf
/usr/share/fonts/truetype/ubuntu-font-family/Ubuntu-B.ttf
/usr/share/fonts/truetype/ubuntu-font-family/Ubuntu-BI.ttf
/usr/share/fonts/truetype/ubuntu-font-family/Ubuntu-C.ttf
/usr/share/fonts/truetype/ubuntu-font-family/Ubuntu-L.ttf
/usr/share/fonts/truetype/ubuntu-font-family/Ubuntu-LI.ttf
/usr/share/fonts/truetype/ubuntu-font-family/Ubuntu-M.ttf
/usr/share/fonts/truetype/ubuntu-font-family/Ubuntu-MI.ttf
/usr/share/fonts/truetype/ubuntu-font-family/Ubuntu-R.ttf
/usr/share/fonts/truetype/ubuntu-font-family/Ubuntu-RI.ttf
/usr/share/fonts/truetype/ubuntu-font-family/UbuntuMono-B.ttf
/usr/share/fonts/truetype/ubuntu-font-family/UbuntuMono-BI.ttf
/usr/share/fonts/truetype/ubuntu-font-family/UbuntuMono-R.ttf
/usr/share/fonts/truetype/ubuntu-font-family/UbuntuMono-RI.ttf
It actually returned A LOT MORE than that!
Then I took the python code posted here in Stack Overflow:
PIL: Generating Vertical Gradient Image
Plugged in the font name "Ubuntu-R.ttf" returned by locate:
color_palette = [BLUE, GREEN, RED]
image_w=200
image_h=200
region = Rect(0, 0, image_w, image_h)
imgx, imgy = region.max.x+1, region.max.y+1
image = Image.new("RGB", (imgx, imgy), WHITE)
draw = ImageDraw.Draw(image)
vert_gradient(draw, region, gradient_color, color_palette)
#image.text((40, 80),"No Artwork",(255,255,255))
#font = ImageFont.truetype(r'C:\Users\System-Pc\Desktop\arial.ttf', 40)
#font = ImageFont.load_default()
font = ImageFont.truetype("Ubuntu-R.ttf", int(float(image_w) / 6))
draw.text((int(image_w/12), int(image_h / 2.5)), "No Artwork", \
fill=(0,0,0), font=font)
image.show()
And voila! I now have an image to display when there is no image to display in a music file I'm playing:
I was also facing the same issue. but later found out it was an issue with the slash. So, if anybody else missed this small thing, it's for you.
I had the font inside the font folder. When I tried to import the font using font/<font_name>.ttf, the code couldn't locate it. Replaced / with \ and it could locate the font.
If you are on Windows, by the following method my problem was resolved:
go to the Font folder in C:\Windows\Fonts
Right-click on the font that you want to use and click on "Properties"
go to the Security tab and copy the address of "Object name:"
then place the above path (*****) in the following code:
ImageFont.truetype("*****", int(float(image_w) / 6))
PIL cannot find the specified font. Check that it exists, and that it is named with the exact same case.
You can also try to copy it directly in the project folder.
I think you should move the font to fonts or any folder. I had the same issue on heroku. After i moved the font to fonts directory it works
When I ran into this issue in Linux, I solved by:
Install the .ttf into Linux
Run the code with the actual .tff in ~. For some reason, even with the font installed and the .py running inn another dir, it needed to have the .tff in the home dir.
This issue also crops up in PIL for android. While it is quite possible to copy a font file into the project directory as others have noted, if more than a few android fonts are wanted it may be tidier to add the android font directory to the ImageFont.py search path instead. I altered it like this between lines 870-871:
870 elif sys.platform in ("linux", "linux2"):
if 'ANDROID_BOOTLOGO' in os.environ:
# kludge added for android
dirs += [
"/system/fonts"
]
else:
871 lindirs = os.environ.get("XDG_DATA_DIRS", "")
872 if not lindirs:
... # According to the freedesktop spec, XDG_DATA_DIRS should
# default to /usr/share
lindirs = "/usr/share"
dirs += [os.path.join(lindir, "fonts") for lindir in lindirs.split(":")]
As I have understood, a problem with location
of the file: "Arial.ttf"
As I have understood the Arial.ttf file is with the program, but start is made from other place.
In this case it is necessary to add the Arial.ttf file to a full path:
# __file__ contain full path to the py scrypt
# for python 3.9 and later
import os
path = os.path.dirname(__file__) + '/'
im = Image.open("example.jpg")
font_type = ImageFont.truetype(path + "Arial.ttf", 18)
For older py version see more at:
How do I get the full path of the current file's directory?
It's just that the name of the .ttf file does'nt match with the filename you pass to ImageFont.truetype().
Make sure that the case and spelling are correct. There is no other external factor that could affect merely loading a font from a ttf file.
I solved it simply by doing the following steps:
Created a hidden folder named fonts (.fonts) in the home directory.
Downloaded my desired font from the web. Note: You can download any
font you want by typing "font_name.tiff download" on Google.
Copy and paste the downloaded .ttf file into the .fonts folder.
Copy and paste the new path of your .ttf file in the code as shown below.
im_show = draw_ocr(image, boxes, txts, scores, font_path='/.fonts/simfang.ttf')
That's how it worked for me.
You can follow this link as a visual aid. https://www.youtube.com/watch?v=Q5GO_glHXyQ&t=35s

matplotlib save animation in gif error

I want to save matplotlib animation in gif format.
I succeded to save animation to mp4 format, using code
import matplotlib
matplotlib.use("Agg")
~some codes~
ani = animation.FuncAnimation(fig, draw, update, interval=10, blit=False)
mywriter = animation.FFMpegWriter(fps=60)
ani.save('myanimation.mp4',writer=mywriter)
but if I change myanimation.mp4 to gif format, python makes error
Traceback (most recent call last):
File "C:\Users\Administrator\Desktop\edison\Edison_v4_backup_1\ver5.py", line 164, in <module>
ani.save('demoanimation.gif',writer=mywriter);
File "C:\Python27\lib\site-packages\matplotlib\animation.py", line 718, in save
writer.grab_frame(**savefig_kwargs)
File "C:\Python27\lib\site-packages\matplotlib\animation.py", line 204, in grab_frame
dpi=self.dpi, **savefig_kwargs)
File "C:\Python27\lib\site-packages\matplotlib\figure.py", line 1421, in savefig
self.canvas.print_figure(*args, **kwargs)
File "C:\Python27\lib\site-packages\matplotlib\backend_bases.py", line 2220, in print_figure
**kwargs)
File "C:\Python27\lib\site-packages\matplotlib\backends\backend_agg.py", line 497, in print_raw
renderer._renderer.write_rgba(filename_or_obj)
RuntimeError: Error writing to file
Seeing that I succeded to save in mp4 format, I don't know why it makes error when saving gif format.
This is because matplotlib does not support GIFs without external programs. If you have imagemagick correctly installed and configured, this should work:
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
import matplotlib.animation
import numpy as np
def init_animation():
global line
line, = ax.plot(x, np.zeros_like(x))
ax.set_xlim(0, 2*np.pi)
ax.set_ylim(-1,1)
def animate(i):
line.set_ydata(np.sin(2*np.pi*i / 50)*np.sin(x))
return line,
fig = plt.figure()
ax = fig.add_subplot(111)
x = np.linspace(0, 2*np.pi, 200)
ani = matplotlib.animation.FuncAnimation(fig, animate, init_func=init_animation, frames=50)
ani.save('/tmp/animation.gif', writer='imagemagick', fps=30)
Just a reminder, before you use Matplotlib and ImageMagick to convert images or videos to gif, you need to modify Matplotlib's config and add ImageMagick's path.
The following code will show you the config file path of Matplotlib
import matplotlib
matplotlib.matplotlib_fname()
For me the path is
C:\Anaconda\lib\site-packages\matplotlib\mpl-data\matplotlibrc
Then changing animation.convert_path
#animation.convert_path: 'convert' # Path to ImageMagick's convert binary.
# On Windows use the full path since convert
# is also the name of a system tool.
by adding convert.exe path to it
animation.convert_path: C:\Program Files\ImageMagick-6.9.2-Q16-HDRI\convert.exe
Don't forget to remove the # before animation.convert_path.
After the above modification, Matplotlib and ImageMagick will perfectly work and output the gif file you want.
Hope it helps.
Still had some trouble on Mac OS X, but the answers above pointed me in the right direction.
What I did came down to:
brew install imagemagick
edit the path file on location /etc/path (sudo vim path) and added the location of bin folder of imagemagick, in my case: /usr/local/Cellar/imagemagick/6.9.6-4/bin
changed the settings of matplotlibrc as described above. You can find the location of matplotlibrc by doing in Python: import matplotlib matplotlib.matplotlib_fname()
The settings you need to adjust can be found at the end of the file. I adjusted the following ones (commented out). Please note that those settings are not in 'quotes':
animation.writer : imagemagick
animation.codec : mpeg4
animation.bitrate: -1
animation.frame_format: png
animation.convert_path: convert
DrV's script didn't work for me on Windows 7, even though convert and ffmpeg both are in the system path.
File "C:\Python27\lib\site-packages\matplotlib\backends\backend_agg.py", line 513, in print_raw
renderer._renderer.write_rgba(filename_or_obj)
RuntimeError: Error writing to file
C:\Users>ffmpeg
ffmpeg version N-50911-g9efcfbe Copyright (c) 2000-2013 the FFmpeg developers
...
C:\Users>convert
Version: ImageMagick 6.9.1-1 Q16 x64 2015-03-20 http://www.imagemagick.org
Copyright: Copyright (C) 1999-2015 ImageMagick Studio LLC
...
Editing my matplotlibrc to add the full path to the exe fixed it:
###ANIMATION settings
#animation.html : 'none' # How to display the animation as HTML in
# the IPython notebook. 'html5' uses
# HTML5 video tag.
#animation.writer : ffmpeg # MovieWriter 'backend' to use
#animation.codec : mpeg4 # Codec to use for writing movie
#animation.bitrate: -1 # Controls size/quality tradeoff for movie.
# -1 implies let utility auto-determine
#animation.frame_format: 'png' # Controls frame format used by temp files
animation.ffmpeg_path: C:\Program Files\ImageMagick-6.9.1-Q16\ffmpeg.exe # Path to ffmpeg binary. Without full path
# $PATH is searched
#animation.ffmpeg_args: '' # Additional arguments to pass to ffmpeg
#animation.avconv_path: 'avconv' # Path to avconv binary. Without full path
# $PATH is searched
#animation.avconv_args: '' # Additional arguments to pass to avconv
#animation.mencoder_path: 'mencoder'
# Path to mencoder binary. Without full path
# $PATH is searched
#animation.mencoder_args: '' # Additional arguments to pass to mencoder
animation.convert_path: C:\Program Files\ImageMagick-6.9.1-Q16\convert.exe # Path to ImageMagick's convert binary.
# On Windows use the full path since convert
# is also the name of a system tool.
I have just tried to install ffmpeg, didn't help. Then I tried to install imagemagick - failed. So I resorted to looping animation and recording it with screen scraper . I use ScreenToGif.

Opening Blender (a program) from a specific filepath, relative paths, Unix executable

In my previous question, Open a file from a specific program from python, I found out how to use subprocess in order to open a program (Blender) — well, a specific .blend file — from a specific file path with this code.
import os
import subprocess
path = os.getcwd()
os.system("cd path/")
subprocess.check_call(["open", "-a", os.path.join(path, "blender.app"),"Import_mhx.blend"])
With the help of a guy at a forum, I wanted to use relative paths inside the .blend file, so I changed the code in this way (for Windows)
import os
import subprocess
# This should be the full path to your Blender executable.
blenderPath = "/cygdrive/c/Program Files/Blender Foundation/blender-2.62-release-windows32/blender.exe"
# This is the directory that you want to be your "current" directory when Blender starts
path1 = "/Users/user/Desktop/scenario/Blender"
# This makes makes it so your script is currently based at "path1"
os.chdir(path1)
subprocess.check_call([blenderPath, "Import_mhx.blend"])
and for Mac,
import os
import subprocess
path = os.getcwd()
os.system("cd path/")
print (path)
# This should be the full path to your Blender executable.
blenderPath = path + "/blender.app/Contents/macos/blender"
# This is the directory that you want to be your "current" directory when Blender starts
path1 = "/Users/user/Desktop/scenario/Blender"
# This makes makes it so your script is currently based at "path1"
os.chdir(path1)
subprocess.check_call([blenderPath, "Import_mhx.blend"])
Results:
In Windows, it works fine.
On Macs, the result is that the file is opened, but the program seems not to be opened. It is quite strange, I think.
Questions:
Is there any extension that I should place for the blender (UNIX executable file) in order for it to open?
Is there any other way that I can do it in order to open the program correctly, but also be able to use relative paths inside .blend files?
import os
import subprocess
blenderPath = "./blender.app/Contents/MacOS/blender"
path1 = "./"
os.chdir(path1)
subprocess.check_call([ blenderPath, "Animation.blend"])
Both blender opens perfectly and relative paths inside .blend file work ok:)

How do I find iTunes library folder on Mac and Windows?

I made an application that parse the iTunes library to retrieve its content. It works fine in most cases but if a user moved his library somewhere else than the default iTunes folder (see: http://lifehacker.com/238296/ultranewb--how-to-move-your-itunes-library-to-an-external-drive), then I need a way to find this path.
On Mac, I was looking into ~/Library/Preferences/com.apple.iTunes.plist. There is a setting called "alis:1:iTunes Library Location" but it contains several parameters all concatenated and converted to hexadecimal.
On Windows, I found this file "C:\Documents and Settings\\Application Data\Apple Computer\iTunes\iTunesPrefs.xml" that contains a setting "iTunes Library XML Location:1" but this one is encoded.
Any help would be greatly appreciated.
Thanks!
On Windows, the iTunes Library XML Location:1 entry in iTunesPrefs.xml is a Base 64 encoded Unicode string, so you'll need to decode it before you can use it. On my PC, it decodes to C:\Documents and Settings\Emerick\My Documents\My Music\iTunes\iTunes Music Library.xml.
It should be relatively easy to decode this value using your language of choice; your platform may even provide utility libraries that make this trivial. In C#, for example, the decoding function would look something like this:
static public string DecodeBase64(string encodedData)
{
byte[] encodedBytes = System.Convert.FromBase64String(encodedData);
return System.Text.UnicodeEncoding.Unicode.GetString(encodedBytes);
}
I can't help you with the Windows stuff, but on the Mac what you're seeing in that prefs file is old-school alias handle data. Take a look at or just use Chris Hanson's BDAlias class to convert it to a path.
http://github.com/rentzsch/bdalias
As the others point out "alis:1:iTunes Library Location" is alias data. Here's how I find the path from the data in OS X using Python.
#!/usr/bin/env python
import commands, plistlib
from Carbon import File
from os.path import expanduser
PLIST_PATH = '~/Library/Preferences/com.apple.iTunes.plist'
PLIST_KEY = 'alis:1:iTunes Library Location'
def resolve_path_from_alias_data( alis ):
fs_ref = File.Alias( rawdata=alis ).FSResolveAlias( None )[0]
file_path = fs_ref.as_pathname()
return file_path
plist_str = commands.getoutput( '/usr/bin/plutil -convert xml1 -o - "' + expanduser( PLIST_PATH ) + '"' )
plist_data = plistlib.readPlistFromString( plist_str )
alis_data = plist_data[ PLIST_KEY ].data
file_path = resolve_path_from_alias_data( alis_data )
print repr( file_path )
Unfortunately, iTunes no longer uses "alis:1:iTunes Library Location" so this no longer works. Now iTunes 11 uses an entry called "RDoc:132:Documents" which seems to be completely different. I have posted a similar question with the appropriate iTunes 11 details.
Actually, my answer works just fine as of OS X 10.9.1. I'm not sure whether it stopped due to some error I made, or if Apple quietly reverted something. Either way, it's working again on my Mac.

Resources