Pillow是强大的图像处理器,却很难在iOS上运行,能在iOS上运行pillow,几乎是很多人甚至技术公司的需要。这使得手机作为当前常用的拍照工具无法能借助Pillow做图像处理。同时,使用python开发iOS App也是当前的一个重要需求。开源kivy和beeWare在这方面都有发展。
然而,通过google和百度出来的python+pillow+iOS的结果,都几乎没有可用的,或者说基本没有真正能跑的通的一个真实项目。
本文就是阐述,如何一步步,通过使用beeWare的基础架构工具(Rubicon和Toga),让Pillow(PIL)成功运行于iOS系统上。
App代码完全使用Python开发。最后的App功能就是允许从手机相册中选取一张图片,然后使用各种PIL图像功能对图像进行处理,并展示最终图像处理结果。
从结果可见,python可访问手机相册,获得图片后,使用PIL功能进行各种处理,处理结果可以展现。只要稍稍改进,便可成为iOS的图片处理的App。当然,本文并不是针对如何开发App,因此,这部分留给读者自己扩展。
如果仅仅让Python+Pillow运行在iOS上,把python解释器编译成静态库,通过接口,也勉强可以运行。但实际问题是,如何读取相册,如何展示图片,这些必须用到objective- c的基础库。因此,和objective c的集成是必不可少的。这点上,beeWare的框架做的还不错,随着他提供的教程步骤,6步就能生成框架。熟悉的人几分钟就够了,当然对了解beeware的rubicon和toga的人来说,自己手动安装使用也可以。
beeware的App框架6步教程在这里:
https://docs.beeware.org/en/latest/
去github下载pillow-master源程序,pillow的底层是使用c开发的,因此,需要把底层库编译成静态库才能在iOS上运行。特别注意的是,pillow的图像解码器部分是插件形式的,因此,解码器是单独的源程序,插件部分在github上fork比较多,但差别都不太大,一般搜索libimg下面的某个版本就可以了。
接下来是最痛苦的部分,如果你直接用make编译,99%的情况会直接fail掉,而且就算成功,也不是静态库。用xcode的libtool转过来也不能用。因此,自己在xcode下创建静态库项目,然后再进行编译是最佳方案,当然,想编译成功需要非常多的细节工作,包括修改.h 和.c的源文件。
这里要说明的是,为了避免单独插件的编译,我把插件的源代码和pillow的源代码合并编译了,结果这个坑很深,调试了好久,修改了不知道多少代码,最终,成功编译。
我已经把编译好的静态库上传到github,一般需要可以直接下载使用,一个是libpillow_simu.a 使用模拟器的,一个是libpillow_iphone.a是给真机用的,
此时如果正常,通过添加以下code,应该可以正常使用pillow了。
在第一步的App项目框架中,找到main.m, 打开后,在其头部(main(。。。)的上面),添加如下代码:
void imaging_importer() {
PyRun_SimpleString(
"import sys, importlib\n" \
"class ImagingImporter(object):\n" \
" def find_module(self, fullname, mpath=None):\n" \
" if fullname in (" \
" '_imaging', " \
" ):\n" \
" return self\n" \
" return\n" \
" def load_module(self, fullname):\n" \
" f = '__' + fullname.replace('.', '_')\n" \
" mod = sys.modules.get(f)\n" \
" if mod is None:\n" \
" mod = importlib.__import__(f)\n" \
" sys.modules[fullname] = mod\n" \
" return mod\n" \
" return mod\n" \
"sys.meta_path.append(ImagingImporter())"
);
}
extern PyMODINIT_FUNC
PyInit__imaging(void);
这部分就是定义把静态库load进内存以便于以后使用。
ok,现在代码往下翻以下,找到这句:
PyEval_InitThreads();
在这句后面添加如下代码:
imaging_importer();
至此,完成了代码,当然如果编译成功,就可以正常调用PIL使用了。但是,要编译成功,需要进行适当的配置。
首先就是arch,这个是xcode的基础配置,我们一般开始都在simulator上调试,因此,设置成x86_64,
现在,需要把pillow静态库加进来了,点击target,选择building phases,在link binary with libraries中,添加libpillow-simu.a. 如下图:
3,把PIL的.py源程序,添加进project的support package里面。当然单独建一个目录。下面是正常的下面的重要目录结构:
试试你的运气把,点击build,看看是否编译链接成功。如果成功,恭喜你,最关键的一部,完成了。如果发现错误,一般是链接错误,那么或者配置arch问题,或者静态库没链接对。
既然我们用python编iOS的app,不写python怎么能行。现在进入真正的功能实现方面。
我们的功能是允许打开相册,选取一张照片,使用PIL进行图片处理,然后再把处理结果展示处理。很简单有没有?嗯嗯,欢迎入坑。
真正开始编程时发现,beeware确实就是个框架,需要大量的自己集成的部分。首先说以下beeware的rubicon和toga都是怎么回事。
通过这种封装,我们就可以直接使用使用UITableView的方法和属性了。这就是rubicon的作用。但是,这里有非常多的坑,很多的function call是并没有集成的,譬如,我们这次图像处理要用到的把图像先转成jpeg格式的object function:
UIImageJPEGRepresentation(UIImage,float)
要想在python里面使用这个功能,必须要自己去按ctypes的规则去声明它的restype和argtypes,以这个功能为例,说明方法如下:
uikit.UIImageJPEGRepresentation.restype = objc_id
uikit.UIImageJPEGRepresentation.argtypes=[objc_id,CGFloat]
这里一定要注意,因为在rubicon中,扩展了ctypes的类型,因此,譬如objc_id,是标准ctypes里面没有的。
在上面正常说明后(在uikit.py)中,此后程序就可以使用上述功能了。
是不是累了,那么先休息一下,我将在下一篇中,讲述如何对rubion和toga扩展,成功访问到相册,如何在python中设置delegate,对相册图片返回进行处理,及如何修改toga的class,使得其可以支持多种图片格式以便于可以和PIL进行图片的数据传输。
当然,我也会把资源开放出来。供大家下载测试。