2013年7月5日金曜日

[PHP]投稿された画像を圧縮してmySQLに保存する方法

iPhoneから送信された画像を保存する仕組みを作っていたので、サーバーサイドの処理をまとめておきます。

iPhoneから画像を送信する方法は次回の記事に載せておきます。

name="image"
の形式で送信された画像を保存する方法です。

DBのテーブルは
id            int(11)  auto_increment
mime      varchar(50)
image     mediumblob
ってかんじです。


//アップロードされた画像は$_FILE['image']でアクセスできます
if($_FILES['image']['error']){

 exit;
}
 

$mime = $_FILES['image']['type'];          //画像のmime(image/jpegなど)を取得
$path = $_FILES['image']['tmp_name']; //アップロードされた一時ファイルへのパスを取得

if($mime == 'image/pjpeg') $mime  = 'image/jpeg';
if($mime == 'image/x-png') $mime  = 'image/png';

$imginfo = getimagesize($path); //画像情報の取得
if($imginfo[2] == IMAGETYPE_JPEG || $imginfo[2] == IMAGETYPE_GIF || $imginfo[2] == IMAGETYPE_PNG){
 
        //画像データの取得
        if($mime == 'image/png')
  $image = imagecreatefrompng($path);
 else if($mime == 'image/jpeg')
  $image = imagecreatefromjpeg($path);
 
        $width = $imginfo[0];     //0:widthを取得
 $height= $imginfo[1];     //1:heightを取得
 
 $newWidth = 240;
 $rate = $newWidth / $width;
 $newHeight = $rate*$height;
 
 global $newImage;
 $newImage = imagecreatetruecolor($newWidth, $newHeight);
 
        //リサイズして$newImageに保存 imagecopyresizedよりも効率がいいらしい
 imagecopyresampled($newImage, $image, 0, 0, 0, 0, $newWidth, $newHeight, $width, $height);
 //imagejpeg($newImage,"./files/tmp.jpeg",100);
 
        //jpeg形式で一時ファイルに保存(100の部分は品質。75がデフォルト値)
        imagejpeg($newImage,$_FILES['image']['tmp_name'],100);
}

if(is_uploaded_file($path)){
 $mime = 'image/jpeg';
 
 $mime = addslashes($mime);
 
        //圧縮画像データの取得
        $data = addslashes(file_get_contents($path));
 
 //DBに格納
 $sql = "insert into image (mime, image) values ('$mime', '$data')";
 mysql_query($sql) or die(mysql_error());
 

 
}
こんな感じでDBに画像を保存できます。
iPhoneからの投稿はまた次回〜

2013年6月25日火曜日

この思いを忘れてしまわないように

いつもは忘備録のための技術情報を記載していたけど、少し日常生活のことを記載する。

先日僕は親友を亡くした。小学生来の親友で15年以上の付き合いであった。
僕の人生で最も大切で最も愛していた。

こんなにいきなりいなくなるなんて思ってもいなかった。今でも心の整理がつかない。

心のどこかで今の日常がいつまでも続くと思っている節があったが、そんなのは間違い。
変化はいきなりやってくるし、当然だったことが当然ではなくなる。
僕にとっては3.11の時以上に人生について考えさせられている。

好き勝手生きることも年を追うごとに難しくなるのかもしれない。
けれど、今の僕は失って困るものはない。

安定を求めても幸せは落ちていない。辛いけれども自分の好きなこと、やりたいことを突き詰めて行った方が幸せなのかもしれない。

自分の信念を信じてこの先の真っ暗闇の道を進んで行こうと思う。
僕は自己保身と金にしか考えられない大人にはならない。
いつまでも子どもの頃のワクワク感と無鉄砲さを胸に。

天国で僕のことを見てくれている大親友へ。暖かく見守っていてください。僕がそっちに行ったらまた一緒にお散歩に行こう。

2013年5月5日日曜日

[iOS]iOSのGUIをopenFrameworksのプロジェクトに追加する際の画面サイズの注意

openFrameworksでiPhoneアプリを開発していると、iOS標準のGUIを追加する方法で悩まされるかもしれません。

