ページ 11

ppm形式の画像

Posted: 2014年5月07日(水) 15:40
by rozeo
お久しぶりです、rozeoです。

ppm形式の画像を扱うプログラムを書いてるんですが、1000ピクセル*1000ピクセルのppmのような大きなppmになると色要素が莫大になるため、1つ1つ読むとロードにかなりの時間を有します。

そこでですが、ppmの形式のような莫大な数の要素を扱う際処理が比較的に早いアルゴリズムってありますか?ヘッダ部の読み込みはできるため、データ部のみでいいです。
ppmを扱うフリーソフトがあるようですが、そういうようなソフトは読み込みが高速です。

なにかしっていることあればよろしくお願いします。

Re: ppm形式の画像

Posted: 2014年5月07日(水) 17:25
by h2so5
具体的にどのような方法でロードしているのでしょうか。
fscanfなどを利用していると遅いと思いますよ。

Re: ppm形式の画像

Posted: 2014年5月07日(水) 18:54
by rozeo
バイナリ(P6)形式なのでrb指定で開いてfreadで読んでます

Re: ppm形式の画像

Posted: 2014年5月07日(水) 21:31
by h2so5
ソースコードを貼ってもらえますか。

Re: ppm形式の画像

Posted: 2014年5月08日(木) 00:54
by rozeo
ソースに問題があるわけではなく、莫大な量の要素の能率のいいほうほうを聞いてるんですが…

あとソースは、オフラインPCで作業してるため、はることができません…悪しからず…
(これは携帯から投稿してます)

Re: ppm形式の画像

Posted: 2014年5月08日(木) 01:03
by softya(ソフト屋)
> ソースに問題があるわけではなく、莫大な量の要素の能率のいいほうほうを聞いてるんですが…

freadは早いはずなので、rozeoさんが効率が悪いfreadの使い方をしているのでは?とh2so5さんは危惧しておられます。
プロがソースコードを要求するのは、それなりに理由がありますよ。

Re: ppm形式の画像

Posted: 2014年5月08日(木) 05:31
by へにっくす
rozeo さんが書きました:ソースに問題があるわけではなく、莫大な量の要素の能率のいいほうほうを聞いてるんですが…
freadでファイルサイズぶん読み込めばいいだけでは?
ファイル読み込みを1画素ずつ何回も呼び出すより、
ファイル読み込みを1回で済ました方が格段に速いです。

まああまりにでかい画像だとメモリを圧迫して、システム全体が重くなりますけどね。
今あるソースを掲示してもらえると、能率のいいやり方を教えてもらえると思いますよ。

Re: ppm形式の画像

Posted: 2014年5月08日(木) 10:40
by rozeo
ようするに一度ファイル全てを読みこみ、そのあとRGBの値を整理するって感じですかね?

ちなみに並べ替えを行うのでその後にまたRGBの値を走査しますが。

Re: ppm形式の画像

Posted: 2014年5月08日(木) 10:51
by rozeo
あと、寮に帰ったらソースコードはれるかどうかやってみます

Re: ppm形式の画像

Posted: 2014年5月08日(木) 17:12
by rozeo
なぜかプロジェクトを消してしまっていたのでデータ部の読み込みの概要のみかきます。

まず前提条件としてfopen関数でrb属性でファイルを開いています。

コード:

case WM_CREATE:
    FILE *f = fopen( "test.ppm","rb" );
    if( f == NULL ) return -1;
    break;
その後WM_PAINTメッセージが来たとこでロードを行います。
(WM_CREATEメッセージのときにヘッダ部はスキップしています。)

コード:

case WM_PAINT:
//hdcとpsは関数上部で定義済み
hdc = BeginPaint( hWnd,&ps );
for( int i = 0;i < 1000;i++ ){//ppmのサイズは1000*1000固定
    for( int j = 0;j < 1000;j++ ){
        unsigned char r,g,b;
        fread( &r,1,1,f );
        fread( &g,1,1,f );
        fread( &b,1,1,f );
        SetPixcel( hdc,i,j,RGB( r,g,b ));
    }
}
EndPaint( hWnd,&ps );
break;
ようするにサイズが1000*1000なので100万回ループさせてかつ、rgbの3つの値を読み取っています。

Re: ppm形式の画像

Posted: 2014年5月08日(木) 17:27
by usao
1バイトずつfreadしている「読み方」も確かに遅そうではありますが,
それ以前に
 fread()をWM_PAINTのとこに書いてる=表示が必要な際に毎回毎回読み込む
