yoppa.org


多摩美 - “iTamabi” – iPhoneアプリ開発プロジェクト 2010

openFrameworks + iPhoneで、オリジナルのカメラアプリを作る

今日の目標 – オリジナルのカメラアプリを作成する

  • iPhoneのカメラを使用して、オリジナルのカメラアプリを作成してみる
  • openFramworks for iPhoneを使用すると、簡単に内蔵カメラの画像を使用できる
  • 内蔵カメラから画像イメージを取得するには、ofxiPhoneImagePicker アドオンを使用する

カメラアプリ1:写真を撮影する

  • 内蔵カメラで写真を撮影し、撮影した写真を画像(ofImage)にコピーして表示する
  • 以降のカメラアプリの基本となる機能

testApp.h

[code language=”cpp”]
#pragma once

#include "ofMain.h"
#include "ofxiPhone.h"
#include "ofxiPhoneExtras.h"

//シミュレータを使用する際には、コメントアウトを外す
//#define _USE_SIMULATOR

class testApp : public ofxiPhoneApp {

public:
void setup();
void update();
void draw();
void exit();

void touchDown(ofTouchEventArgs &touch);
void touchMoved(ofTouchEventArgs &touch);
void touchUp(ofTouchEventArgs &touch);
void touchDoubleTap(ofTouchEventArgs &touch);

void lostFocus();
void gotFocus();
void gotMemoryWarning();
void deviceOrientationChanged(int newOrientation);

//カメラの画像を一時的に格納する領域
unsigned char * cameraPixels;
//カメラを使用するためのアドオン
ofxiPhoneImagePicker * camera;
//撮影した画像データ
ofImage photo;
//現在の状態を格納
//0:撮影待ち, 1:画像処理, 2:処理終了(待機)
int status;

};
[/code]

testApp.mm

[code language=”cpp”]
#include "testApp.h"

//————————————————————–
void testApp::setup(){
// iPhone用初期設定
ofRegisterTouchEvents(this);
ofxAccelerometer.setup();
ofxiPhoneAlerts.addListener(this);
ofEnableSmoothing();
ofSetCircleResolution(64);
ofEnableAlphaBlending();

//カメラ初期設定
cameraPixels = NULL;
camera = new ofxiPhoneImagePicker();
camera->setMaxDimension(480);

status = 0;
}

//————————————————————–
void testApp::update()
{
#ifndef _USE_SIMULATOR
//実機使用の場合
if(camera->imageUpdated){
//カメラのメモリ領域を確保
if (cameraPixels == NULL){
cameraPixels = new unsigned char [camera->width * camera->height*4];
}

//カメラの画像のままだと上下反転してるので、イメージの上下を反転
for (int i = 0; i < camera->height; i++){
memcpy(cameraPixels+(camera->height-i-1)*camera->width*4, camera->pixels+i*camera->width*4, camera->width*4);
}

//カメラから取得したイメージを、処理用のofImage(photo)にコピーする
photo.setFromPixels(cameraPixels, camera->width, camera->height, OF_IMAGE_COLOR_ALPHA);
camera->imageUpdated = false;
status = 1;
}
#endif
}

//————————————————————–
void testApp::draw()
{
if (status == 0) {
//撮影を促す画面を表示
ofSetColor(255, 255, 255);
ofDrawBitmapString("Double tap on the screen!!", 40, ofGetHeight()/2-5);
}

if(status == 1){
//撮影した画像を表示
photo.draw(0, 0);
}
}

//————————————————————–
void testApp::exit() {
}

//————————————————————–
void testApp::touchDown(ofTouchEventArgs &touch){

}

//————————————————————–
void testApp::touchMoved(ofTouchEventArgs &touch){

}

//————————————————————–
void testApp::touchUp(ofTouchEventArgs &touch){
}

//————————————————————–
void testApp::touchDoubleTap(ofTouchEventArgs &touch){
#ifdef _USE_SIMULATOR
//シミュレータ使用の場合:画像をロード
photo.loadImage("images/photo.png");
photo.setImageType(OF_IMAGE_COLOR_ALPHA);
status = 1;
#else
//実機使用の場合:写真を新規に撮影
camera->openCamera();
#endif
}