でもちょっと調べれば解決策がネット上にありました。
こちらのサイトにinterface builderを使ってGUIを追加する方法が記載されています。

ただしここで注意が必要なのはこの方法をただ適用しただけでは、iPhone4S以下向け3.5inchディスプレイとiPhone5向け4inchディスプレイの共存ができないことです。
iPhone5用に作成したviewを適用しようとするとiPhone4S以下では画面が下にはみ出してしまいます。
これはaddSubViewで追加したviewControllerのviewサイズがディスプレイのサイズによって自動で変換されないためです。

じゃあどうすればいいかというと、addSubViewでiOSのGUIを追加する直前でviewのサイズを変更してください。


      
   //iPhoneの画面サイズの取得
   CGSize addViewSize = CGSizeMake(ofGetWidth(), ofGetHeight());
        
   //iOSGUIのviewサイズをiPhoneの画像サイズに変換
   CGRect addViewRect = [xibViewController.view frame];
   addViewRect.size.width = addViewSize.width;
   addViewRect.size.height = addViewSize.height;
   [xibViewController.view setFrame:addViewRect];        
        
   //openFrameworksに追加
   [ofxiPhoneGetUIWindow() addSubview:xibViewController.view];

これでiPhone4S以下にもiPhone5にも対応できると思います。

2013年3月29日金曜日

[iOS]iOSでofxFaceTrackerを使用する方法

openFrameworksのアドオンの1つ、ofxFaceTrackerをiOSで使用するのに手こずったのでメモ。

まず普通にアドオンを追加してコンパイルしてもコンパイルが通らない。
こんな感じ。

iOSでは先にofxOpenCVを読み込んであげなきゃいけない。のでofxOpenCVをアドオンに追加し、ofxFaceTrackerの前(厳密にはofxCvの前)でインクルードしてもう一度コンパイルしてみる。
するとコンパイルは通った!

でカメラの初期化とかしてFacetrackerを使ってみようとコンパイルは通るけど起動時にエラーが出る。

なんでかなーと色々調べてると、ofxiPhoneアドオン内のofiPhoneVideoGrabber内の関数にこんな記述が…(笑)

 
ofPixelsRef ofiPhoneVideoGrabber::getPixelsRef(){
    static ofPixels dummy;
    //@TODO implement me
    return dummy;
}
 
ここをこんな風に修正したらちゃんと動くようになりました。

 
ofPixelsRef ofiPhoneVideoGrabber::getPixelsRef(){
    static ofPixels pixels;
    pixels.setFromExternalPixels(getPixels(), getWidth(), getHeight(), 3);
    return pixels;
}
 


参考URL:https://github.com/kylemcdonald/ofxCv/issues/14

2013年3月13日水曜日

[openCV]openFrameworksでwatershedアルゴリズムを実装する

openFrameworksを使ってopenCVを使おうと思ったら、意外と単純じゃなかったのでメモ。
学生の時にもっと画像処理系の研究しておくべきだった。

openFrameworksにはopenFrameworks用のopenCVライブラリofxopenCVが用意されているのだけど、openCVよりもできることが限られてる気がしました。
例えば、watershedアルゴリズムを使って領域分割しようとしてもofxopenCV用にはその関数が用意されていなかったり。直接openCVも使えるので多分そっち使ってってことだとは思うけど。

以下、openFrameworks内でwatershedアルゴリズムを実装した方法です。

1、ofxCvClolorImageのインスタンスに画像を読み込む

    ofImage buf; 
    ofxCvColorImage ofxSrc;
    buf.loadImage("munk.jpeg");
    ofxSrc.setFromPixels(buf.getPixels(), buf.width, buf.height);

2、openCVの画像データ構造体IplImageの入力画像、マーカー画像、出力画像用インスタンスを生成する。
この時、marker用画像のdepthは IPL_DEPTH_32S、nchannelは1にしなければエラーになる。(ここに詰まった。)

 
    IplImage *srcImg, *markers, *dstImg;
    srcImg = ofxSrc.getCvImage();
    
    markers = cvCreateImage(cvGetSize(srcImg), IPL_DEPTH_32S, 1); //depth, nchannelに注意
    cvZero(markers);

    dstImg = cvCloneImage(srcImg);