という方式を改めるべきかと思いますが,いかがでしょうか.

例えば,
WM_CREATE時に画像内容までを読み込み,その内容はどこかに保持しておく→WM_PAINT時にはその内容を表示する
という形にすれば ロードは一回で済みますよね.

[追記]
あと,SetPixel()も遅い部類のものだと聞いたことがありますよ.

Re: ppm形式の画像

Posted: 2014年5月08日(木) 17:40
by usao
>ロードにかなりの時間を有します。

とのことですが,
「ロード部分の処理時間だけを測った」とかいう話ではなくて
「何かプログラムの挙動が激しく重たい」という感じでの話なのであれば,
少なくとも,その原因として

・読み方(1バイトずつ読んでる)
・読込処理を書いている場所(WM_PAINTのところに書いてる)
・描画方法(SetPixel)

の3つがあるのではないかと思います ……ということです.

Re: ppm形式の画像

Posted: 2014年5月08日(木) 17:54
by rozeo
usao さんが書きました:1バイトずつfreadしている「読み方」も確かに遅そうではありますが,
それ以前に
 fread()をWM_PAINTのとこに書いてる=表示が必要な際に毎回毎回読み込む
という方式を改めるべきかと思いますが,いかがでしょうか.

例えば,
WM_CREATE時に画像内容までを読み込み,その内容はどこかに保持しておく→WM_PAINT時にはその内容を表示する
という形にすれば ロードは一回で済みますよね.

[追記]
あと,SetPixel()も遅い部類のものだと聞いたことがありますよ.
ならば、WM_CREATEでピクセル数100万*三色、300万の配列をmallocで用意してWM_CREATEのときに
unsigned char color[100000];//malloc文書くのめんどいので省略
fread( color,sizeof( char ),1000000,f );

で読み込むのはいいのですが、描画の際にSetPixcel以外で描画する方法ってあるのでしょうか?
SetPixcelをつかうと100万回ループさせないといけないので...

Re: ppm形式の画像

Posted: 2014年5月08日(木) 18:50
by usao
DIBセクションとか使うとよいのではないでしょうか.

Re: ppm形式の画像

Posted: 2014年5月08日(木) 19:34
by softya(ソフト屋)
usaoさんが書いているDIBセクションを使ってください。WIn32APIの一部であるGDIの機能です。
「標準 Windows API」
http://wisdom.sakura.ne.jp/system/winap ... index.html

Re: ppm形式の画像

Posted: 2014年5月08日(木) 22:28
by rozeo
GDIちょっとやってみることにします。

またこの方法でのちのち読んだファイル内容(RGB配列やピクセル位置)を変更することは可能でしょうか?(この部分だけきりとって新しくファイルつくるとか)

Re: ppm形式の画像

Posted: 2014年5月08日(木) 23:02
by softya(ソフト屋)
rozeo さんが書きました:GDIちょっとやってみることにします。

またこの方法でのちのち読んだファイル内容(RGB配列やピクセル位置)を変更することは可能でしょうか?(この部分だけきりとって新しくファイルつくるとか)
ペイント系ソフトが使っているものなので、もちろん出来ますよ。

Re: ppm形式の画像

Posted: 2014年5月09日(金) 01:01
by rozeo
先ほどのページでやってみたんですが、CreateDIBSectionの第4引数でエラーがでます。
BYTE *bCBits;
HBITMAP hBitmap = CreateDIBSction( NULL,bmpinfo,DIB_RGB_COLORS,&bCBits,NULL,0 );

エラー文
BYTE **からvoid **に変換できません。

さっきのページは通っていたのですがやはり仕様の変更でしょうか?もしそうなら解決方法を教えてください

っていうかこのサイトビットマップを例にあげてやってますが、PPMの場合はどのように工夫すればよいでしょうか?

Re: ppm形式の画像

Posted: 2014年5月09日(金) 01:09
by softya(ソフト屋)
> C++ としてコンパイルした場合、エラーが発生する場合があります
と書かれている通りC++でコンパイルされてませんか?
一応C++でもキャストすれば通るはずです。

Re: ppm形式の画像

Posted: 2014年5月09日(金) 01:11
by rozeo
C++でコンパイルしています。void **でキャストすると通りました

Re: ppm形式の画像

Posted: 2014年5月09日(金) 01:32
by rozeo
コード丸写ししましたがbmpが表示できません。なにが考えられるでしょうか?
変更したところはCreateDIBSctionのキャストだけです

