opencvのインストールログ
インストールログ
基本的に
http://opencv.willowgarage.com/wiki/InstallGuide%20%3A%20Debian
に書いてあるとおり実行しました。
一部インストールするパッケージを変更しています。
IPPやTBBはインストールしていません。pythonラッパーはインストールしました。
sudo aptitude -y install build-essential sudo aptitude -y install cmake sudo aptitude -y install pkg-config sudo aptitude -y install libpng12-0 libpng12-dev libpng++-dev libpng3 sudo aptitude -y install libpnglite-dev libpngwriter0-dev libpngwriter0c2 sudo aptitude -y install zlib1g-dbg zlib1g zlib1g-dev sudo aptitude -y install libjasper-dev libjasper-runtime libjasper1 sudo aptitude -y install pngtools libtiff4-dev libtiff4 libtiffxx0c2 libtiff-tools sudo aptitude -y install libjpeg62 libjpeg62-dev libjpeg62-dbg libjpeg-progs sudo aptitude -y install ffmpeg libavcodec-dev libavcodec52 libavformat52 libavformat-dev sudo aptitude -y install libgstreamer0.10-0-dbg libgstreamer0.10-0 libgstreamer0.10-dev sudo aptitude -y install libxine1-ffmpeg libxine-dev libxine1-bin sudo aptitude -y install libunicap2 libunicap2-dev sudo aptitude -y install libdc1394-22-dev libdc1394-22 libdc1394-utils sudo aptitude -y install swig sudo aptitude -y install libv4l-0 libv4l-dev sudo aptitude -y install python-numpy sudo aptitude -y install libpython2.6 python-dev python2.6-dev sudo aptitude -y install libjpeg-progs libjpeg-dev sudo aptitude -y install libgstreamer-plugins-base0.10-dev mkdir ocv cd ocv/ # sudo aptitude install subversion svn co https://code.ros.org/svn/opencv/trunk cd trunk/opencv/ mkdir release cd release/ cmake -D CMAKE_BUILD_TYPE=RELEASE -D CMAKE_INSTALL_PREFIX=/usr/local -D BUILD_PYTHON_SUPPORT=ON -D BUILD_EXAMPLES=ON .. make sudo make install sudo ldconfig cd unix-install pkg-config opencv --libs
見出し語化の高速化
nltkのWordNetLemmatizerを力ずくで高速化した。
環境
Python 2.6.5
コード
# -*- coding: utf-8 -*- from collections import defaultdict import nltk from nltk.corpus import wordnet as _wordnet _STEMMER = nltk.PorterStemmer().stem _LEMMATIZATION_POS_PRIORITY = (_wordnet.NOUN, _wordnet.VERB, _wordnet.ADJ, _wordnet.ADV) _POS_LIST = (_wordnet.ADJ, _wordnet.ADV, _wordnet.NOUN, _wordnet.VERB) def stem_form(form): return _STEMMER(form) def _detect_pos(form): form = form.replace(' ', '_') synsets = _wordnet.synsets(form) if not synsets: return None pos = None stem = stem_form(form) for synset in synsets: if stem_form(synset.name[:-5]) == stem: pos = synset.pos break if pos is None: pos = synsets[0].pos if pos == _wordnet.ADJ_SAT: pos = _wordnet.ADJ return pos def lemmatize_with_wordnet(form, pos=None): if pos is None: pos = _detect_pos(form) if not pos: return form assert(pos in _POS_LIST) return nltk.WordNetLemmatizer().lemmatize(form, pos=pos) def _lemmatize_form_with_wordnet(form, pos_set): assert(pos_set) if len(pos_set) == 1: target_pos = pos_set.copy().pop() else: target_pos = _detect_pos(form) if not target_pos or target_pos not in pos_set: for pos in _LEMMATIZATION_POS_PRIORITY: if pos in pos_set: target_pos = pos break assert(target_pos in _POS_LIST) return nltk.WordNetLemmatizer().lemmatize(form, pos=target_pos) def _construct_inflected_form_to_lemma_dictionary(): all_inflected_forms = defaultdict(set) for pos, excepted_forms in _wordnet._exception_map.iteritems(): if pos == _wordnet.ADJ_SAT: continue for excepted_form in excepted_forms: all_inflected_forms[excepted_form].add(pos) for pos in _POS_LIST: substitutions = _wordnet.MORPHOLOGICAL_SUBSTITUTIONS[pos] for lemma in _wordnet.all_lemma_names(pos=pos): lemma = lemma.replace('_', ' ') all_inflected_forms[lemma].add(pos) form = lemma if pos == _wordnet.NOUN and form.endswith('ful'): suffix = 'ful' form = form[:-3] # len('ful') else: suffix = '' for new_suffix, old_suffix in substitutions: if form.endswith(old_suffix) or old_suffix == '': if old_suffix == '': inflected_form = form + new_suffix else: inflected_form = form[:-len(old_suffix)] + new_suffix inflected_form += suffix all_inflected_forms[inflected_form].add(pos) inflected_form_to_lemma = {} for inflected_form, pos_set in all_inflected_forms.iteritems(): lemma = _lemmatize_form_with_wordnet(inflected_form, pos_set) inflected_form_to_lemma[inflected_form] = lemma.replace('_', ' ') return inflected_form_to_lemma _INFLECTED_FORM_TO_LEMMA = _construct_inflected_form_to_lemma_dictionary() def lemmatize_with_dict(form): try: return _INFLECTED_FORM_TO_LEMMA[form.lower().replace(' ', '_')] except KeyError: pass return form.lower() def _test(): test_forms = ('media', 'playing', 'player', 'possesses', 'sung', 'became', 'begun', 'fallen', 'men', 'buses', 'initial', 'initialization') print 'original\tstemming\twordnet_lemmatizer\tlemmatize_with_dict' for form in test_forms: print ('{0}\t{1}\t{2}\t{3}').format(form, stem_form(form), lemmatize_with_wordnet(form), lemmatize_with_dict(form)) if __name__ == '__main__': _test() ''' original stemming wordnet_lemmatizer lemmatize_with_dict media media medium medium playing play playing playing player player player player possesses possess posse possess sung sung sung sung became becam become become begun begun begin begin fallen fallen fall fall men men man man buses buse bus bus initial initi initial initial initialization initi initialization initialization '''
nltkのWordNetLemmatizerの動作は、入力語句に対して以下の手順で見出し語化を行っているようです。
- 入力語句が例外リストに載っていないかどうかチェックする。載っていれば別途処理する。
- 末尾置換ルールを用いて入力語句の末尾を置換する。
- 辞書の見出し語に語句が存在しているかどうかをチェックする。
- 末尾置換ルールが無くなるまで手順2.と3.を繰り返す。
手順1.の例外リストとは、主に不規則変化(sing-sang-sungなど)を解決するための手順のようです。
手順2.と手順3.では、あらかじめ用意された末尾置換ルールを使用します。
例えば入力語句が名詞であり、xesで終わっている場合、末尾からxesを取り除き、代わりにxを追加する。その後、置換した単語が辞書の見出し語にあるかどうかをチェックする。例えば入力語句がboxesであるとき、末尾のxesをxに置換しboxとし、その後boxが辞書の見出し語にあるかどうかチェックする。
ソースコードを読む限り、nltkのWordNetLemmatizerは本家WordNetのC言語実装と同様の手順で見出し語化を行っているらしい。
末尾の置換、検索、を繰り返しているので実行速度が遅い。
そこで、これら例外リストと置換ルールを使用して、WordNetLemmatizerが処理できる語句をすべて生成する。
それらWordNetLemmatizerが処理できる語句をキーとし、それらの見出し語を値とする辞書(_INFLECTED_FORM_TO_LEMMA)を作成した。
{'boxes': 'box', 'box': 'box', ..., 'media': 'medium', 'medium': 'medium', ...}のような活用形がキーであり、見出し語が値である辞書。
実行速度テスト
http://americannationalcorpus.org/OANC/index.htmlから抽出した16,814,123個の英単語(296,528種類)に対して、見出し語化にかかる時間を測った。
nltkのWordNetLemmatizerによる見出し語化(lemmatize_with_wordnet)では3583秒(およそ1時間)、
nltkのWordNetLemmatizerによる見出し語化(lemmatize_with_wordnet)の入出力をキャッシュした場合では80秒、
今回作成した作成した辞書を使用した見出し語化(lemmatize_with_dict)では20秒、
となった。結局入出力をキャッシュした場合と大差無い結果になった。
しかし、入出力をキャッシュする場合、WordNetに登録されていない単語や固有名詞などが入力されるたび、キャッシュのサイズが大きくなる。
その点、今回作成した辞書のサイズは固定されているので、メモリ消費量の増加などを気にしなくて良い。
今回作成した辞書にも問題点がある。今回作成した辞書では、入力語句から出力が一意に決まるが、通常は一意には決まらない。例えば、betterの見出し語はwellかgoodかは品詞を用いない限り決めにくい。
pythonによる文字列の正規化
テキストマイニングなどを行うためには文書、文、単語などの文字列の正規化が重要です。
単語の大文字小文字の統一、半角全角の統一などをする必要があります。
文字列の正規化のために利用しているpythonコードを以下に書いておきます。
今後増える可能性もあります。
unicode型に変換する
def unicode_ignore_invalid_char(text): if isinstance(text, str): return text.decode('utf-8', 'ignore') return text
str型に変換する
def str_ignore_invalid_char(text): if isinstance(text, unicode): return text.encode('utf-8', 'ignore') return text
入出力の文字列型を統一する
from functools import wraps def consistent_texttype(function): @wraps(function) def _consistent_texttype(*args, **kwargs): assert(1 <= len(args)) input_text = args[0] is_unicode = False if isinstance(input_text, unicode): is_unicode = True elif not isinstance(input_text, str): is_unicode = isinstance(input_text[0], unicode) # for collections output_text = function(*args, **kwargs) if isinstance(output_text, unicode) or isinstance(output_text, str): if is_unicode: return unicode_ignore_invalid_char(output_text) return str_ignore_invalid_char(output_text) if is_unicode: return map(unicode_ignore_invalid_char, output_text) return map(str_ignore_invalid_char, output_text) return _consistent_texttype
入力文字列がstr型であるとき出力文字列もstr型にし、入力文字列がunicode型であるとき出力文字列もunicode型にするデコレータ。
unicodeを正規化する
import unicodedata @consistent_texttype def normalize_unicode(text, form='NFKC'): assert(form in ('NFC', 'NFKC', 'NFD', 'NFKD')) unicode_text = unicode_ignore_invalid_char(text) normalized_text = unicodedata.normalize(form, unicode_text) return normalized_text
半角カタカナを全角カタカナに変換したりする。
例えば㌻をページ、ハンカクカナをハンカクカナに変換する。
HTMLエンティティを変換する
from BeautifulSoup import BeautifulSoup @consistent_texttype def unescape_entities_with_beautifulsoup(htmltext, prettify=False): soup = BeautifulSoup(htmltext, convertEntities=BeautifulSoup.HTML_ENTITIES) if prettify: return soup.prettify() return soup.__repr__()
BeautifulSoupを利用してHTMLエンティティを変換する。
例えば>を>に変換する。
from BeautifulSoup import BeautifulStoneSoup @consistent_texttype def unescape_entities_with_beautifulstonesoup(htmltext, prettify=False): soup = BeautifulStoneSoup(htmltext, convertEntities=BeautifulStoneSoup.HTML_ENTITIES) if prettify: return soup.prettify() return soup.__repr__()
BeautifulStoneSoupを利用してHTMLエンティティを変換する。
BeautifulSoupを利用した場合と同じかもしれない。
from htmlentitydefs import name2codepoint import re # derived from BeautifulSoup # __author__ = "Leonard Richardson (leonardr@segfault.org)" # __version__ = "3.1.0.1" # __copyright__ = "Copyright (c) 2004-2009 Leonard Richardson" # __license__ = "New-style BSD" def _unescape_entity(match): x = match.group(1) if x in name2codepoint: return unichr(name2codepoint[x]) elif 0 < len(x) and x[0] == '#': if 1 < len(x) and x[1] == 'x': return unichr(int(x[2:], 16)) return unichr(int(x[1:])) return u'&{0};'.format(x) @consistent_texttype def unescape_entities(htmltext): unicode_htmltext = unicode_ignore_invalid_char(htmltext) unescaped_text = re.sub(u'&(#\d+|#x[0-9a-fA-F]+|\w+);', _unescape_entity, unicode_htmltext) assert(isinstance(unescaped_text, unicode)) return unescaped_text
BeautifulSoupのHTMLエンティティ変換部分を抽出し、少し変更を加えたもの。
HTMLエンティティ変換のためだけにBeautifulSoupを利用するのは高価すぎると考えるときはこちらを利用する。
BeautifulSoupはNew-style BSDライセンスです。
語幹を抽出する(ステミング)
import nltk @consistent_texttype def stem_term(term, porter=True): if porter: return nltk.PorterStemmer().stem(term) return nltk.LancasterStemmer().stem(term)
英語用。例えばinitial, initializeをinitiにする。
見出し語化・レンマ化(lemmatization)
import nltk from nltk.corpus import wordnet @consistent_texttype def lemmatize_term(term, pos=None): if pos is None: synsets = wordnet.synsets(term) if not synsets: return term pos = synsets[0].pos if pos == wordnet.ADJ_SAT: pos = wordnet.ADJ assert(pos in (wordnet.NOUN, wordnet.VERB, wordnet.ADJ, wordnet.ADV)) return nltk.WordNetLemmatizer().lemmatize(term, pos=pos)
英語用。WordNetを用いて単語の見出し語化を行う。
例えばis, areをbeに、potatosをpotatoにする。
品詞(pos)の指定がなければsynsetsのうち、一番最初に現れる品詞を使用する。
小文字にする
text.lower()
もしくは、
import string def lower_text(text): return string.lower(text)
大文字にする
text.upper()
もしくは、
import string def upper_text(text): return string.upper(text)
先頭のみ大文字にする
text.capitalize()
もしくは、
import string def capitalize_text(text): return string.capitalize(text)
参考文献
- 作者: Steven Bird,Ewan Klein,Edward Loper,萩原正人,中山敬広,水野貴明
- 出版社/メーカー: オライリージャパン
- 発売日: 2010/11/11
- メディア: 大型本
- 購入: 20人 クリック: 639回
- この商品を含むブログ (44件) を見る
nkf python インターフェースのインストール
インストール方法
$ mkdir temp # 作業用ディレクトリの作成 $ cd temp # http://sourceforge.jp/projects/nkf/ から nkf-2.1.1.tar.gz をダウンロード temp$ tar zxvf nkf-2.1.1.tar.gz temp$ cd nkf-2.1.1/ temp/nkf-2.1.1$ wget ftp://city.plala.jp:1221/NkfPython/NKF_python20090602.tgz temp/nkf-2.1.1$ tar zxvf NKF_python20090602.tgz temp/nkf-2.1.1$ cd NKF.python/ temp/nkf-2.1.1/NKF.python$ sudo python setup.py install # Python.h: No such file or directory と表示された場合は、以下を実行する # sudo aptitude install python-dev temp/nkf-2.1.1/NKF.python$ cd ../../../ $ sudo rm -rf temp
使用方法
import nkf flag = '-w' output = nkf.nkf(flag, input_text) input_code = nkf.guess(input_text)
Pythonにおける並行処理について
気になったのでPythonのGIL(Global Interpreter Lock)が並行処理にどの程度影響するかについて少し実験しました。
はじめに
まず、「並行」処理と「並列」処理という言葉を区別する必要があります。下記参考文献の「並行コンピュータ技法」によると、
システムが複数の動作を同時に実行状態(in progress)に保てる機能を備えている場合を並行(concurrent)と言い、 複数の動作を同時に実行できる場合を並列(parallel)と言います。 重要な概念、違いは「実行状態」という点です。 ...中略... 「並行」は「並列」を含有します。
だそうです。同書によると、1つのCPUコアが2つのスレッドを切り替えながら処理する場合は「並行」処理に含まれるようです。「並列」処理では複数のCPUコアが必須で、複数のスレッドが複数のCPUコアにより同時に実行される事を「並列」処理と言うようです。
目指すべきは「並行」処理ではなく「並列」処理な気がします。
テストコード
1. 逐次処理でのCPU負荷の大きい処理
# sequential_cpu.py def _cpu_bound_work(): i = 0 while i < 100000000: i += 1 if __name__ == '__main__': for _ in xrange(8): _cpu_bound_work()
2. threadingでのCPU負荷の大きい処理
# threading_cpu.py import threading def _cpu_bound_work(): i = 0 while i < 100000000: i += 1 class TestThread(threading.Thread): def run(self): _cpu_bound_work() if __name__ == '__main__': mainthread = threading.currentThread() for _ in xrange(8): thread = TestThread() thread.start() for thread in threading.enumerate(): if mainthread != thread: thread.join()
3. multiprocessingによるCPU負荷の大きい処理
# multiprocessing_cpu.py import multiprocessing def _cpu_bound_work(): i = 0 while i < 100000000: i += 1 class TestProcess(multiprocessing.Process): def run(self): _cpu_bound_work() if __name__ == '__main__': for _ in xrange(8): process = TestProcess() process.start() for process in multiprocessing.active_children(): process.join()
4. 逐次処理によるIO待ちの大きい処理
# sequential_io.py import time def _io_bound_work(): time.sleep(10.0) # to simulate i/o bound work if __name__ == '__main__': for _ in xrange(8): _io_bound_work()
5. threadingによるIO待ちの大きい処理
# threading_io.py import threading import time def _io_bound_work(): time.sleep(10.0) # to simulate i/o bound work class TestThread(threading.Thread): def run(self): _io_bound_work() if __name__ == '__main__': mainthread = threading.currentThread() for _ in xrange(8): thread = TestThread() thread.start() for thread in threading.enumerate(): if mainthread != thread: thread.join()
6. multiprocessingによるIO待ちの大きい処理
# multiprocessing_io.py import multiprocessing import time def _io_bound_work(): time.sleep(10.0) # to simulate i/o bound work class TestProcess(multiprocessing.Process): def run(self): _io_bound_work() if __name__ == '__main__': for _ in xrange(8): process = TestProcess() process.start() for process in multiprocessing.active_children(): process.join()
テスト結果
1. 逐次処理によるCPU負荷の大きい処理
$ time python sequential_cpu.py real 0m45.265s user 0m45.230s sys 0m0.020s
2. threadingによるCPU負荷の大きい処理
$ time python threading_cpu.py real 1m8.033s user 1m7.420s sys 0m16.930s
3. multiprocessingによるCPU負荷の大きい処理
$ time python multiprocessing_cpu.py real 0m10.969s user 1m24.960s sys 0m0.040s
4. 逐次処理によるIO待ちの大きい処理
$ time python sequential_io.py real 1m20.095s user 0m0.010s sys 0m0.010s
5. threadingによるIO待ちの大きい処理
$ time python threading_io.py real 0m10.029s user 0m0.020s sys 0m0.000s
6. multiprocessingによるIO待ちの大きい処理
$ time python multiprocessing_io.py real 0m10.035s user 0m0.020s sys 0m0.010s
まとめ
1,4の逐次処理が遅いのは当然として、2のthreadingモジュールを使用してCPU負荷の大きい処理を行った場合の実行速度がかなり遅いです。
pythonのGILの影響で、並列処理ができていない事が原因なのでしょう。3のmultiprocessingモジュールを使用した場合はGILを回避できるようです。
並列処理できないthreadingモジュールを使う意味はあるのでしょうか。少なくとも5のように、IO待ち時間が長い処理を複数回行う場合はthreadingモジュールを使用する意味はあるようです。webページのクローラなどには向いているようです。
やはりthreadingモジュールよりもmultiprocessingモジュールを使用した方がいい気がする。(もしくはos.forkを使用するか)
参考
multiprocessing — Process-based parallelism — Python 3.8.0a4 documentation
2.6に新搭載のmultiprocessingを見て俺のPythonがおっきした件 | TRIVIAL TECHNOLOGIES 4 @ats のイクメン日記
Python 2.6 multiprocessing package を触ってみた。 [GIL回避] | Tricorn Tech Labs
並行コンピューティング技法 ―実践マルチコア/マルチスレッドプログラミング
- 作者: Clay Breshears,千住治郎
- 出版社/メーカー: オライリージャパン
- 発売日: 2009/12/21
- メディア: 大型本
- 購入: 12人 クリック: 598回
- この商品を含むブログ (38件) を見る
ALAssetsLibraryについて
以下は私がWebや書籍から集めた情報や、私が試行錯誤した経験により、ALAssetsLibraryについてまとめたものです。
そのため、間違いが含まれている可能性があります。何かしらの間違いを見つけた方はご指摘願います。
はじめに
ALAssetsLibraryとはiOS4で追加された、iPhone/iPad/iPodの写真/映像フォルダにアクセスするためのフレームワークです。
私はiOS4がリリースされた頃からiOSプログラミングを始めたため、iOS3以前については良く知りませんが、iOS3以前ではファイルパスを指定し、写真/映像フォルダに直接アクセスしていたようです。しかし現在はファイルに直接アクセスするとAppleの審査が通らないようです。そのため現在iPhone/iPad/iPodの写真・映像フォルダにアクセスするためにはALAssetsLibraryを使用しないといけません。
ALAssetsLibraryを使用すると画像データのEXIFデータなども取得でき、便利です。
ALAssetsLibraryがリリースされてから半年程度経ちますが、ALAssetsLibraryに関するサンプルコードやチュートリアルなどはかなり少ないです。そのため今回ALAssetsLibraryについてまとめることにしました。
サンプルコード
MYAssetsAccessor.h
#import <UIKit/UIKit.h> @class ALAssetsLibrary; @interface MYAssetsAccessor : NSObject { @private ALAssetsLibrary *assetsLibrary_; } - (void)loadUrlsWithCallbackTarget:(id)anObject callbackMethod:(SEL)aSelector; - (NSDictionary *)metadataOfAssetIdentifiedByUrl:(NSURL *)url; - (UIImage *)thumbnailOfAssetsIdentifiedByUrl:(NSURL *)url; - (UIImage *)fullResolutionImageOfAssetsIdentifiedByUrl:(NSURL *)url; + (void)addObserverForAssetsLibraryChange:(id)anObserver selector:(SEL)aSelector; + (void)removeObserverForAssetsLibraryChange:(id)anObserver; // to understand how the enumeration works. - (void)enumerationTest_; @end
MYAssetsAccessor.m
#import <AssetsLibrary/AssetsLibrary.h> // require AssetsLibrary.framework #import "MYAssetsAccessor.h" @implementation MYAssetsAccessor - (id)init { assetsLibrary_ = [[ALAssetsLibrary alloc] init]; return self; } - (void)dealloc { [assetsLibrary_ release]; [super dealloc]; } - (void)loadUrlsWithCallbackTarget:(id)anObject callbackMethod:(SEL)aSelector { id callbackTarget = [anObject retain]; SEL callbackMethod = aSelector; NSMutableArray *urls = [[NSMutableArray alloc] init]; void (^assetResultBlock)(ALAsset *, NSUInteger, BOOL *) = ^(ALAsset *asset, NSUInteger index, BOOL *stop) { if (!*stop && asset) { ALAssetRepresentation *defaultRepresentation = [asset defaultRepresentation]; [urls addObject:[defaultRepresentation url]]; } }; void (^resultBlock)(ALAssetsGroup *, BOOL *) = ^(ALAssetsGroup *group, BOOL *stop) { if (*stop || !group) { [callbackTarget performSelector:callbackMethod withObject:urls]; } else { [group setAssetsFilter:[ALAssetsFilter allAssets]]; [group enumerateAssetsUsingBlock:assetResultBlock]; } }; void (^failureBlock)(NSError *) = ^(NSError *error) { NSLog(@"exception in enumerating assets. %@", error); [callbackTarget performSelector:callbackMethod withObject:urls]; }; ALAssetsGroupType targetGroupType = ALAssetsGroupAll; [assetsLibrary_ enumerateGroupsWithTypes:targetGroupType usingBlock:resultBlock failureBlock:failureBlock]; [urls release]; [callbackTarget release]; } - (NSDictionary *)metadataOfAssetIdentifiedByUrl:(NSURL *)url { __block NSDictionary *metadata = nil; [assetsLibrary_ assetForURL:url resultBlock:^(ALAsset *asset) { ALAssetRepresentation *defaultRepresentation = [asset defaultRepresentation]; metadata = [[defaultRepresentation metadata] copy]; } failureBlock:^(NSError *error) { NSLog(@"exception in accessing assets by url. %@", error); }]; return [metadata autorelease]; } - (UIImage *)thumbnailOfAssetsIdentifiedByUrl:(NSURL *)url { __block UIImage *thumbnail = nil; [assetsLibrary_ assetForURL:url resultBlock:^(ALAsset *asset) { thumbnail = [[UIImage alloc] initWithCGImage:[asset thumbnail]]; } failureBlock:^(NSError *error) { NSLog(@"exception in accessing assets by url. %@", error); }]; return [thumbnail autorelease]; } - (UIImage *)fullResolutionImageOfAssetsIdentifiedByUrl:(NSURL *)url { __block UIImage *fullResolutionImage = nil; [assetsLibrary_ assetForURL:url resultBlock:^(ALAsset *asset) { ALAssetRepresentation *defaultRepresentation = [asset defaultRepresentation]; fullResolutionImage = [[UIImage alloc] initWithCGImage:[defaultRepresentation fullResolutionImage]]; } failureBlock:^(NSError *error) { NSLog(@"exception in accessing assets by url. %@", error); }]; return [fullResolutionImage autorelease]; } + (void)addObserverForAssetsLibraryChange:(id)anObserver selector:(SEL)aSelector { NSNotificationCenter *defaultCenter = [NSNotificationCenter defaultCenter]; [defaultCenter addObserver:anObserver selector:aSelector name:ALAssetsLibraryChangedNotification object:nil]; } + (void)removeObserverForAssetsLibraryChange:(id)anObserver { NSNotificationCenter *defaultCenter = [NSNotificationCenter defaultCenter]; [defaultCenter removeObserver:anObserver name:ALAssetsLibraryChangedNotification object:nil]; } // to understand how the enumeration works. - (void)enumerationTest_ { void (^assetResultBlock)(ALAsset *, NSUInteger, BOOL *) = ^(ALAsset *asset, NSUInteger index, BOOL *stop) { if (*stop) { NSLog(@"stopped enumerating assets."); } else if (!asset) { NSLog(@"the end of the group."); } else { NSDictionary *urls = [asset valueForProperty:ALAssetPropertyURLs]; NSLog(@"%@", urls); } }; void (^resultBlock)(ALAssetsGroup *, BOOL *) = ^(ALAssetsGroup *group, BOOL *stop) { if (*stop) { NSLog(@"stopped enumerating groups."); } else if (!group) { NSLog(@"the end of enumation."); } else { NSString *groupName = [group valueForProperty:ALAssetsGroupPropertyName]; ALAssetsFilter *photosFilter = [ALAssetsFilter allPhotos]; [group setAssetsFilter:photosFilter]; NSInteger numberOfPhotos = [group numberOfAssets]; NSLog(@"%d photos in %@", numberOfPhotos, groupName); [group enumerateAssetsUsingBlock:assetResultBlock]; ALAssetsFilter *videosFilter = [ALAssetsFilter allVideos]; [group setAssetsFilter:videosFilter]; NSInteger numberOfVideos = [group numberOfAssets]; NSLog(@"%d videos in %@", numberOfVideos, groupName); [group enumerateAssetsUsingBlock:assetResultBlock]; ALAssetsFilter *allFilter = [ALAssetsFilter allAssets]; [group setAssetsFilter:allFilter]; NSInteger numberOfAssets = [group numberOfAssets]; NSLog(@"%d assets in %@", numberOfAssets, groupName); [group enumerateAssetsUsingBlock:assetResultBlock]; } }; void (^failureBlock)(NSError *) = ^(NSError *error) { NSLog(@"exception in enumerating assets. %@", error); }; // ALAssetsGroupType targetGroupType = ALAssetsGroupLibrary | // ALAssetsGroupAlbum | // ALAssetsGroupEvent | // ALAssetsGroupFaces | // ALAssetsGroupSavedPhotos; ALAssetsGroupType targetGroupType = ALAssetsGroupAll; [assetsLibrary_ enumerateGroupsWithTypes:targetGroupType usingBlock:resultBlock failureBlock:failureBlock]; // the result will be: // 2011-04-28 18:38:40.939 MyProject[869:707] 4 photos in Photo Library // 2011-04-28 18:38:40.964 MyProject[869:707] { // "public.jpeg" = "assets-library://asset/asset.jpg?id=104&ext=jpg"; // } // ... // 2011-04-28 18:38:40.989 MyProject[869:707] the end of the group. // 2011-04-28 18:38:40.993 MyProject[869:707] 0 videos in Photo Library // 2011-04-28 18:38:40.997 MyProject[869:707] the end of the group. // 2011-04-28 18:38:41.003 MyProject[869:707] 4 assets in Photo Library // 2011-04-28 18:38:41.008 MyProject[869:707] { // "public.jpeg" = "assets-library://asset/asset.jpg?id=104&ext=jpg"; // } // ... // 2011-04-28 18:38:41.030 MyProject[869:707] the end of the group. // 2011-04-28 18:38:41.039 MyProject[869:707] 25 photos in Camera Roll // 2011-04-28 18:38:41.053 MyProject[869:707] { // "public.jpeg" = "assets-library://asset/asset.JPG?id=1000000021&ext=JPG"; // } // ... // 2011-04-28 18:38:41.273 MyProject[869:707] the end of the group. // 2011-04-28 18:38:41.277 MyProject[869:707] 4 videos in Camera Roll // 2011-04-28 18:38:41.285 MyProject[869:707] { // "com.apple.quicktime-movie" = "assets-library://asset/asset.MOV?id=1000000004&ext=MOV"; // } // ... // 2011-04-28 18:38:41.314 MyProject[869:707] the end of the group. // 2011-04-28 18:38:41.319 MyProject[869:707] 29 assets in Camera Roll // 2011-04-28 18:38:41.327 MyProject[869:707] { // "public.jpeg" = "assets-library://asset/asset.JPG?id=1000000021&ext=JPG"; // } // ... // 2011-04-28 18:38:41.569 MyProject[869:707] the end of the group. // 2011-04-28 18:38:41.573 MyProject[869:707] the end of enumation. } @end
AppDelegate_iPhone.h
#import <UIKit/UIKit.h> @class MYAssetsAccessor; @interface AppDelegate_iPhone : NSObject <UIApplicationDelegate> { UIWindow *window_; MYAssetsAccessor *myAssetsAccessor_; } @property (nonatomic, retain) IBOutlet UIWindow *window; @end
AppDelegate_iPhone.m
#import "AppDelegate_iPhone.h" #import "MYAssetsAccessor.h" @interface AppDelegate_iPhone(Private) - (void)assetLibraryDidChange_:(NSNotification *)aNotification; - (void)assetUrlsDidLoad_:(NSArray *)urls; @end @implementation AppDelegate_iPhone @synthesize window = window_; - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { [self.window makeKeyAndVisible]; myAssetsAccessor_ = [[MYAssetsAccessor alloc] init]; [MYAssetsAccessor addObserverForAssetsLibraryChange:self selector:@selector(assetLibraryDidChange_:)]; [myAssetsAccessor_ enumerationTest_]; [myAssetsAccessor_ loadUrlsWithCallbackTarget:self callbackMethod:@selector(assetUrlsDidLoad_:)]; return YES; } - (void)applicationWillTerminate:(UIApplication *)application { [MYAssetsAccessor removeObserverForAssetsLibraryChange:self]; } - (void)dealloc { [myAssetsAccessor_ release]; [window_ release]; [super dealloc]; } @end @implementation AppDelegate_iPhone(Private) - (void)assetLibraryDidChange_:(NSNotification *)aNotification { NSLog(@"asset library changed. %@", aNotification); // TODO reload urls } - (void)assetUrlsDidLoad_:(NSArray *)urls { NSLog(@"%d urls, %@", [urls count], urls); if ([urls count] == 0) { return; } NSURL *url = [urls objectAtIndex:30]; NSDictionary *metadata = [myAssetsAccessor_ metadataOfAssetIdentifiedByUrl:url]; NSLog(@"metadata of %@ is %@", url, metadata); UIImage *thumbnail = [myAssetsAccessor_ thumbnailOfAssetsIdentifiedByUrl:url]; NSLog(@"thumbnail of %@ is %@", url, thumbnail); UIImage *fullResolutionImage = [myAssetsAccessor_ fullResolutionImageOfAssetsIdentifiedByUrl:url]; NSLog(@"full resolution image of %@ is %@", url, fullResolutionImage); } @end
サンプルコード概要
MYAssetsAccessorはALAssetsLibraryを利用した、画像/映像フォルダへアクセスするためのクラスです。AppDelegate_iPhoneはMYAssetsAccessorを利用したコードです。
MYAssetsAccessorのloadUrlsWithCallbackTarget:callbackMethod:で画像/映像フォルダ中の画像/映像を表すURLの配列を取得します。メソッド中のenumerateGroupsWithTypes:usingBlock:failureBlock:は非同期で実行されるため、URLの取得が終了するもしくは何らかのエラーが生じた場合、指定したcallbackメソッドを呼び出します。なお、ブロック内で*stop = YES;とすると、走査を終了させることもできます。
URLの一覧が取得できれば後はメタデータや、サムネイルを取得したりといろいろできます。
URLは画像/映像フォルダに何らかの変更が生じた場合、変更される事があるので、ALAssetsLibraryChangeNotificationを監視しておきます。
MYAssetsAccessorには動作確認用のenumerationTest_メソッドを用意しています。画像/映像フォルダを走査して、URL等をログに書き出します。
サンプルコードはMITライセンスです。
MYAssetsAccessor.h
MYAssetsAccessor.m
AppDelegate_iPhone.h
AppDelegate_iPhone.m
注意点
ALAssetsLibraryのインスタンスのメソッドはメインスレッドで呼ぶ方が良い
ALAssetsLibraryのインスタンスのメソッドを初めて呼び出すと、次のようなアラートが表示されます。
メインスレッドで呼び出した場合は上記のアラートが表示されます。
しかし、メインスレッド以外で呼び出した場合は上記のアラートが表示されず、ユーザの入力待ち状態のままになり、ALAssetsLibraryに関する処理が一向に進まなくなります。
なお、上記のアラートでユーザがDon't Allowを選択すると写真フォルダにアクセスできず、failureBlockが呼び出されます。
ALAssetsLibraryのインスタンスのメモリ消費量は写真/映像フォルダ内のコンテンツ量に応じて増える
画像フォルダに10枚しか写真がない場合と、画像フォルダに10,000枚の写真がある場合とでは、ALAssetsLibraryのインスタンスのメモリ消費量がかなり違います。検索用インデックスでも作っているのでしょうか。
画像/映像フォルダ内のコンテンツ量が増えるにつれ、アプリケーションで使用できるメモリ量が少なくなってしまいます。動作保障コンテンツ数などを決める必要があるのかもしれません。iPhone/iPad/iPodのデフォルトの写真ビューアも画像が80,000枚程度保存されていると強制終了するようになりますし。
ALAssetsLibraryChangeNotificationはバックグラウンド状態では受信できない
1. 対象のアプリケーションをバックグラウンド状態にする
2. 他のアプリケーションを起動/フォアグラウンド状態にする
3. 他のアプリケーションで画像/映像フォルダに変更が生じる(新たな画像を追加する、など)
4. 他のアプリケーションを終了/バックグラウンド状態にする
5. 対象のアプリケーションをフォアグラウンド状態にする
上記のようなアプリケーションの切り替えなどを行った場合、対象のアプリケーションがALAssetsLibraryChangeNotificationを受信するのは5のタイミングです。3の画像/映像フォルダに変更が生じた際ではなく、5の対象のアプリケーションがフォアグラウンド状態になったときです。
iTunesの同期ではALAssetsLibraryChangeNotificationは発生しない
iTunesの同期により画像/映像フォルダに変更が生じても、ALAssetsLibraryChangeNotificationは発生しません。
iTunesで同期すると画像のEXIFは消失する
PCからiTunes経由でiPhone/iPad/iPodに画像を転送した場合、転送元のPC上の画像にEXIFが付属していたとしても、iPhone/iPad/iPodに転送される際に削除されるようです。
下記参考文献のiOS4プログラミングブックの中でも次のように述べられています。
PC上のオリジナル画像がExif情報付きだったとしても、同期するタイミングで情報が削られているようです。
ただし、iPhone/iPad/iPodのカメラで撮った写真に関しては、iTuneの同期でEXIFは削除されません。おそらくApple独自のメタデータ形式などがあるのでしょう。
iTunesで同期すると画像はJpegとして保存される
ALAssetsLibraryと直接関係はないですが、iTunesで同期すると、bmpもpngもjpegに変換されてiPhone/iPad/iPod上に保存されるようです。
画像/映像の読み込みと新規書き込みのみ可能
ALAssetsLibraryでは、画像/映像の読み込みと新規書き込みのみが可能で、削除や修正/上書きなどはできないようです。
参考
公式リファレンス
ALAssetsLibrary Class Reference
Apple公式のリファレンスです。
読み込み
iOS 4の新機能13選&AssetsLibraryで作る画像ビューア (2/4):SDKで始めるiPad/iPhoneアプリ開発の勘所(終) - @IT
ALAssetsLibraryを使用した画像ビューアの作成方法。ソースコードのダウンロードもできます。
iPhoneの画像からEXIF,GPSデータを取得する ~ guess what?
GPSデータの読み込みなど。
書き込み
[AssetsLibrary] フォトライブラリの写真にExif情報を付けて保存する - Ni chicha, ni limona - 平均から抜けられない僕 - iPhoneアプリ開発グループ
画像データをEXIF付きで保存する。
書籍
- 作者: 畑圭輔,加藤寛人,坂本一樹,藤川宏之,高橋啓治郎,沖田知彦,柳澤昇
- 出版社/メーカー: インプレス
- 発売日: 2011/01/27
- メディア: 単行本(ソフトカバー)
- 購入: 26人 クリック: 910回
- この商品を含むブログ (33件) を見る
私が知っている限り、ALAssetsLibraryについて書かれている日本語の本はこれだけです。
docx, xlsx, pptxファイルからテキストをXMLとして抽出する
MS Office 2007以降のファイル(.docx, .xlsx, .pptx)はZip圧縮されたXMLファイルなので、ファイルからのテキスト抽出などが簡単にできます。
.pptxファイルをZipファイルとして展開すると上記のようになります。
各スライドがXMLファイルとして保存されています。
.docx, .xlsx, .pptxはそれぞれディレクトリ構造が若干違います。
読み取り、もしくは書き込みパスワードが設定されている場合はZipファイルとして解凍はできないようです。
import os.path import zipfile def _extract_xmls_from_msxml_base(filepath, prefix): xmls = [] zf = None try: zf = zipfile.ZipFile(filepath, 'r') for name in zf.namelist(): if name.startswith(prefix): xmls.append(zf.read(name)) except zipfile.BadZipfile, e: # for locked files print e finally: if zf is not None: zf.close() return xmls def extract_xmls(filepath, ext=None): if ext is None: root, ext = os.path.splitext(filepath) ext = ext.lower() prefix = None if ext == '.pptx': prefix = 'ppt/slides/slide' elif ext == '.xlsx': prefix = 'xl/worksheets/sheet' elif ext == '.docx': prefix = 'word/document' if prefix is not None: return _extract_xmls_from_msxml_base(filepath, prefix) return None def _test(): print extract_xmls('test.pptx') if __name__ == '__main__': _test()