//————————————————————–
void testApp::lostFocus() {
}

//————————————————————–
void testApp::gotFocus() {
}

//————————————————————–
void testApp::gotMemoryWarning() {
}

//————————————————————–
void testApp::deviceOrientationChanged(int newOrientation){
}
[/code]


写真撮影を促す画面


カメラから取得された画像

カメラアプリ2:モザイクカメラ

  • 撮影した写真をモザイク状に加工する
  • 撮影した写真のピクセル情報を一定間隔でとりだして、取得したRGB情報をもとに四角形を描いている
  • 写真画像ののピクセル情報は、getPixel() 関数を用いて取り出している
  • 写真のイメージは、1ピクセルごとにRGBA(Red, Green, Blue, Alpha)の4つの情報が含まれている
  • ピクセルをスキャンしていく際には、4ピクセルを1単位として扱うように留意する

testApp.h

[code language=”cpp”]
#pragma once

#include "ofMain.h"
#include "ofxiPhone.h"
#include "ofxiPhoneExtras.h"

//シミュレータを使用する際には、コメントアウトを外す
//#define _USE_SIMULATOR

class testApp : public ofxiPhoneApp {

public:
void setup();
void update();
void draw();
void exit();

void touchDown(ofTouchEventArgs &touch);
void touchMoved(ofTouchEventArgs &touch);
void touchUp(ofTouchEventArgs &touch);
void touchDoubleTap(ofTouchEventArgs &touch);

void lostFocus();
void gotFocus();
void gotMemoryWarning();
void deviceOrientationChanged(int newOrientation);

//カメラの画像を一時的に格納する領域
unsigned char * cameraPixels;
//カメラを使用するためのアドオン
ofxiPhoneImagePicker * camera;
//撮影した画像データ
ofImage photo;
//現在の状態を格納、0:撮影待ち, 1:画像処理, 2:処理終了(待機)
int status;
};
[/code]

testApp.mm

[code language=”cpp”]
#include "testApp.h"

//————————————————————–
void testApp::setup(){
// iPhone用初期設定
ofRegisterTouchEvents(this);
ofxAccelerometer.setup();
ofxiPhoneAlerts.addListener(this);
ofEnableSmoothing();
ofSetCircleResolution(64);
ofEnableAlphaBlending();

//カメラ初期設定
cameraPixels = NULL;
camera = new ofxiPhoneImagePicker();
camera->setMaxDimension(480);

status = 0;
}

//————————————————————–
void testApp::update()
{
#ifndef _USE_SIMULATOR
//実機使用の場合
if(camera->imageUpdated){
//カメラのメモリ領域を確保
if (cameraPixels == NULL){
cameraPixels = new unsigned char [camera->width * camera->height*4];
}

//カメラの画像のままだと上下反転してるので、イメージの上下を反転
for (int i = 0; i < camera->height; i++){
memcpy(cameraPixels+(camera->height-i-1)*camera->width*4, camera->pixels+i*camera->width*4, camera->width*4);
}

//カメラから取得したイメージを、処理用のofImage(photo)にコピーする
photo.setFromPixels(cameraPixels, camera->width, camera->height, OF_IMAGE_COLOR_ALPHA);
camera->imageUpdated = false;
status = 1;
}
#endif
}

//————————————————————–
void testApp::draw()
{
if (status == 0) {
//撮影を促す画面を表示
ofSetColor(255, 255, 255);
ofDrawBitmapString("Double tap on the screen!!", 40, ofGetHeight()/2-5);
}

if(status == 1){
//撮影した画像を表示
photo.draw(0, 0);

//画像データのビットマップ情報を配列に格納
unsigned char * pixels = photo.getPixels();

//画像の幅と高さを所得
int w = photo.width;
int h = photo.height;

//スキャンする間隔
int skip = 20;

//モザイク化:等間隔にピクセルの色を検知し、四角形を描く
for (int j = 0; j < h; j+=skip){
for (int i = 0; i < w; i+=skip){
int valueR = pixels[j*4*w + i*4];
int valueG = pixels[j*4*w + i*4+1];
int valueB = pixels[j*4*w + i*4+2];
ofSetColor(valueR,valueG,valueB);
ofRect(i, j, skip, skip);
}
}
}
}