3、watershedアルゴリズム用のマーカーをランダムに分割数(この例では200個にした)作成する

 
    int seed_rad = 20;
    static int seed_num = 0;
    
    for(int i=0;iwidth), ofRandom(srcImg->height));
        cvCircle (markers, pt, seed_rad, cvScalarAll (seed_num), CV_FILLED, 8, 0);
    }

4、watershedアルゴリズムを実行し、出力画像に結果を代入する。

 
    cvWatershed(srcImg, markers);
    
    // 実行結果の画像中のwatershed境界(ピクセル値=-1)を結果表示用画像上に表示する
    int *idx;
    for (int i = 0; i < markers->height; i++) {
        for (int j = 0; j < markers->width; j++) {
            idx = (int *) cvPtr2D (markers, i, j, NULL);
            if (*idx == -1)
                cvSet2D (dstImg, i, j, cvScalarAll (255));
        }
    }

5、最後に画面描画ができるように用にofxCvClolorImageに結果画像を代入する。

 
    ofxCvColorImage ofxSrc,ofxDst;
    ofxDst.allocate(dstImg->width, dstImg->height);
    ofxDst = dstImg;

ムンクの画像を分割した結果がこれ。

2013年2月22日金曜日

[iOS]バックグラウンドで音楽を再生させつづける方法

音楽系アプリを開発していてバックグラウンドでも音楽を鳴らし続ける方法につまづいたのでメモ。

やり方は簡単でソースコードを2カ所修正するだけ。

1. info.plistファイルを修正する
[Required background modes]を追加し、Item 0のValueを[App plays audio]にする。



2. アプリ起動時の初期化関数内に次のコードを挿入する。

        AVAudioSession *session = [AVAudioSession sharedInstance];
        [session setCategory:AVAudioSessionCategoryPlayback error:nil];

たったこれだけでバックグラウンドでも音楽を再生することが可能です。

2013年2月18日月曜日

iPhoneのミュージックライブラリ内の曲をopenALで使用する方法

最近はopenFrameworksを用いてiPhoneアプリを創作しています。

音楽とインタラクションをテーマにしたアプリを作成していたところ、iPhoneのミュージックライブラリ内の曲を編集するようにする方法につまづいたため記録しておきます。

AVAudioPlayerでは複数曲の再生や音楽データの加工に対応していません。このため、openALを用いて音楽データを扱います。
ところが、openALではm4aやmp3のファイルに対応していないためcafファイルに変換して書き出す必要があります。

やりかたはこのサイトを参考にして実装しました。

基本的にはMPMediaPickerControllerで選択した曲に対して、AVAssetReaderとAVAssetWriterを用いてcaf形式に変換してます。

書き出したcaf形式の音楽データをopenALのインスタンスから読み込めば音楽データの加工できます。

ちなみに書き出す際のチャンネルは1つにしておかなければ距離に応じた音の減衰ができないので気をつけてください。
ここに数時間とられました。。。

// iPodの曲目のピッカーを表示
-(IBAction)showMediaPicker:(id)sender
{
    MPMediaPickerController *picker = [[MPMediaPickerController alloc] initWithMediaTypes: MPMediaTypeMusic];
    [picker setDelegate: self];
    [picker setAllowsPickingMultipleItems: NO];
    picker.prompt = NSLocalizedString (@"Add songs to play", "Prompt in media item picker");

    // pickerをModalViewに表示
    [self presentModalViewController: picker animated: YES];
    [picker release];
}



//選択した曲に対して処理を行う
- (void)mediaPicker:(MPMediaPickerController *)mediaPicker didPickMediaItems:(MPMediaItemCollection *)mediaItemCollection
{
    MPMediaItem *item = [mediaItemCollection.items lastObject];
    
    NSLog(@"export succeeded?:%@", [self exportItem:item]? @"YES": @"NO");
    
    [self dismissModalViewControllerAnimated:YES];
}

