Welcome!
This is the community forum for my apps Pythonista and Editorial.
For individual support questions, you can also send an email. If you have a very short question or just want to say hello — I'm @olemoritz on Twitter.
Take photo without 'Use' step, join ObjC with Photos module
-
To avoid running out of memory when dealing with many images on Pythonista, I believe that following is safer because https://docs.python.org/3/library/io.html#io.BytesIO only frees its memory when
.close()
is called.def ui2pil(ui_img): with io.BytesIO(ui_img.to_png()) as mem_file: return PILImage.open(mem_file.getvalue())
-
Thank you but I am afraid I need my hand held some more. The block of four lines which I presume replace the block of four lines in the handler function does indeed show the image. But the same apparently identical show() repeated outside of the ObjC def fails. I have put comments at the end of the troublesome line.
from objc_util import * import time import threading import ui import photos C = ObjCClass def take_photo_now(filename='photo.jpg'): session = C('AVCaptureSession').new().autorelease() session.sessionPreset = 'AVCaptureSessionPresetPhoto' device = C('AVCaptureDevice').defaultDeviceWithMediaType_('vide') device_input = C('AVCaptureDeviceInput').deviceInputWithDevice_error_(device, None) session.addInput_(device_input) image_output = C('AVCaptureStillImageOutput').new().autorelease() session.addOutput_(image_output) session.startRunning() # NOTE: You may need to adjust this to wait for the camera to be ready (use a higher number if you see black photos): time.sleep(0.1) def handler_func(_block, _buffer, _err): buffer = ObjCInstance(_buffer) img_data = C('AVCaptureStillImageOutput').jpegStillImageNSDataRepresentation_(buffer) ui_image = ui.Image.from_data(nsdata_to_bytes(img_data)) ui_image.show() # this show() works #img_data.writeToFile_atomically_(filename, True) e.set() video_connection = None for connection in image_output.connections(): for port in connection.inputPorts(): if str(port.mediaType()) == 'vide': video_connection = connection break if video_connection: break e = threading.Event() handler = ObjCBlock(handler_func, restype=None, argtypes=[c_void_p, c_void_p, c_void_p]) retain_global(handler) image_output.captureStillImageAsynchronouslyFromConnection_completionHandler_(video_connection, handler) e.wait() # my code calls the take_photo_now def above then wishes to go on to ImageDraw on top of it – take_photo_now('photo.jpg') ui_image.show() #this apparently identical show() outside of the subroutine does not work. "name' ui_images' is not defined", or if I declare at the top with "ui_image=None" then it errors with "NoneType' object has no attribute 'show'". the show() in the ObjC continues to work on both variations.
-
@AlanE your ui_image is local in your handler...
-
@cvp Oh crikey. I fear I am lost. Does that mean that ui_image cannot come out of the ObjC for use elsewhere?
-
@AlanE try
from objc_util import * import time import threading import ui import photos C = ObjCClass def take_photo_now(): global ui_image session = C('AVCaptureSession').new().autorelease() session.sessionPreset = 'AVCaptureSessionPresetPhoto' device = C('AVCaptureDevice').defaultDeviceWithMediaType_('vide') device_input = C('AVCaptureDeviceInput').deviceInputWithDevice_error_(device, None) session.addInput_(device_input) image_output = C('AVCaptureStillImageOutput').new().autorelease() session.addOutput_(image_output) session.startRunning() # NOTE: You may need to adjust this to wait for the camera to be ready (use a higher number if you see black photos): time.sleep(0.1) ui_image = None def handler_func(_block, _buffer, _err): global ui_image buffer = ObjCInstance(_buffer) img_data = C('AVCaptureStillImageOutput').jpegStillImageNSDataRepresentation_(buffer) ui_image = ui.Image.from_data(nsdata_to_bytes(img_data)) e.set() video_connection = None for connection in image_output.connections(): for port in connection.inputPorts(): if str(port.mediaType()) == 'vide': video_connection = connection break if video_connection: break e = threading.Event() handler = ObjCBlock(handler_func, restype=None, argtypes=[c_void_p, c_void_p, c_void_p]) retain_global(handler) image_output.captureStillImageAsynchronouslyFromConnection_completionHandler_(video_connection, handler) e.wait() return ui_image # my code calls the take_photo_now def above then wishes to go on to ImageDraw on top of it – ui_image = take_photo_now() ui_image.show() #this apparently identical show() outside of the subroutine does not work. "name' ui_images' is not defined", or if I declare at the top with "ui_image=None" then it errors with "NoneType' object has no attribute 'show'". the show() in the ObjC continues to work on both variations.
-
-
@cvp Oh yes! that edit with the return line works. Completely over the top of my head but thankfully there are people who can. Thank you again.
-
@AlanE the global passes the ui_image from the handler to your take_photo_now function.
The return passes the same ui_image to the main, easy, isn'it? -
@cvp Easier In hindsight yes. But there will always be people faster than me, stronger than me, and in your case clearer-thinking than me, if not also faster and stronger. I now recall disliking the need for global in another program wot I wrote. In this case I think I was also frightened at the complexity of the ObjC.
-
@ccc said
def ui2pil(ui_img):
with io.BytesIO(ui_img.to_png()) as mem_file:
return PILImage.open(mem_file.getvalue())Not sure that your code works, doesn't PILImage.open need a path?
I know the reverse pil2ui
def pil2ui(imgIn): with io.BytesIO() as bIO: imgIn.save(bIO, 'PNG') imgOut = ui.Image.from_data(bIO.getvalue()) return imgOut