//————————————————————–
void testApp::exit() {
}

//————————————————————–
void testApp::touchDown(ofTouchEventArgs &touch){

}

//————————————————————–
void testApp::touchMoved(ofTouchEventArgs &touch){

}

//————————————————————–
void testApp::touchUp(ofTouchEventArgs &touch){
}

//————————————————————–
void testApp::touchDoubleTap(ofTouchEventArgs &touch){
#ifdef _USE_SIMULATOR
//シミュレータ使用の場合:画像をロード
photo.loadImage("images/photo.png");
photo.setImageType(OF_IMAGE_COLOR_ALPHA);
status = 1;
#else
//実機使用の場合:写真を新規に撮影
camera->openCamera();
#endif
}

//————————————————————–
void testApp::lostFocus() {
}

//————————————————————–
void testApp::gotFocus() {
}

//————————————————————–
void testApp::gotMemoryWarning() {
}

//————————————————————–
void testApp::deviceOrientationChanged(int newOrientation){
}
[/code]


モザイク化されたイメージ

カメラアプリ3:スキャンカメラ

  • モザイクの応用
  • ある高さ(Y座標)のピクセル情報のみとりだす
  • 全てのX座標のRGBAの値を取得して、縦に拡げてバーコード状に表示する
  • 取得する高さは、画面をタッチして変更できるようにする
  • 写真を指定したラインでスキャンするような効果を得ることができる

testApp.h

[code language=”cpp”]
#pragma once

#include "ofMain.h"
#include "ofxiPhone.h"
#include "ofxiPhoneExtras.h"

//シミュレータを使用する際には、コメントアウトを外す
//#define _USE_SIMULATOR

class testApp : public ofxiPhoneApp {

public:
void setup();
void update();
void draw();
void exit();

void touchDown(ofTouchEventArgs &touch);
void touchMoved(ofTouchEventArgs &touch);
void touchUp(ofTouchEventArgs &touch);
void touchDoubleTap(ofTouchEventArgs &touch);

void lostFocus();
void gotFocus();
void gotMemoryWarning();
void deviceOrientationChanged(int newOrientation);

//カメラの画像を一時的に格納する領域
unsigned char * cameraPixels;
//カメラを使用するためのアドオン
ofxiPhoneImagePicker * camera;
//撮影した画像データ
ofImage photo;
//現在の状態を格納、0:撮影待ち, 1:画像処理, 2:処理終了(待機)
int status;
//イメージをスキャンするY座標
int scanHeight;
};
[/code]

testApp.mm

[code language=”cpp”]
#include "testApp.h"

//————————————————————–
void testApp::setup(){
// iPhone用初期設定
ofRegisterTouchEvents(this);
ofxAccelerometer.setup();
ofxiPhoneAlerts.addListener(this);
ofEnableSmoothing();
ofSetCircleResolution(64);
ofEnableAlphaBlending();

//カメラ初期設定
cameraPixels = NULL;
camera = new ofxiPhoneImagePicker();
camera->setMaxDimension(480);

status = 0;
scanHeight = 0;
}

//————————————————————–
void testApp::update()
{
#ifndef _USE_SIMULATOR
//実機使用の場合
if(camera->imageUpdated){
//カメラのメモリ領域を確保
if (cameraPixels == NULL){
cameraPixels = new unsigned char [camera->width * camera->height*4];
}

//カメラの画像のままだと上下反転してるので、イメージの上下を反転
for (int i = 0; i < camera->height; i++){
memcpy(cameraPixels+(camera->height-i-1)*camera->width*4, camera->pixels+i*camera->width*4, camera->width*4);
}

//カメラから取得したイメージを、処理用のofImage(photo)にコピーする
photo.setFromPixels(cameraPixels, camera->width, camera->height, OF_IMAGE_COLOR_ALPHA);
camera->imageUpdated = false;
status = 1;
}
#endif
}

