ofxJpegGlitch解説

ofxJpegGlitch (http://github.com/2bbb/ofxJpegGlitch/) の解説記事です.

参考にしたのはこちらのサイト. http://www.setsuki.com/hsp/ext/jpg.htm
jpgのフォーマットの解説ですね.

取り敢えず, jpgのフォーマットをきちんと読みたい.

一番大外の構造としてはJFIFって構造があると. 0xFFD8ってバイト列で始まって0xFFD9ってバイト列で終わればよろしいと.
で, そういう0xFFXXという2バイトのデータをマーカーと呼ぶらしい, と.
マーカーをマジックナンバーで書くと混乱するので2バイト目を定数化しましょうってのが JpegConstant.h ですね.
ここのMarkerType ofxJpegGlitch::calcMarkerType(unsigned char *bytes, int cur) で unsigned charでマーカーの値書いてenumの名前返してるのも頭悪いなーとは思ってるのでそのうち何とかしたいですね.

で, 0xFFD8〜0xFFD9の間のデータはどういう構造してるかっていうと

マーカー: 2byte
データ長: 2byte
実データ部: データ長で表されたバイト数


っていうセグメントに分かれてる, と.

ということは大雑把なフローはここで決まって

bytestream *stream = /* jpgファイルストリーム */;
データ marker = /* 2バイト読み込み */;
if(marker != 0xFFD8) exit("これjpgちげーよバカ");
while(true) {
    データ marker = /* 2バイト読み込み */;
    MarkerType mt = getMarkerType(marker);
    if(mt == MT_UNKNOWN) exit("マーカーおかしいよ");

    if(mt == 0xFFD9) break; // 終了マーカー

    int32_t length = /* 2バイト読み込み */;
    データ data = /* lengthバイト読み込み */l
    switch(mt) {
        /* セグメントタイプによる諸々 */;
    }
}
/* ファイル書き込み */

ってことですね. あとはどこを壊せば美味しいか, どこを壊しちゃ駄目かを知れば宜しいと.

まあ, ヘッダーデータ壊すとあんまり良く無さそうですよね. 画像サイズとか壊すのも一興ではあるけど取り扱いめんどくさくなりますよね. メターデータ壊しても絵には影響しないし.
APPn系マーカーはAPP0のJFIFデータくらいしか読まなくても良さそうです.
APP0
ぶっちゃけAPP0も読まなくても良いくらいです. ここら変壊すと地雷臭もします. JFIF識別子壊しちゃいけませんし, サムネイルイメージとか今の僕らには関係ありません. 壊したければ壊してみれば良いですね. それは自由です.
APP1のEXIF改竄したら浮気した時に言い訳したりするのに使えるかも知れませんがそもそも浮気しちゃ駄目です.

で, 色々あるんですが取り敢えず飛ばしてSOSマーカー以降のデータ部分, こいつは幾ら壊しても結構ですがあんまり壊し過ぎるとグレーになって面白くないので節度を持って壊しましょう.

そうそう, 壊すって適当に言ってますが実際適当で良いです.
どんくらい適当かっていうと

data[cur] = rand() % 256; /* rand() & 0xFF って書くとオールドスクールっぽい*/;

くらい適当で良いです.
(ちゃんと壊したかったらもっとちゃんと符号化の方法まで調べてみるのも楽しいかと思います.)

で, だいぶ説明がめんどくさくなってるのですがここがある意味yoppa氏の入門記事: 画像をグリッ (54ページ目)との違いで, DQTとかDHTの部分. 量子化テーブル定義(Define Quantization Table segment)とハフマン符号テーブル定義セグメント(Define Huffman Table segmet)ですね.
こいつらがjpgフォーマットの肝なので心して壊してください.
説明はめんどくさいんで超完結に書きますが, DQTとかDHTのセグメント形式見て実データ部分(DQTだったら量子化因子QF00〜QF77くらい, DHTだったら各コード長のコード辺り)を丁寧にrand() % 256;で壊します.
そこらへんはofxJpegGlitchでsetDataGlitchness(0);してやってデータ部分を壊す可能性を0にしてsetQNGlitchnessとかsetDHTGlitchness辺りだけ変えてやってみると多分なかなか簡易グリッチではお目に掛かれないjpgグリッチが出るかと思います. (運が良いと簡易でも見れます)

あとは雰囲気で.

で, まとめとして, ofxJpegGlitchは単純に適当な位置をrand() % 256;するのとは違って, きちんとインテリ眼鏡な感じでフォーマットに従ってお上品かつ気品ある壊し方をすることによって壊したらjpg読み込めないぜって可能性を減らし(その可能性は0では無いです. この世は数学以外で絶対なんて言葉は存在しない.), コントロールパラメータを3つ(Data, QN, DHT)に増やしたことによって表現の幅を多少広げたことにある, と, いうことにしています. 知らんけど.

まあ, jpgグリッチなんて基本的にバカでも作れるんだけど地方底辺国立大学程度の学力で壊したらどうなるかっていうこと試した感じですね.

ソースコードはテンプレートゴリゴリboost黒魔術ゴリゴリの難しいC++ではなくて愚鈍な低レベルCなので頑張れば読めるはずなので上に書いてあることを踏まえてコードリーディングしてバグ見つけてPRしてくださいね.

以上.

iOS8でハマったこと.

以下の情報は物凄くちゃんと調べたわけでは無いのでご注意ください.
ちなみにXcodeはVersion 6.0.1 (6A317)での話. (後々古くなったときの為に追記)

Anti Taggingが動かない.

そんな一報から僕の平穏は終わった.
それは大変だ, でも俺のiPhone5sだと普通に動いてるし5cでも動いてるぞ?
まさか, あいつのせいか…

あいつというのは言うまでもない, iOS8へのアップデートである.
以前, 想ひ出ブレイカーがiOS7に上がった瞬間pngフォーマットで壊す事が出来なくなった辛い過去を持つ僕はすぐに気付いた.

しかし, 今僕の手元にはiOS8環境が無い. 持ってる端末をアップデートしてしまうとあと一年はiOS7対応もしなければならないので僕にiOS開発の仕事が来なくなって餓死してしまうからそれだけは避けなければならない…
more大きいiPhone, iPhone VentiことiPhone 6 Plusを9/12に発注したんだ, もうすぐ届くさ.
そう思って一週間, やっと届いた.

そろそろ物語調も疲れたので普通に.

まずやったことはAnti Taggingのjpgグリッチ→読み込み部分をUIImageJPEGRepresentation[UIImage imageWithData:]のlibjpegへの差し替え. 正確にはlibjpeg-turbo.
ここはすんなり行った.
imageWithData:で壊れたjpegデータを読み込むときに物凄く敷居が高くなってたのは回避.
しかし, 如何せん画像データがおかしい. 1pxが3byte or 4byteとか縦横のサイズ間違えたとかそんなん比較にならない程斜めってたりする. かと思えば意外と斜めってないこともある.
なんだこれ, と思ってCGImageRefをダンプするコードを書いてみた所

CGImageGetWidth:  648
CGImageGetHeight: 820
CGImageGetColorSpace:  (kCGColorSpaceDeviceRGB)
CGImageGetBitsPerPixel:     32
CGImageGetBitsPerComponent: 8
CGImageGetBytesPerRow:      3840
CGImageGetBitmapInfo: 0x00002006
  kCGBitmapAlphaInfoMask     = YES
  kCGBitmapFloatComponents   = NO
  kCGBitmapByteOrderMask     = 0x00002000
  kCGBitmapByteOrderDefault  = NO
  kCGBitmapByteOrder16Little = NO
  kCGBitmapByteOrder32Little = YES
  kCGBitmapByteOrder16Big    = NO
  kCGBitmapByteOrder32Big    = NO


みたいな. (上のデータは飽くまでも雰囲気)
注目すべきは CGImageGetWidth * CGImageGetBitsPerPixel / CGImageGetBitsPerComponent != CGImageGetBytesPerRow になってること.
顔の部分をクロップしてるんだけど, 横がおかしい.
何度かやってみてwidth, heightはちゃんと変わってるけどCGGetBytesPerRowが3840固定になってるっぽい.
クロップは CGImageCreateWithImageInRect でやってたんだけど, こいつが返すCGImageRefは与えたrectをメタ情報的に持って元のピクセルデータは元の画像のをそのまま参照してるんじゃないかと憶測. 縦に切り出すのはmemcpyでさくっといけるけどwidth側はheight分切り出してくっ付けてみたいな作業居るし大変だしね, うんうん. とか思いつつ, context作って素直に切り出し部分をきちんと生成するように変更.
具体的には

CGImageRef imageRef = CGImageCreateWithImageInRect(image.CGImage, rect);
UIImage *cropped = [UIImage imageWithCGImage:imageRef];
CGImageRelease(imageRef);


みたいなのを

UIGraphicsBeginImageContext(rect.size);
[image drawInRect:CGRectMake(-rect.origin.x, -rect.origin.y, image.size.width, image.size.height)];
UIImage *cropped = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();


みたいな感じに変更してみた.

うん. うまく行った. よしよし. 申請だ!
Xcode6での申請も色々はまりましたがprovとcertな問題だったので割愛. というかよく分かってないけど申請出来たから良いんだよ.

そして満足して海外で幾つか紹介されてるのニヤニヤしてたんだけど気付いてしまった.
想ひ出ブレイカーもjpg壊れねーじゃねーか.

悲しい. too 悲しい. I’m too 悲し過ぎる. それは too bad.
まあ, libjpeg-turboのビルドおじさんは済ませてるし, 想ひ出ブレイカーはクロップしねーし楽勝でしょう. ふふん. 秒殺だな.

等と思いつつ修正に入った所, ソースコードが古い. ARCじゃないし俺俺ライブラリも古いしiOS7のときにやったlibpngもarm64対応させてなかったぽいし酷い.
いや, 今は一刻を争ってるんだ, そこは目を瞑ろう. 取り敢えずlibjpegに差し替えよう.
うん. 動いた. ふー. 楽勝だったぜ. 申請するか.
その前にちゃんと一通り動作確認するかー.
およ?pngとtiffがおかしいぞ?斜めってるぞ?およ?

ということで苦悶の時間.
現実逃避でlibpngとかもビルドし直したし俺俺ライブラリも差し替えたし, ARCにも対応させたし, でもやっぱりpngとtiffがおかしい.
それで動作が変わらない事は分かってたけども.

色々やった所, max1024に合わせるリサイズモードにしてると斜める. ふむ.

でも, まあ, 結局斜めってるんでしょ. はいはい, cgimageダンプすりゃ良いんでしょ, はいはい.
ダンプした所

CGImageGetWidth:  769
CGImageGetHeight: 1025
CGImageGetBitsPerPixel:     32
CGImageGetBitsPerComponent: 8
CGImageGetBytesPerRow:      3104

うん. なんと言う事でしょう. 突っ込みどころが二つ.
769×1025って何. あたしはmax1024でリサイズしてるのよ?ソースコード見てごらんなさいよ.

    if(withResize) {
        int max = fmaxf(size.width, size.height);
        float scale = 1024.0f / max;
        size.width *= scale;
        size.height *= scale;
    }


なのよ?あなたおかしくない?
そしてまたCGImageGetBytesPerRowがズレてらっしゃるわよ?もうやだやだ…

取り敢えずちゃんと見てみましょうよと size.width, size.height をプリンツ.

LogTrace(@"size: %f, %f", size.width, size.height);


と.

768.000027, 1024.000036


やだ, これだから浮動小数点ってば…
でも普通切り捨てるじゃん. int width = size.width だったら切り捨てんじゃん!
とは言ってもどうやらUIGraphicsBeginImageContextWithOptionsで作ったcontextは切り上げられちゃう感じなのでもう良いよと

        size.width *= scale;
        size.height *= scale;
        size.width = (int)size.width;
        size.height = (int)size.height;


とだいぶやけっぱちなコードを書いて乙事主様の怒りを鎮めたのでした. めでたしめでたし.

と思ったけどやっぱり駄目. よく考えたら想ひ出ブレイカーさん, 任意の画像読み込むんだから縦長の画像で1024にフィットさせたら横幅が8の倍数じゃない感じの厭らしい子に当たったら同じ事起きるかもしれないじゃない!
それは美しくない!!きちんと礼儀正しくぶっ壊すのがアタシの使命なの!グリッチ風とかなんちゃってグリッチとかあたし認めないの!!世に蔓延るグリッチ風画像が作れるiOSアプリなんて糞食らえですのよ!!
ってことでlibpngとかに食わせるバッファーの読み込ませ方をCGImageGetBytesPerRowを意識したお行儀の良い感じのコードに変えて上のキャストしなくてもちゃんと動く様にもしました. 1025の画像とかマジファッキンだから上のキャストも最終的にやっぱり入れたけど.

実際の所, 上の問題はちゃんと追ってないから元々UIGraphicsBeginImageContextWithOptionsで作った画像はサイズ切り上げだったけど8byteパディングされてなかったのかもしれないんだけど. まあ解決したから良し.

とは言え, ちゃんと色んなサイズで試したわけじゃないので知らないけどね. まあ, そういう壊れ方も良いじゃん?って言い張れば良いしね. うん.

てことでCoreGraphics周り地味に変わってね?
ピクセルデータへのアクセスとかそんなたき火の周りで葉っぱ食ってラリってるシャーマンみたいなことやんなよボケってApple様に言われてる様な気もしますが, 僕はピクセルデータへのアクセスが大好きですし, GLSLとかいうよく分からん化け物を手懐けて

gl_FragColor  = f(texture2D(tex, pos))


とか書くよりも

for(int j = 0; j < height; j++) {
  for(int i = 0; i < width; i++) {
    for(unsigned char c = 0; c < bpp; c++) {
      buf[bpp * (i + j * width) + c] = f(pix[bpp * (i + j * width) + c]);
    }
  }
}


とか

memcpy(buf, pix, width * height * bpp);


みたいなレガシートロンみたいなソースコードを書き続けてたいんです. 死ぬまで.

Anti Taggingも想ひ出ブレイカーももう暫くでちゃんとアップデートされるはずなのでjpgグリッチフェティシズムな方々, もう少々お待ちください.

てことで, iOS8, 地味に変わってるっぽいので皆様ご注意を.

追記:
そういえばXcode6からiOSプロジェクトとか作ったときにprefix.pchが作られなくなりましたね. マジ不便.
イカれた解決策知ってる方はご一報ください.

youtube mosaic

下のリンクをブックマークに追加してyoutubeとかyoutube埋め込まれたりしてるページで.
youtube mosaic

同じ感じで画像も大体モザイクする感じの.
image mosaic

ofxAppleScript / ofxMacControl

openframeworksからAppleScriptを実行するaddon
ofxAppleScript

上のaddonを使ってログオフ, スリープ, リブート, シャットダウンを実行する関数をまとめたaddon
ofxMacControl

当然どちらもMac専用です.

ofxLinguisticUtilities

ofxLinguisticUtilities

OSXのNSLinguisticTaggerとCFStringTransformをC++から叩ける様なラッパーを書こうと思い立ち取り敢えず公開.

NSLinguisticTaggerは日本語だとワードで区切るくらいしか出来ないけど英語だと品詞解析まで出来ます.

CFStringTransformは平仮名をローマ字に変換したり, 英語をギリシャ文字にしたり出来ます. が, 漢字から英語への変換は中国語扱いに…

ボチボチと機能追加していきます.

openframeworks + LLVM + -std=c++11 = …

ofでC++11のlambdaとか使いたいなー.
とか思ってLLVMの設定のC++ Language DialectをC++11 [-std=c++11] にしたらコンパイル通らない.
ofTypes.hで転ける.

なんでだと思って色々調べてたら

#if __cplusplus<201103L
	using std::tr1::shared_ptr;
	using std::tr1::weak_ptr;
	using std::tr1::enable_shared_from_this;
#endif


の部分が良くないらしい.

-std=c++11にすると 201103L<=__cplusplus になってるんだけどまだ名前空間としては std::tr1::shared_ptr とかになってるらしい.

これ, ofのgithubにissue立てた方が良いのかな...?でもXcode+LLVMでしか調べてないので怖くて立てれない.
それよりも英語が怖くて立てれない.
give me english power...

追記:
取り敢えず

#if __cplusplus<201103L || defined(__clang__)
	using std::tr1::shared_ptr;
	using std::tr1::weak_ptr;
	using std::tr1::enable_shared_from_this;
#endif


にして何とか.

追々記:
issue立ってた.
「clang __cplusplus 201103L shared_ptr」で検索したら一番上に出て来たからきっと俺と同じ様なことをやってたんだろうな...
https://github.com/openframeworks/openFrameworks/issues/2335

最近書いたof用addon紹介

of0.8.0出ましたね.
そんな中早速ofxiPhoneがofxiOSになったり皆が慣れ親しんだtestAppがofAppに変わったり色々と色々ですが, 夏ですね.

ということで, 最近全然クロスプラットフォームじゃないaddonを書いては公開(at github)してるので紹介.

ofxCIDetector

github:ofxCIDetector
CoreImageの顔認識ライブラリCIDetectorをofxiOS用に包んだaddon.
クラスにofImage投げたら輪郭, 目, 口の情報が返って来る. まだgrayscaleの画像には対応してない. (このことREADMEにも書かなきゃ…)

ofxVoiceSynthesizer

github:ofxVoiceSynthesizer
OSXのText2SpeechライブラリNSSpeechSynthesizerを包んだaddon.
iOSでもprivateであるけど公開出来ないから一応osxのみ. 需要があればiOS用も書くけど…

ofxCoreMIDI

github:ofxCoreMIDI
OSXのMIDIライブラリCoreMIDIを包んだaddon.
まだまだだけど取り敢えずなんか受信は出来るし公開したって状態.
でもちゃんと調べたらofxMIDIっていうちゃんとしてるっぽいaddonあったしもう良いかなって気も.

ofxiGKSession

github:ofxiGKSession
iOSのGameKitの特にBluetooth通信部分であるGKSessionを包んだaddon.
設計的にofEventにすべきかどうか悩んだけどiOS脳になってるのでdelegate的なクラスを継承するって形で実装.
ofEvent投げるようなサブクラス使えばそういうofEventも使えるし, それはオプションで近々書くつもり.
Server-Clientモデルはあんまり試してないのでまだまだ改良の余地あり.

ofxTBTexture

github:ofxTBTexture
俺がよく使うofTextureの拡張drawメソッドをちゃんとクラス化したもの.
主に長方形以外の図形にフォーカス. ドキュメント書くのめんどくさい.
一応疑似補間みたいなこともやってるけど精度は…

ofxObjective-C++Utility

github:ofxObjective-C–Utility
githubのURL制限のせいで残念な感じになったObjective-C++用ユーティリティ. かなりニッチ.
NSStringとstringの変換とかvectorからNSArrayへの変換とか. あとof向けにofPointとCGPoint, ofRectとCGRect, ofBufferとNSDataとかの変換も.
vectorからNSArrayへの変換はblocks書けないと辛い. ofっぽくないよなーとは思ってたり.
関数ポインタとか変換するだけのインターフェイス作ってそれを継承したクラス作るようにしても良いっちゃ良いんだけど…

ということで, 全然まだまだ改良の余地のあるα版ばかりですが, 何か興味があったら使って貰えると. そんでバグがあったらissue立てて貰ったり勝手に修正してpull request投げてもらったりしたら!勝手に修正してpull request投げてもらったりしたりしたら!!
あ, あと, mdとかの英語で変な所とかもあったら是非指摘してください!英語難しい. ドキュメントも全部Objective-C++で書きたい.

よろしくお願いします.

UITableViewCellをIBで

参考にしたサイト:
http://hamken100.blogspot.jp/2013/01/ios-uitableviewcell.html
http://blogios.stack3.net/archives/195

はまったこと:
・File’s Ownerをカスタムセルのクラスにした
 → File’s OwnerはNSObject
・それに関連してFile’s Ownerにviewをガンガン繋いだ
 → 駄目!絶対!
・cellのheight…?
 → UITableView側で設定した
  →セルの高さ可変だったらどうすんの?
・Background Colorがtransparentになって反映されなかった
 ・更にCellを使う時にsetBackgroundColorしても駄目
 → background viewを自前で作って繋いでおく
  → これが正解かどうか分かってないけど取り敢えず動いた.

いつまでもUITableViewにはiOSプログラムの鬼門であって欲しいものです. クソ…

ofxVoiceSynthesizer

ついでに前から作ってたNSSpeechSynthesizerをofから叩ける様にする ofxVoiceSynthesizer も公開しました. OSX only.

ofxCIDetector

iPhone用of向けに超シンプルなCIDetectorのラッパーを書きました.

ofxCIDetector

早めにgrayscaleにも対応します.

MacだとofxOSXFaceDetector ってのが既にあります.