Re: ppm形式の画像

Posted: 2014年5月09日(金) 05:21
by へにっくす
rozeo さんが書きました:先ほどのページでやってみたんですが、CreateDIBSectionの第4引数でエラーがでます。
BYTE *bCBits;
HBITMAP hBitmap = CreateDIBSction( NULL,bmpinfo,DIB_RGB_COLORS,&bCBits,NULL,0 );
rozeo さんが書きました:コード丸写ししましたがbmpが表示できません。なにが考えられるでしょうか?
変更したところはCreateDIBSctionのキャストだけです
・・・
コード丸写しってことは元のソースがあるはずですよね。そのリンクかもしくは、今のソースをはってください。
少なくともこのスレではそれが示されていません。
今のあなたは、なんでも教えてクンか察してクンと同じ印象を受けます。
オフトピック
先ほどのページってどこのことよ。
softyaさんの示した「標準 Windows API」のことでも、DIBに関するページがいくつかあるんですけど。
質問するなら、ちゃんとその情報を伝えてください。

Re: ppm形式の画像

Posted: 2014年5月09日(金) 07:47
by rozeo
softyaさんのURLのDIBセクションって項目です

Re: ppm形式の画像

Posted: 2014年5月09日(金) 08:53
by softya(ソフト屋)
プログラムコードは、ちゃんと理解されていますか?
コマンドライン引数で画像を指定するようになっているコードですが、そこは自分の都合に合わせて変更なり、引数の指定なりをされていますよね?

Re: ppm形式の画像

Posted: 2014年5月09日(金) 09:49
by usao
手順としては こんな感じですかね.
(1)~(3)はWM_CREATE時なり,あるいはWinMain()の最初らへんでやるなりすればいいでしょう.

(1) BITMAPINFO 型の変数を用意して,そこに(bit数とか縦横サイズ等々の)情報を入れる
(2) CreateDIBSection() でDIBセクションを作る.
  (2-1)戻り値として,作られたDIB用のHBITMAPが返される
  (2-2)引数によって,作られたDIBの画素データメモリ位置を得られる
(3)ファイルから画像をロードした結果の画素値を (2-2)の場所に入れれば描画準備完了.
(4)あとはWM_PAINTのとこで好きな方法でDIBを描画する(↓が一応ヒントになるかな)
(5)後始末は,DeleteObject()で.
オフトピック
StretchDIBits()みたいなAPIで単に等倍描画するやつって無いのかな?…といろいろ見てたら
SetDIBitsToDevice() なんていうAPIがあるんですね.


SetDIBitsToDevice() とか StretchDIBits() は
画像描画コードがとりあえずそのAPIだけで済むので書くのが楽.
 ↑↓
メモリデバイスコンテキストを用意してそこにSelectObject()して…という手順は
やや面倒だけど描画(転送?)方法の自由度が高い.
(大昔にやったときは AlphaBlend()やTransparentBlt() が自環境でサポートされてなくて無念だったなぁ)

Re: ppm形式の画像

Posted: 2014年5月09日(金) 12:19
by rozeo
softya(ソフト屋) さんが書きました:プログラムコードは、ちゃんと理解されていますか?
コマンドライン引数で画像を指定するようになっているコードですが、そこは自分の都合に合わせて変更なり、引数の指定なりをされていますよね?
もちろんそこはCreateFileで開くときに一つのファイル名に決めてます


usao氏のやり方で再度コードを組み直してみます。
また質問があれば質問させていただくので一旦解決とさせていただきます

Re: ppm形式の画像

Posted: 2014年5月09日(金) 12:23
by softya(ソフト屋)
rozeo さんが書きました:
softya(ソフト屋) さんが書きました:プログラムコードは、ちゃんと理解されていますか?
コマンドライン引数で画像を指定するようになっているコードですが、そこは自分の都合に合わせて変更なり、引数の指定なりをされていますよね?
もちろんそこはCreateFileで開くときに一つのファイル名に決めてます


usao氏のやり方で再度コードを組み直してみます。
また質問があれば質問させていただくので一旦解決とさせていただきます
私の所ではキャスト(void**)で問題なく動きましたのでなにか間違いがあると思います。
VisualC++2008を使用。

Re: ppm形式の画像

Posted: 2014年5月09日(金) 12:44
by rozeo
帰ったらプログラムを見直してみます。

あとusao氏の手順3、ファイルから画像をロードした画素値を2-2に入れるというのはどのような処理をおこなったらいいのでしょう?