//————————————————————–
void testApp::draw()
{
if (status == 0) {
//撮影を促す画面を表示
ofSetColor(255, 255, 255);
ofDrawBitmapString("Double tap on the screen!!", 40, ofGetHeight()/2-5);
}

if(status == 1){
//撮影した画像を表示
photo.draw(0, 0);

//画像データのビットマップ情報を配列に格納
unsigned char * pixels = photo.getPixels();

//画像の幅と高さを所得
int w = photo.width;
int h = photo.height;

//モザイク化:等間隔にピクセルの色を検知し、四角形を描く
for (int i = 0; i < w; i++){
int valueR = pixels[scanHeight*4*w + i*4];
int valueG = pixels[scanHeight*4*w + i*4+1];
int valueB = pixels[scanHeight*4*w + i*4+2];
ofSetColor(valueR,valueG,valueB);
ofRect(i, 0, 1, h);
}

//今スキャンしている場所を表示
ofSetColor(255, 255, 255);
ofLine(0, scanHeight, ofGetWidth(), scanHeight);
}
}

//————————————————————–
void testApp::exit() {
}

//————————————————————–
void testApp::touchDown(ofTouchEventArgs &touch){

}

//————————————————————–
void testApp::touchMoved(ofTouchEventArgs &touch){
//画面をドラッグした場所で、スキャンする高さを決定
scanHeight = touch.y;
}

//————————————————————–
void testApp::touchUp(ofTouchEventArgs &touch){
}

//————————————————————–
void testApp::touchDoubleTap(ofTouchEventArgs &touch){
#ifdef _USE_SIMULATOR
//シミュレータ使用の場合:画像をロード
photo.loadImage("images/photo.png");
photo.setImageType(OF_IMAGE_COLOR_ALPHA);
status = 1;
#else
//実機使用の場合:写真を新規に撮影
camera->openCamera();
#endif
}

//————————————————————–
void testApp::lostFocus() {
}

//————————————————————–
void testApp::gotFocus() {
}

//————————————————————–
void testApp::gotMemoryWarning() {
}

//————————————————————–
void testApp::deviceOrientationChanged(int newOrientation){
}
[/code]


写真をスキャン

カメラアプリ4:ピクセレイトカメラ(グレイスケール版)

  • 写真イメージのピクセル情報を解析
  • 一定間隔ごとに写真のRGB別のレベル(明るさ)を取得して、その平均値をとる
  • その値で半径を変化させながら円を描いていく
  • モノクロで写真を印刷したようなドットによる濃淡の表現になる

testApp.h

[code language=”cpp”]
#pragma once

#include "ofMain.h"
#include "ofxiPhone.h"
#include "ofxiPhoneExtras.h"

//シミュレータを使用する際には、コメントアウトを外す
//#define _USE_SIMULATOR

class testApp : public ofxiPhoneApp {

public:
void setup();
void update();
void draw();
void exit();

void touchDown(ofTouchEventArgs &touch);
void touchMoved(ofTouchEventArgs &touch);
void touchUp(ofTouchEventArgs &touch);
void touchDoubleTap(ofTouchEventArgs &touch);

void lostFocus();
void gotFocus();
void gotMemoryWarning();
void deviceOrientationChanged(int newOrientation);

//カメラの画像を一時的に格納する領域
unsigned char * cameraPixels;
//カメラを使用するためのアドオン
ofxiPhoneImagePicker * camera;
//撮影した画像データ
ofImage photo;
//現在の状態を格納、0:撮影待ち, 1:画像処理, 2:処理終了(待機)
int status;
};
[/code]

testApp.mm

[code language=”cpp”]
#include "testApp.h"

//————————————————————–
void testApp::setup(){
// iPhone用初期設定
ofRegisterTouchEvents(this);
ofxAccelerometer.setup();
ofxiPhoneAlerts.addListener(this);
ofEnableSmoothing();
ofSetCircleResolution(64);
ofEnableAlphaBlending();

//カメラ初期設定
cameraPixels = NULL;
camera = new ofxiPhoneImagePicker();
camera->setMaxDimension(480);

status = 0;
}