//cafファイルに変換してファイルの書き出し
- (BOOL)exportItem:(MPMediaItem *)item
{   
    NSError *error = nil;
    
    NSDictionary *audioSetting = [NSDictionary dictionaryWithObjectsAndKeys:
                                  [NSNumber numberWithFloat:44100.0],AVSampleRateKey,
                                  [NSNumber numberWithInt:1],AVNumberOfChannelsKey, //チャネルを1にしておかないと音の減衰ができない
                                  [NSNumber numberWithInt:16],AVLinearPCMBitDepthKey,
                                  [NSNumber numberWithInt:kAudioFormatLinearPCM], AVFormatIDKey,
                                  [NSNumber numberWithBool:NO], AVLinearPCMIsFloatKey,
                                  [NSNumber numberWithBool:0], AVLinearPCMIsBigEndianKey,
                                  [NSNumber numberWithBool:NO], AVLinearPCMIsNonInterleaved,
                                  [NSData data], AVChannelLayoutKey, nil];
    
    //読み込み側のセットアップ
    NSURL *url = [item valueForProperty:MPMediaItemPropertyAssetURL];
    AVURLAsset *URLAsset = [AVURLAsset URLAssetWithURL:url options:nil];
    if (!URLAsset) return NO;
    
    AVAssetReader *assetReader = [AVAssetReader assetReaderWithAsset:URLAsset error:&error];
    if (error) return NO;
    
    NSArray *tracks = [URLAsset tracksWithMediaType:AVMediaTypeAudio];
    if (![tracks count]) return NO;
    
    AVAssetReaderAudioMixOutput *audioMixOutput = [AVAssetReaderAudioMixOutput
                                                   assetReaderAudioMixOutputWithAudioTracks:tracks
                                                   audioSettings:audioSetting];
    
    if (![assetReader canAddOutput:audioMixOutput]) return NO;
    
    [assetReader addOutput:audioMixOutput];
    
    if (![assetReader startReading]) return NO;
    
    
    //書き込み側のセットアップ
    NSString *title = [item valueForProperty:MPMediaItemPropertyTitle];
    NSArray *docDirs = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *docDir = [docDirs objectAtIndex:0];
    NSString *outPath = [[docDir stringByAppendingPathComponent:@"music1"]
                         stringByAppendingPathExtension:@"caf"];
    
    NSURL *outURL = [NSURL fileURLWithPath:outPath];
    AVAssetWriter *assetWriter = [AVAssetWriter assetWriterWithURL:outURL
                                                        fileType:AVFileTypeCoreAudioFormat
                                                             error:&error];
    //ファイルが存在している場合は削除する
    NSFileManager *manager = [NSFileManager defaultManager];
    if([manager fileExistsAtPath:outPath]){
        [manager removeItemAtPath:outPath error:&error];
    }
    
    if (error) return NO;
    
    AVAssetWriterInput *assetWriterInput = [AVAssetWriterInput assetWriterInputWithMediaType:AVMediaTypeAudio
                                                                              outputSettings:audioSetting];
    assetWriterInput.expectsMediaDataInRealTime = NO;
    
    if (![assetWriter canAddInput:assetWriterInput]) return NO;
    
    [assetWriter addInput:assetWriterInput];
    
    if (![assetWriter startWriting]) return NO;
    
    
    
    //コピー処理
    [assetReader retain];
    [assetWriter retain];
    
    [assetWriter startSessionAtSourceTime:kCMTimeZero];
    
    dispatch_queue_t queue = dispatch_queue_create("assetWriterQueue", NULL);
        
    //書き込み処理
    [assetWriterInput requestMediaDataWhenReadyOnQueue:queue usingBlock:^{
        
        NSLog(@"start");
        
        while (1){
            if ([assetWriterInput isReadyForMoreMediaData]) {
            
                CMSampleBufferRef sampleBuffer = [audioMixOutput copyNextSampleBuffer];
                
                if (sampleBuffer) {
                    [assetWriterInput appendSampleBuffer:sampleBuffer];
                    CFRelease(sampleBuffer);
                }
                else {
                    [assetWriterInput markAsFinished];
                    break;
                }
            }
        }
        
        [assetWriter finishWriting];
        [assetReader release];
        [assetWriter release];
        
        NSLog(@"finish");
                
    }];
    
    dispatch_release(queue);
    
    return YES;
}

