Photo captions
@sgspecker This allows to create a new photo with updated caption in camera roll if you share an existing photo which has already a caption
import appex
import console
import os
import photos
def main():
if appex.is_running_extension():
fil = appex.get_attachments()[0]
else:
fil = 'a.jpg'
with open(fil, mode='rb') as fin:
b = fin.read()
#b'\xff\xed\xllllPhotoshop 3.0\x00' = marker APP1
# -----
#b'8BIM\x04\x04\x00\x00\x00\x00\x00\x17\x1c\x01Z\x00\x03\x1b%G\x1c\x02\x0\x0\x02
# ---
#b'\x00\x02\x1c\x02x\x00\x03Cap\x00
ip = b.find(b'Photoshop')
if ip >= 0:
#print(b[ip-6:ip+100])
lip = int.from_bytes(b[ip-2:ip], "big")
i8 = b.find(b'8BIM',ip)
if i8 >= 0:
l8 = int.from_bytes(b[i8+10:i8+12], "big")
i = b.find(b'\x1c\x02x', i8)
# caption field identified by x'1c0278' then length in 2 bytes x'0005' = 5
l = int.from_bytes(b[i+3:i+5], "big")
caption = b[i+5:i+5+l].decode("utf-8")
#print(caption)
#return # if no update
#
caption = console.input_alert('new caption?','', caption, 'ok', hide_cancel_button=True)
lu = len(caption)
bl = lu.to_bytes(2,'big') # x'000l'
# store new caption
b = b[:i+3] + bl + caption.encode('utf-8') + b[i+5+l:]
# change length of 8BIM marker
l8aft = l8 - l + lu
bl8aft = l8aft.to_bytes(2,'big') # x'000l'
b = b[:i8+10] + bl8aft + b[i8+12:]
# change length of Photoshop marker at ip-2
lipaft = lip - l + lu
blaft = lipaft.to_bytes(2,'big') # x'000l'
b = b[:ip-2] + blaft + b[ip:]
tmp = '_temp.jpg'
with open(tmp, mode='wb') as fout:
fout.write(b)
asset = photos.create_image_asset(tmp)
os.remove(tmp)
appex.finish()
if __name__ == '__main__':
main()
PKCanvasView
@ChelsWin here-after, 3 methods:
- In Pythonista, using depreciated method
- In ObjectiveC, without passing via a temporary file
- In Pythonista, using Photos module, passing via a temporary file because create_image_asset needs a file as parameter
with ui.ImageContext(w,h) as ctx:
img.drawInRect_(CGRect(CGPoint(0, 0), CGSize(w,h)))
iv.image = ctx.get_image()
# save ui.Image using depreciated method
import photos
photos.save_image(iv.image)
# save ui.Image in camera roll without passing via a file
import threading
NSBundle.bundleWithPath_('/System/Library/Frameworks/Photos.framework').load()
PHPhotoLibrary = ObjCClass('PHPhotoLibrary')
PHAssetChangeRequest = ObjCClass('PHAssetChangeRequest')
lib = PHPhotoLibrary.sharedPhotoLibrary()
def change_block():
req = PHAssetChangeRequest.creationRequestForAssetFromImage_(img)
def perform_changes():
lib.performChangesAndWait_error_(change_block, None)
t = threading.Thread(target=perform_changes)
t.start()
t.join()
# save ui.Image in camera roll, passing via a file
import photos
# Save as temporary JPEG file
path = 'temp.jpg'
with open(path, 'wb') as f:
quality = 0.9 # From 0.0 (more compression) to 1.0 (better quality)
f.write(iv.image.to_jpeg(quality))
# Save file in camera roll
photos.create_image_asset(path)
# Remove temporary file
os.remove(path)
Crop ui.Image
take a look at this thread and see if this is your issue.
other than that... without using PIL all i could find was inside photos
module.. make sure if you test this example you have a JPEG in your current directory called temp.jpg
...
import ui
import photos
img=photos.create_image_asset('./temp.jpg').get_ui_image(size=(512,512), crop=True)
v=ui.View(frame=(0, 0, 600, 800))
iv=ui.ImageView(image=img)
v.add_subview(iv)
v.present('sheets')
hope this helps.
Photos module
@resserone13 create a photo in camera roll via
import photos
import os
pil_image = photos.capture_image() # returns a PIL Image
path = 'new.jpg'
pil_image.save(path, format='JPEG')
photos.create_image_asset(path)
os.remove(path)
Photos module
@ccc when I take a pic with the capture_image() how do I name it so I can pass it to create_image_asset() then send it via a mime email.
Photos module
Can some demonstrate all the methods and function of the photos module? I'm have trouble using create_image_asset(). I want to take a photo and save it to a perticular album. After that I want the photo sent via email and then deleted for the album.
Containers for photos, with scroll, drag&drop between them
@cvp just to let you know here is my code.
It wont run because the rest of the code is missing, but it gives you the idea
def save(self):
# make a hi resolution copy of back & images, then save it in camera roll
xo, yo, w, h = self.page.back.frame
c = self.page.back.background_color
targetWidth = 4*1024
s = targetWidth / w
w, h = w*s, h*s
page = ui.View( frame=(0,0,w,h), background_color=c)
views = []
for thumb in self.thumbs:
x,y,w,h = thumb.frame
x,y,w,h = (x-xo)*s, (y-yo)*s, w*s, h*s
v = ui.View( frame=(x,y,w,h) )
x,y,w,h = thumb.iv.frame
x,y,w,h = x*s, y*s, w*s, h*s
img = thumb.getImage(thumb.asset)
iv = ui.ImageView(frame=(x,y,w,h), image=img)
v.add_subview(iv)
page.add_subview(v)
views.append(v)
# save page image in pythonista
getTopView().add_subview(page)
#page.bring_to_front()
#if True: return
path = 'temp.jpg'
with ui.ImageContext(page.width, page.height) as ctx:
page.draw_snapshot()
ui_image = ctx.get_image()
pil = Image.open(io.BytesIO(ui_image.to_png()))
pil.save(path , quality=99)
# save page image in albums
asset = photos.create_image_asset(path)
os.remove(path)
getTopView().remove_subview(page)
views = False
console.hud_alert('saved')
looks like i must add the view to the screen to get the draw snapshot to work.
using appex to modify a photo creation date directly
@average install piexif.py
from here
and try this script
from datetime import datetime
import dialogs
from objc_util import ObjCInstance
import os
import photos
import piexif # https://github.com/hMatoba/Piexif/tree/master/piexif
def exif_as_str(exif_val):
exif_str = str(exif_val)
if len(exif_str) >= 3:
if exif_str[:2] == "b'" and exif_str[-1] == "'":
exif_str = exif_str[2:-1]
return exif_str
def main():
# select photo
asset = photos.pick_asset()
# get its path
path = str(ObjCInstance(asset).pathForOriginalFile())
#print(path)
# get photo exifs
exif_info = piexif.load(path)
exif_str = exif_as_str(exif_info['Exif'][piexif.ExifIFD.DateTimeOriginal])
#print('photo taken date/time',exif_str)
# get new taken date-time
new_date = dialogs.datetime_dialog(title=str(exif_str))
dt_str = datetime.strftime(new_date,'%Y:%m:%d %H:%M:%S')
# update exif of taken datetime
exif_info['Exif'][piexif.ExifIFD.DateTimeOriginal] = dt_str
exif_str = exif_as_str(exif_info['Exif'][piexif.ExifIFD.DateTimeOriginal])
#print('modified photo taken date/time',exif_str)
# create new photo with modified taken date-time
exif_bytes = piexif.dump(exif_info)
new_path = 'temp.jpg'
piexif.insert(exif_bytes,path,new_file=new_path)
photos.create_image_asset(new_path)
os.remove(new_path)
#--- protect for import
if __name__ == '__main__':
main()
If any error, please forgive me and don't forget I'm in holidays In Bourgogne (🍷=>bugs)
Pythonista crashes trying to save merged photo
@jm2466 this
# 1) convert PIL Image to ui.Image
console.hud_alert('convert PIL Image into ui.Image')
with io.BytesIO() as bIO:
new_im.save(bIO, 'PNG')
ui_image = ui.Image.from_data(bIO.getvalue())
del bIO
# 2) Create a PHAsset from an ui.Image (not from a PIL Image)
console.hud_alert('Create a PHAsset from an ui.Image')
lib = PHPhotoLibrary.sharedPhotoLibrary()
def change_block():
req = PHAssetChangeRequest.creationRequestForAssetFromImage_(ui_image)
def perform_changes():
lib.performChangesAndWait_error_(change_block, None)
t = threading.Thread(target=perform_changes)
t.start()
t.join()
is about (😀) 1000 x slower than
path = 'temp.jpg'
new_im.save(path , quality=95)
photos.create_image_asset(path)
os.remove(path)