//————————————————————–
void testApp::update()
{
#ifndef _USE_SIMULATOR
//実機使用の場合
if(camera->imageUpdated){
//カメラのメモリ領域を確保
if (cameraPixels == NULL){
cameraPixels = new unsigned char [camera->width * camera->height*4];
}

//イメージの上下を反転
for (int i = 0; i < camera->height; i++){
memcpy(cameraPixels+(camera->height-i-1)*camera->width*4, camera->pixels+i*camera->width*4, camera->width*4);
}

//カメラから取得したイメージを、処理用のofImage(photo)にコピーする
photo.setFromPixels(cameraPixels, camera->width, camera->height, OF_IMAGE_COLOR_ALPHA);
camera->imageUpdated = false;
status = 1;
}
#endif
}

//————————————————————–
void testApp::draw()
{
if (status == 0) {
//撮影を促す画面を表示
ofSetColor(255, 255, 255);
ofDrawBitmapString("Double tap on the screen!!", 40, ofGetHeight()/2-5);
}

if(status == 1){
//画像データのビットマップ情報を配列に格納
unsigned char * pixels = photo.getPixels();

//画像の幅と高さを所得
int w = photo.width;
int h = photo.height;

//ピクセレイトの半径を設定
int r = 12;

//画像を等間隔にスキャン
for (int j = 0; j < h; j+=r){
for (int i = 0; i < w; i+=r){
//ピクセルのRGBの値を取得
//RGBAの順番で並んでいるので、4ピクセルずつ
int valueR = pixels[j*4*w + i*4];
int valueG = pixels[j*4*w + i*4+1];
int valueB = pixels[j*4*w + i*4+2];

//RGBの値の平均値を算出
float value = r*(valueR+valueG+valueB)/2.0/255.0;

//取得したRGB値をもとに、円を描画
//取得したピクセルの明るさを、円の半径に対応させている
ofSetColor(255,255,255,100);
ofCircle(i, j, value);
}
}
}
}

//————————————————————–
void testApp::exit() {
}

//————————————————————–
void testApp::touchDown(ofTouchEventArgs &touch){

}

//————————————————————–
void testApp::touchMoved(ofTouchEventArgs &touch){

}

//————————————————————–
void testApp::touchUp(ofTouchEventArgs &touch){
}

//————————————————————–
void testApp::touchDoubleTap(ofTouchEventArgs &touch){
#ifdef _USE_SIMULATOR
//シミュレータ使用の場合:画像をロード
photo.loadImage("images/photo.png");
photo.setImageType(OF_IMAGE_COLOR_ALPHA);
status = 1;
#else
//実機使用の場合:写真を新規に撮影
camera->openCamera();
#endif
}

//————————————————————–
void testApp::lostFocus() {
}

//————————————————————–
void testApp::gotFocus() {
}

//————————————————————–
void testApp::gotMemoryWarning() {
}

//————————————————————–
void testApp::deviceOrientationChanged(int newOrientation){
}
[/code]


グレースケールでのピクセレイト

カメラアプリ5:ピクセレイトカメラ(カラー版)

  • ピクセレイトのカラー版
  • RGB別々に円を描いて、重ねていく
  • 描画する際の色の合成を、加算合成に変更するところがポイント
  • OpenGLの命令を直接使用する – glEnable(GL_BLEND); と glBlendFunc(GL_SRC_ALPHA, GL_ONE); で実現可能
  • カラー写真を印刷したようなドットによる濃淡の表現になる

testApp.h

[code language=”cpp”]
#pragma once

#include "ofMain.h"
#include "ofxiPhone.h"
#include "ofxiPhoneExtras.h"

//シミュレータを使用する際には、コメントアウトを外す
//#define _USE_SIMULATOR

class testApp : public ofxiPhoneApp {

public:
void setup();
void update();
void draw();
void exit();

void touchDown(ofTouchEventArgs &touch);
void touchMoved(ofTouchEventArgs &touch);
void touchUp(ofTouchEventArgs &touch);
void touchDoubleTap(ofTouchEventArgs &touch);

void lostFocus();
void gotFocus();
void gotMemoryWarning();
void deviceOrientationChanged(int newOrientation);

//カメラの画像を一時的に格納する領域
unsigned char * cameraPixels;
//カメラを使用するためのアドオン
ofxiPhoneImagePicker * camera;
//撮影した画像データ
ofImage photo;
//現在の状態を格納、0:撮影待ち, 1:画像処理, 2:処理終了(待機)
int status;
};
[/code]