2013年1月23日水曜日

iPhoneでXMLファイルからデータを取得する

サーバーからデータ取得してiPhoneで使用したい場合はXML形式で読み込むようにしています。
このやり方が正しいのかイマイチわからないのが1人で作業するとこの難しさだと感じてます。
だれか正攻法教えてください。

まずはサーバー側ではPHPでXML形式のデータを作成。

createXML.php
&lt?php
include_once "DatabaseClass.php";

if(!empty($_POST)){  
 $database = new Database;
 
 $table = "user_name";
 $email = $_POST['email'];
 $password = $_POST['password'];
 
 $select = sprintf('SELECT id, name, picture FROM %s WHERE email="%s" AND password="%s"',
      mysql_real_escape_string($table),
      mysql_real_escape_string($email),
      mysql_real_escape_string($password));
           
 $row = $database->array_select($select);

 if($row != NULL){
  $xml = "\n";
     
  $xml.= '';
  $xml.="\n";
     
   foreach($row as $value){
    $xml.="\n";
    $id=$value["id"];
    $xml.="\t$id\n";
    $name=$value["name"];
    $xml.="\t$name\n";
    $pict=$value["picture"];
    $xml.="\t$pict\n";
    $xml.="\n";
   }//foreach
     
   $xml.="\n";
   echo $xml; 
 } //if($row != NULL)
 
 $database->close();
}


?>

iPhoneからはPOSTメソッドでデータを送信し、返り値をNSXMLParserオブジェクトに代入してデータを取得する。

XMLParseAppDelegate.m
- (void)loadData{   
    NSURL *url = [[NSURL alloc]initWithString:@"http://localhost:8888/createXML.php"];
 
    NSMutableURLRequest *request = [[NSMutableURLRequest alloc]initWithURL:url];
    
    // MethodにPOSTを指定する。
    request.HTTPMethod = @"POST";
    
    NSString *email    = @"momo@momochibi.com";
    NSString *password = @"momo";
    
    NSString *body = [NSString stringWithFormat:@"email=%@&password=%@",email,password];
    
    request.HTTPBody = [body dataUsingEncoding:NSUTF8StringEncoding];

    //XML形式のデータを取得   
    NSData *returnData = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:nil]; 
    NSXMLParser *parser = [[NSXMLParser alloc]initWithData:returnData];
  [parser setDelegate:self];
    [parser parse];
}

// XMLのパース開始
- (void)parserDidStartDocument:(NSXMLParser *)parser {
 // 初期化処理
 //statuses = [NSMutableArray array];
    
    members = [NSMutableArray array];
    membersPicture = [NSMutableArray array];
    
    inIdElement   = NO;
    inNameElement = NO;
    inPictElement = NO;
    
}

// XMLのパース終了
- (void)parserDidEndDocument:(NSXMLParser *)parser {
 // 取得したデータを出力する
 for (NSString *member in members) {
  NSLog(@"%@", member);
 }
}

// 要素の開始タグを読み込み
- (void)parser:(NSXMLParser *)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qualifiedName attributes:(NSDictionary *)attributeDict {
 if ([elementName isEqualToString:@"id"]) {
  // nameタグの中に入った
  inIdElement = YES;
        ID = [NSMutableString string];
 }
    
    if ([elementName isEqualToString:@"name"]){
        inNameElement =YES;
        name = [NSMutableString string];
    
    }
    
    if( [elementName isEqualToString:@"pict"] ){
        inPictElement = YES;
        pict = [NSMutableString string];
    }
    
}