ほかのサイトを漁ってみるとvoid **でキャストしてました。しかしその変数の型がそのサイトはLPDWORD型、softyaさんに紹介してもらったサイトはBYTE型でしたがそこに問題はないのでしょうか?

Re: ppm形式の画像

Posted: 2014年5月09日(金) 13:08
by usao
>あとusao氏の手順3、ファイルから画像をロードした画素値を2-2に入れるというのはどのような処理をおこなったらいいのでしょう?

うーん,イメージ湧きませんかね?
CreateDIBSection() は,
例えば,あなたが「画像サイズが1000*1000で,1画素あたり24bitなんですが」という指定で使えば,
そのサイズの画素データ群を保持するために必要なメモリ領域を確保してくれて,そのメモリの場所を
((void**)がどうのといっている引数で)教えてもらえるわけです.
なので,後は,そのメモリに,あなたが表示したいイメージの画素値を書き込んであげればいいわけです.
(1000*1000画素分の画素値を,DIBのフォーマットに従って.←フォーマットについては少し調べましょう)


>ほかのサイトを漁ってみるとvoid **でキャストしてました。しかしその変数の型がそのサイトはLPDWORD型、softyaさんに紹介してもらったサイトはBYTE型でしたがそこに問題はないのでしょうか?

上記のように,この引数で返される情報は,指定した画像サイズ分の画素値群を格納するのに必要なサイズ(バイト数)のメモリ領域の「場所」
であって,その場所をどのようにアクセスしようが,それはあなたの自由です.
バイト単位でアクセスしやすいように BYTE* 型を使ってもいいし,
4バイト単位でアクセスした方がやりやすいのであれば DWORD* 型を使ったりすればいいわけです.

コード:

BYTE A[ 40 ]; //こんなバイト列があるときに…
//A[]にデータを入れるために こんなことをしても別にいいですよね
DWORD *pDW = (DWORD*)A;
pDW[2] = 0xFF801001;  //A[]のどこに何が書き込まれるのでしょうか?

Re: ppm形式の画像

Posted: 2014年5月09日(金) 14:25
by softya(ソフト屋)
こういうRGBなど素のデータを扱うのならメモリイメージの理解は必須です。
デバッガでメモリダンプなどを眺めてみてはどうでしょう?

Re: ppm形式の画像

Posted: 2014年5月09日(金) 15:29
by rozeo
わかりました。ありがとうございます。
とりあえず、丸ごとコピペプログラムがなぜ動かないのか検討してみます

Re: ppm形式の画像

Posted: 2014年5月09日(金) 17:50
by rozeo
CreateDIBSection関数はうまく成功していますが、いまいちどのように画素を格納すればいいのかわかりません。

画素を格納するメモリをさす変数をDWORD *型に変えて

コード:

//hFile : ファイルハンドル
//bmpinfo : BITMAPINFO ,ピクチャデータは格納済み
DWORD *Bits;
hBitmap = CreateDIBSction( hdc,&bmpinfo,DIB_RGB_COLORS,( void ** )Bits,NULL,0 );

for( int i = 0;i < 1000*1000;i++ ) ReadFile( hFile,&Bits[i],3,&dwBytes,NULL );
とやってみましたが、無理でした。
DIBの画素構成は
BYTE型4つ、RGB値と輝度で4bytesであっているでしょうか?

Re: ppm形式の画像

Posted: 2014年5月09日(金) 18:27
by Mana
"SetDIBitsToDevice"で過去ログ検索したらいい感じのサンプルコードが。
http://dixq.net/forum/viewtopic.php?f=3&t=14569#p115692

メモリにピクセルデータ展開してるとこをNo.10のppm読み込むのにそのまま当てはめてしまえるのでは?
1バイトずつ読み込んでもキャッシュがあればいうほど遅くないと思いますが。

Re: ppm形式の画像

Posted: 2014年5月10日(土) 15:36
by rozeo_h
Mana さんが書きました:"SetDIBitsToDevice"で過去ログ検索したらいい感じのサンプルコードが。
http://dixq.net/forum/viewtopic.php?f=3&t=14569#p115692

メモリにピクセルデータ展開してるとこをNo.10のppm読み込むのにそのまま当てはめてしまえるのでは?
1バイトずつ読み込んでもキャッシュがあればいうほど遅くないと思いますが。
ありがとうございます。読んでみたのですが、あまりうまくわからないので、、もう少し自分で考えてみたいと思います。

とりあえず、解決にしておきます