testApp.mm

[code language=”cpp”]
#include "testApp.h"

//————————————————————–
void testApp::setup(){
// iPhone用初期設定
ofRegisterTouchEvents(this);
ofxAccelerometer.setup();
ofxiPhoneAlerts.addListener(this);
ofEnableSmoothing();
ofSetCircleResolution(64);

//カメラ初期設定
cameraPixels = NULL;
camera = new ofxiPhoneImagePicker();
camera->setMaxDimension(480);

status = 0;
}

//————————————————————–
void testApp::update()
{
#ifndef _USE_SIMULATOR
if(camera->imageUpdated){
//カメラのメモリ領域を確保
if (cameraPixels == NULL){
cameraPixels = new unsigned char [camera->width * camera->height*4];
}

//イメージの上下を反転
for (int i = 0; i < camera->height; i++){
memcpy(cameraPixels+(camera->height-i-1)*camera->width*4, camera->pixels+i*camera->width*4, camera->width*4);
}

//カメラから取得したイメージを、処理用のofImage(photo)にコピーする
photo.setFromPixels(cameraPixels, camera->width, camera->height, OF_IMAGE_COLOR_ALPHA);
camera->imageUpdated = false;
status = 1;
}
#endif
}

//————————————————————–
void testApp::draw()
{
if (status == 0) {
//撮影を促す画面を表示
ofSetColor(255, 255, 255);
ofDrawBitmapString("Double tap on the screen!!", 40, ofGetHeight()/2-5);
}

if(status == 1){
//背景の更新を終了
ofSetBackgroundAuto(false);
//画面の描画を加算合成に
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE);

//画像データのビットマップ情報を配列に格納
unsigned char * pixels = photo.getPixels();

//画像の幅と高さを所得
int w = photo.width;
int h = photo.height;

//ピクセレイトの半径を設定
int r = 12;

//画像を8ピクセル間隔でスキャン
for (int j = 0; j < h; j+=r){
for (int i = 0; i < w; i+=r){

//ピクセルのRGBの値を取得
int valueR = pixels[j*4*w + i*4];
int valueG = pixels[j*4*w + i*4+1];
int valueB = pixels[j*4*w + i*4+2];

//取得したRGB値をもとに、円を描画
//取得したピクセルの明るさを、円の半径に対応させている
ofSetColor(255, 0, 0, 63);
ofCircle(i, j, r*valueR/255.0*2.0);
ofSetColor(0, 255, 0, 63);
ofCircle(i, j, r*valueG/255.0*2.0);
ofSetColor(0, 0, 255, 63);
ofCircle(i, j, r*valueB/255.0*2.0);
}
}

//ステイタスを変更し、描画は1度だけに
status = 2;
}
}

//————————————————————–
void testApp::exit() {
}

//————————————————————–
void testApp::touchDown(ofTouchEventArgs &touch){

}

//————————————————————–
void testApp::touchMoved(ofTouchEventArgs &touch){

}

//————————————————————–
void testApp::touchUp(ofTouchEventArgs &touch){

}

//————————————————————–
void testApp::touchDoubleTap(ofTouchEventArgs &touch){
#ifdef _USE_SIMULATOR
//シミュレータ使用の場合:画像をロード
photo.loadImage("images/photo.png");
photo.setImageType(OF_IMAGE_COLOR_ALPHA);
status = 1;
#else
//実機使用の場合:写真を新規に撮影
camera->openCamera();
#endif
}

//————————————————————–
void testApp::lostFocus() {
}

//————————————————————–
void testApp::gotFocus() {
}

//————————————————————–
void testApp::gotMemoryWarning() {
}

//————————————————————–
void testApp::deviceOrientationChanged(int newOrientation){
}
[/code]


カラーでのピクセレイト

サンプルファイルのダウンロード

今回の授業でとりあげた全てのサンプルは下記よりダウンロードすることが可能です。