// 要素の閉じタグを読み込み
- (void)parser:(NSXMLParser *)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName {
 if ([elementName isEqualToString:@"member"]) {
  // memberタグが終わるタイミングで、membersにメンバーを追加する
  NSString *s = [NSString stringWithFormat:@"%@: %@ : %@", ID, name, pict];
  [members addObject:s];
        
        NSString *ps = [NSString stringWithFormat:@"%@",pict];
        [membersPicture addObject:ps];
 } else if ([elementName isEqualToString:@"id"]) {
  // idタグから出た!
  inIdElement = NO;
 } else if ([elementName isEqualToString:@"name"]) {
  // nameタグから出た!
  inNameElement = NO;
 } else if ([elementName isEqualToString:@"pict"]) {
        
        inPictElement = NO;
    }
}

// テキストデータ読み込み
- (void)parser:(NSXMLParser *)parser foundCharacters:(NSString *)string {
 // テキストデータは複数回に分けて呼び出されることがある
 if (inIdElement) {
  [ID appendString:string];
 }
    else if (inNameElement) {
  [name appendString:string];
 }
    else if (inPictElement){
        [pict appendString:string];
    }

}
XMLParseAppDelegate.h
#import &ltoundation/Foundation.h&gt

@interface XMLParseAppDelegate : NSObject&ltUIApplicationDelegate, NSXMLParserDelegate&gt
{
 
    NSMutableArray *members;
    BOOL inIdElement;
    BOOL inNameElement;
    BOOL inPictElement;
    NSMutableString *name;
    NSMutableString *ID;
    NSMutableString *pict;
}

- (void)loadData;


@end
こんな感じでデータをiPhoneからデータベースに接続して データを取得したりしてます。

iPhoneでPOSTメソッドによるデータを取得する方法

iPhoneではGETメソッドでデータをやり取りするのは簡単だけど、 POSTメソッドでやりとりする場合少しめんどくさい。

GETでデータを送信する場合はURLに入れればいいだけでできる。

NSString *s = [NSString stringWithFormat:@"http://localhost:8888/index.php?table_name=products&query_type=select"];    
NSURL  *url = [NSURL URLWithString:s];

POSTメソッドの場合はリクエストをNSMutableURLRequestで宣言する。
その後、POSTメソッドで送る内容をリクエストのHTTPBodyに代入することで可能となる。
    NSURL *url = [[NSURL alloc]initWithString:@"http://localhost:8888/dbconnect/POSTMethodFromiOS.php"];
    NSMutableURLRequest *request = [[NSMutableURLRequest alloc]initWithURL:url];
    
    // MethodにPOSTを指定する。
    request.HTTPMethod = @"POST";
    
    NSString *email    = @"momo@momochibi.com";
    NSString *password = @"momo";
    
    NSString *body = [NSString stringWithFormat:@"email=%@&password=%@",email,password];
    
    request.HTTPBody = [body dataUsingEncoding:NSUTF8StringEncoding];

POSTメソッドでサーバにデータを送る場合は、何かしらの処理をした結果を取得したいと思う。
僕はXML形式に処理されたデータを変換し、そのデータを取得するようにしている。

2013年1月7日月曜日

2013年 目標


2013年の目標は、「技術向上」と「将来のための土台作り」にしようと考えている。

永らく自分のやりたいことはなんだろうと考え続けていたのだけど、少しヒントが見つかった。

キーワードは「IT×芸術」。

シドニー現代美術館で見た芸術作品のようなものを自分でも創りたい、自分が感動したような感覚を他の人に味わってもらいたいと思うようになったのです。

正直25歳にしてこう思うようになったのは遅すぎる気がしなくもないけど、思い立ったのだからやってみようと思ってます。

このブログの立ち位置としては、そのための情報発信としたいと思います。
発信内容は以下3点
1.学んだ技術情報
 -iPhoneアプリ開発(objective-c)
   -webアプリ開発(HTML+CSS+javascript)
   -Microsoft SharePoint(これは仕事で扱っているためおまけ的な感じ)
2.制作物
3.感銘を受けた芸術作品

僕が感銘を受けたのはこの作品です。

drawing machine by Benjamin Forster