HSPポータル
サイトマップ お問い合わせ


HSPTV!掲示板


未解決 解決 停止 削除要請

2016
0122
スペース画像の解像度を取得するモジュール18解決


スペース

リンク

2016/1/22(Fri) 18:52:30|NO.74232

バイナリデータから直接解像度を読み取るモジュールを作ってみました。
手元のファイルは正常に取得できているのですが、正直あらゆるファイルで正常に取得できる自信がありません。
そこでお願いです。
皆さんの持っているjpg・png・bmpの画像を読み込み、正常に取得できるか試していただけないでしょうか?
もし取得できなかった場合、「○○形式の画像は取得できなかった」の一言だけでも構いませんので、書き込んで頂けると嬉しいです。
”可能なら”で構いませんので、画像そのもののアップロード、画像の一部バイナリデータを公開して頂けると助かります。
もちろんソースコードの問題点を直接指摘して頂いても構いません。

どうかよろしくお願い致します。


#module /* p1 画像のパス jpegの場合、正常に完了するとstatが0。 jpegで解像度が取得できなかった場合は-1が返ります。 pngは1、bmpは2が返ります。 つまり正常に終了した場合、0以上が返ります。 jpeg以外は解像度が書き込まれている位置が固定なので、 取得できないことはあり得ませんが、 正常な値が取得されているとは限りません。 */ #deffunc GetImg str p1 if ct=0:{//初回に限り変数を初期化。 //ファイルの0バイト目から取得する長さ。 //明確に何バイト目までに解像度が書かれているか調べてもわからなかったので、 //手元のファイルを元に適当に設定。 長さ=1024 sdim Fbin,長さ sdim bin,長さ*2+1 ct+1 } bload p1,Fbin,長さ,0//ロード repeat 長さ//バイナリデータを扱いやすいように変換 poke bin,cnt*2,strf("%02x",peek(Fbin,cnt)) loop 状態=0 形式=-2 //0バイト目~数バイトを調べて形式を判定。 if instr(bin,0,"ffd8")=0:{//jpeg in=instr(bin,0,"ffc00011")//解像度の位置が可変なので、解像度の始まりを示す文字を検索。 if in!-1:{//発見できたら処理。 in+10 sx=strf("%d",int("$"+strmid(bin,in+4,4))) sy=strf("%d",int("$"+strmid(bin,in+0,4))) }else:状態=-1//出来なかったら 形式=0 } //png、bmpは解像度が書き込まれている位置が固定なのでjpegより簡単。 //strmidで取り出した後に16進を10進に変換。 if instr(bin,0,"89504e470d0a1a0a")=0:{//png sx=strf("%d",int("$"+strmid(bin,36,4))) sy=strf("%d",int("$"+strmid(bin,44,4))) 形式=1 } if instr(bin,0,"424d")=0:{//bmp //リトルエンディアンで書かれているので注意。 sx=strf("%d",int("$"+""+strmid(bin,38,2)+""+strmid(bin,36,2)+"")) sy=strf("%d",int("$"+""+strmid(bin,46,2)+""+strmid(bin,44,2)+"")) 形式=2 } return (状態+形式) //取得できたサイズを返す。 #defcfunc Gsx return sx #defcfunc Gsy return sy #global button "開く",*開く stop *開く Color 255,255,255:boxf:Color 0,0,0 dialog "",16,"画像" if stat=1:{ GetImg refstr st=stat if st=-2:形式="未知" if st=-1:形式="jpeg(解像度の取得に失敗)" if st=0:形式="jpeg" if st=1:形式="png" if st=2:形式="BMP" pos 10,50:mes "形式"+形式+"\n"+gsx()+"x"+gsy()+"" }



この記事に返信する


窓月らら

リンク

2016/1/22(Fri) 20:30:12|NO.74233

jpg,png,gif で試してみました。
gifだけ取得できないですね、他2つは問題なし。



スペース

リンク

2016/1/22(Fri) 21:10:16|NO.74235

>窓月ららさん
有難うございます。
やはりgifはpicloadで読み込めますし対応してたほうが何かと便利そうですね。
後で試してみます。



kanamaru

リンク

2016/1/22(Fri) 22:36:56|NO.74236

gifは調べてみたら、先頭7バイトのところに解像度があるようです。
リトルエンディアンのようですね。



スペース

リンク

2016/1/22(Fri) 23:08:17|NO.74238

>>kanamaruさん
ありがとうございます。
とりあえずこんな感じですかね。
リトルエンディアンめんどくさい・・・

#module /* p1 画像のパス jpegの場合、正常に完了するとstatが0。 jpegで解像度が取得できなかった場合は-1が返ります。 pngは1、bmpは2が返ります。 つまり正常に終了した場合、0以上が返ります。 jpeg以外は解像度が書き込まれている位置が固定なので、 取得できないことはあり得ませんが、 正常な値が取得されているとは限りません。 */ #deffunc GetImg str p1 if ct=0:{//初回に限り変数を初期化。 //ファイルの0バイト目から取得する長さ。 //明確に何バイト目までに解像度が書かれているか調べてもわからなかったので、 //手元のファイルを元に適当に設定。 長さ=1024 sdim Fbin,長さ sdim bin,長さ*2+1 ct+1 } bload p1,Fbin,長さ,0//ロード repeat 長さ//バイナリデータを扱いやすいように変換 poke bin,cnt*2,strf("%02x",peek(Fbin,cnt)) loop 状態=0 形式=-2 //0バイト目~数バイトを調べて形式を判定。 if instr(bin,0,"ffd8")=0:{//jpeg in=instr(bin,0,"ffc00011")//解像度の位置が可変なので、解像度の始まりを示す文字を検索。 if in!-1:{//発見できたら処理。 in+10 sx=strf("%d",int("$"+strmid(bin,in+4,4))) sy=strf("%d",int("$"+strmid(bin,in+0,4))) }else:状態=-1//出来なかったら 形式=0 } //png、bmpは解像度が書き込まれている位置が固定なのでjpegより簡単。 //strmidで取り出した後に16進を10進に変換。 if instr(bin,0,"89504e470d0a1a0a")=0:{//png sx=strf("%d",int("$"+strmid(bin,36,4))) sy=strf("%d",int("$"+strmid(bin,44,4))) 形式=1 } if instr(bin,0,"424d")=0:{//bmp //リトルエンディアンで書かれているので注意。 sx=strf("%d",int("$"+""+strmid(bin,38,2)+""+strmid(bin,36,2)+"")) sy=strf("%d",int("$"+""+strmid(bin,46,2)+""+strmid(bin,44,2)+"")) 形式=2 } if instr(bin,0,"474946")=0:{//gif //リトルエンディアンで書かれているので注意。 sx=strf("%d",int("$"+""+strmid(bin,14,2)+""+strmid(bin,12,2)+"")) sy=strf("%d",int("$"+""+strmid(bin,18,2)+""+strmid(bin,16,2)+"")) 形式=3 } if instr(bin,0,"00000a")=0:{//tga //リトルエンディアンで書かれているので注意。 sx=strf("%d",int("$"+""+strmid(bin,26,2)+""+strmid(bin,24,2)+"")) sy=strf("%d",int("$"+""+strmid(bin,30,2)+""+strmid(bin,28,2)+"")) 形式=4 } return (状態+形式) //取得できたサイズを返す。 #defcfunc Gsx return sx #defcfunc Gsy return sy #global button "開く",*開く stop *開く Color 255,255,255:boxf:Color 0,0,0 dialog "",16,"画像" if stat=1:{ GetImg refstr st=stat if st=-2:形式="未知" if st=-1:形式="JPEG(解像度の取得に失敗)" if st=0:形式="JPEG" if st=1:形式="PNG" if st=2:形式="BMP" if st=3:形式="GIF" if st=4:形式="TGA" pos 10,50:mes "形式"+形式+"\n"+gsx()+"x"+gsy()+"" }



窓月らら

リンク

2016/1/22(Fri) 23:59:03|NO.74239

jpg,gif,png,bmp で動作確認しました。すべてOKです。



GENKI

リンク

2016/1/23(Sat) 01:34:48|NO.74240

手元のファイル(jpeg,png)をいくつか試してみましたが特に問題起きませんでした。

しかしどこかで見たような機能だなと思って調べてみたらこれでした。

#include "a2d.hsp" dialog "",16,"画像" file = refstr if stat { alGetFileWidth file, w,h if stat : mes "読み込み失敗" : stop mes file mes ""+w+" x "+h }
でもこれはGDI+だからTGAだめっぽい…。



スペース

リンク

2016/1/23(Sat) 02:59:06|NO.74243

やっぱり予想通り同じ機能のものが・・・
無駄になったかと思ったけど、処理速度的にはGDI+は5ms、自分が作った奴は2msなので存在意義はある・・・はず?
皆さん報告有難うございます。まだまだ受け付けているので、暇がある時にでもお願いします。



KA

リンク

2016/1/23(Sat) 06:52:05|NO.74244

× OS2形式BMP(サイズが各2バイト)
× トップダウン形BMP(マイナス符号をつけた高さ)

ちなみに「解像度」ではなくて「サイズ」です。



スペース

リンク

2016/1/23(Sat) 11:56:33|NO.74246

>KAさん
検証ありがとうございます。
bmpが一部ダメなものがあるんですね。
修正しておきます。

>「解像度」ではなくて「サイズ」
確かに解像度はdpi(=画素密度)と同じ意味で、正確にはキャンバスサイズとは別なのですが、
サイズだとデータ容量と間違う可能性があるので、わかりやすいように解像度で統一しました。
の、つもりが間違ってコメントで「//取得できたサイズを返す。」と書いているのは内緒です・・・
ですがこれからはサイズで統一します。ご指摘ありがとうございました。



スペース

リンク

2016/1/23(Sat) 15:09:45|NO.74251

1.5~2倍の高速化。
bmp os2、トップダウン型とボトムアップ型に対応。

#module /* p1 画像のパス jpegの場合、正常に完了するとstatが0。 jpegでサイズが取得できなかった場合は-1が返ります。 pngは1、bmpは2が返ります。 つまり正常に終了した場合、0以上が返ります。 jpeg以外はサイズが書き込まれている位置が固定なので、 取得できないことはあり得ませんが、 正常な値が取得されているとは限りません。 */ #deffunc GetImg str p1 if ct=0:{//初回に限り変数を初期化。 //ファイルの0バイト目から取得する長さ。 //明確に何バイト目までにサイズが書かれているか調べてもわからなかったので、 //手元のファイルを元に適当に設定。 長さ=1024 sdim Fbin,長さ sdim bin,長さ*2+1 ct=1 } bload p1,Fbin,長さ,0//ロード repeat 3//バイナリデータを扱いやすいように変換 poke bin,cnt*2,strf("%02x",peek(Fbin,cnt)) loop 状態=0 形式=-2 //0バイト目~数バイトを調べて形式を判定。 if instr(bin,0,"ffd8")=0:{//jpeg repeat 長さ-3//バイナリデータを扱いやすいように変換 poke bin,6+(cnt*2),strf("%02x",peek(Fbin,cnt+3)) loop in=instr(bin,0,"ffc00011")//サイズの位置が可変なので、サイズの始まりを示す文字を検索。 if in!-1:{//発見できたら処理。 in+10 sx=strf("%d",int("$"+strmid(bin,in+4,4))) sy=strf("%d",int("$"+strmid(bin,in+0,4))) }else:状態=-1//出来なかったら 形式=0 } //jpeg以外はサイズが書き込まれている位置が固定なのでjpegより簡単。 if instr(bin,0,"89504e")=0:{//png sx=strf("%d",int("$"+ ""+strf("%02x",peek(Fbin,18))+""+strf("%02x",peek(Fbin,19))+"" )) sy=strf("%d",int("$"+ ""+strf("%02x",peek(Fbin,22))+""+strf("%02x",peek(Fbin,23))+"" )) 形式=1 } if instr(bin,0,"424d38")=0:{//bmp //リトルエンディアンで書かれているので注意。 sx=strf("%d",int("$"+ ""+strf("%02x",peek(Fbin,19))+""+strf("%02x",peek(Fbin,18))+"" )) sy=strf("%d",int("$"+ ""+strf("%02x",peek(Fbin,4))+""+strf("%02x",peek(Fbin,3))+"" )) sy=int(double(sy)/22.5) 形式=2 } if instr(bin,0,"424d1c")=0:{//bmp-os2 //リトルエンディアン sx=strf("%d",int("$"+ ""+strf("%02x",peek(Fbin,19))+""+strf("%02x",peek(Fbin,18))+"" )) sy=strf("%d",int("$"+ ""+strf("%02x",peek(Fbin,21))+""+strf("%02x",peek(Fbin,20))+"" )) 形式=3 } if instr(bin,0,"474946")=0:{//gif //リトルエンディアン sx=strf("%d",int("$"+ ""+strf("%02x",peek(Fbin,7))+""+strf("%02x",peek(Fbin,6))+"" )) sy=strf("%d",int("$"+ ""+strf("%02x",peek(Fbin,9))+""+strf("%02x",peek(Fbin,8))+"" )) 形式=4 } if instr(bin,0,"00000a")=0:{//tga //リトルエンディアン sx=strf("%d",int("$"+ ""+strf("%02x",peek(Fbin,13))+""+strf("%02x",peek(Fbin,12))+"" )) sy=strf("%d",int("$"+ ""+strf("%02x",peek(Fbin,15))+""+strf("%02x",peek(Fbin,14))+"" )) 形式=5 } return (状態+形式) //取得できたサイズを返す。 #defcfunc Gsx return sx #defcfunc Gsy return sy #global #include "winmm.as"//pcの起動時間を測定 n=1000.0 button "開く",*開く stop *開く dialog "",16,"画像" if stat=1:{ timegettime:開始時間=stat //n回の平均 repeat n GetImg refstr st=stat if st=-2:形式="未知" if st=-1:形式="JPEG(サイズの取得に失敗)" if st=0:形式="JPEG" if st=1:形式="PNG" if st=2:形式="BMP" if st=3:形式="BMP-os2" if st=4:形式="GIF" if st=5:形式="TGA" loop timegettime 終了時間=stat Color 255,255,255:boxf:Color 0,0,0 pos 10,50:mes "形式"+形式+"\n"+gsx()+"x"+gsy()+"\n"+(double(終了時間-開始時間)/n)+"" }



cats

リンク

2016/1/23(Sat) 15:34:23|NO.74255

文字列を使っているようですが、せっかくバイナリ(整数値)として取得できるので
そのまま代入すれば良いと思います。
以下はpngの画像サイズを取得するコード。

#define ctype BigEndianInt(%1) (%1 >> 24 & 255) | ((%1 >> 16 & 255) << 8) | ((%1 >> 8 & 255) << 16) | (( %1 & 255) << 24) sdim buf, 64 bload "1.png", buf, 64, 0 w = 0 : h = 0 w = BigEndianInt(lpeek(buf, 16)) h = BigEndianInt(lpeek(buf, 20)) mes strf("%d x %d", w, h) stop



スペース

リンク

2016/1/23(Sat) 16:59:09|NO.74256

>catsさん
有難うございます。
BigEndianIntは4バイト用でしょうか?
wpeekに使うと上手く行かず、BigEndianIntがどのような処理をしているのかもよくわかりません。
もしよろしければ|の意味等、書いているページを紹介して頂けると嬉しいです。

(%1 >> 24 & 255) | ((%1 >> 16 & 255) << 8) | ((%1 >> 8 & 255) << 16) | (( %1 & 255) << 24)



GENKI

リンク

2016/1/23(Sat) 19:03:45|NO.74257

> もしよろしければ|の意味等、書いているページを紹介して頂けると嬉しいです。

どうぞ。
HSP開発wiki
http://wiki.hsp.moe/%E5%B0%8F%E3%83%AF%E3%82%B6%EF%BC%8F%E3%83%93%E3%83%83%E3%83%88%E6%93%8D%E4%BD%9C.html
http://wiki.hsp.moe/HSP%E8%AC%9B%E5%BA%A7%EF%BC%8F%E3%83%93%E3%83%83%E3%83%88%E6%BC%94%E7%AE%97.html



KA

リンク

2016/1/23(Sat) 19:22:42|NO.74258

簡単に言うと1234のバイト並びを、4321と入れ替える処理です。
peek poke でも出来る。

私なら
((%1 & $FF000000) >> 24) | ((%1 & $00FF0000) >> 8) | ((%1 & $0000FF00) << 8) | ((%1 & $000000FF) << 24)
整理して
(%1 >> 24) | ((%1 & $FF0000) >> 8) | ((%1 & $FF00) << 8) | ((%1 & $FF) << 24)
と書くかも


A=$12345678 B=(A>>24)|((A&$FF0000)>>8)|((A&$FF00)<<8)|((A&$FF)<<24) mes strf("%08X",B) stop

例)16進4バイト(32ビット)数値 12 34 56 78 を
内部的に 78 56 34 12 の並びで扱うのがビッグエンディアン
内部的に 12 34 56 78 の並びで扱うのがリトルエンディアン

Windows は前者の処理系なのでHSPも前者です。
デバッグウインドウのメモリダンプで確認できます。



cats

リンク

2016/1/23(Sat) 20:20:39|NO.74259

> スペース さん
論理和の意味や、ビッグエンディアンの説明は、他の方がしてくださっている通りです。
PNGのファイル構造を見ると、画像のサイズはIHDR Chunk内にビッグエンディアンで格納されています。
http://homepage2.nifty.com/sophia0/png.html
一方HSPを含め、Windowsのメインメモリはリトルエンディアンを採用しているので、
例えばサイズが255なら、メモリ上では
FF 00 00 00
となります。
しかし、PNG内ではビッグエンディアンで保存されているので、
00 00 00 FF
という形でlpeekにより読み込まれます。

BigEndianIntは上記のように、4バイトのデータのエンディアンを変更しています。
wpeekを使って上手く動かないということは、00 00 00 FFの先頭00 00だけが取得されているのかもしれません。
PNGのIHDR内では画像サイズは4バイトで固定なので、wpeekを使う場所がよく分かりませんが......



スペース

リンク

2016/1/23(Sat) 20:44:57|NO.74260

皆さん説明ありがとうございます。
開発wikiのページは非常に参考になりました。
>>とかはビットシフトだったんですか。

png等はlpeekで問題ないのですが、
jpegだけは2バイトになっているようで、lpeekが使えません。
wpeekの場合、BigEndianIntの処理はどのように変えれば良いのでしょうか?



cats

リンク

2016/1/23(Sat) 21:42:02|NO.74261

なるほど、jpegは2バイトなんですね。
基本的に、順序を反転すると考えてもらえば正しいです。
wpeekで取得した値は2バイトなので、
12 AB => AB 12
となるようなマクロを作成すればいいです。
まず前提として、1バイト=8ビットです。
そのため、値0xABを0xAB00にする(1バイトずらす)には、

0xAB << 8
とします。逆に0x1200を0x12にするには、

0x1200 >> 8
とします。また、0x12ABから0xABを取り出すには、

0x12AB & 0xFF
とします。(論理積)
これらを組み合わせると、

#define ctype wBigEndianInt(%1) (%1 >> 8) | (%1 & 255) << 8
のようになります。
jpeg版も作って見ました。(本当は少しずつbloadした方がいいのですが、一度に全部読み込みました)

#define ctype wBigEndianInt(%1) (%1 >> 8) | (%1 & 255) << 8 exist "1.jpg" fsize = strsize sdim buf, fsize bload "1.jpg", buf, fsize, 0 w = 0 : h = 0 offset = 0 while offset < fsize if ( wpeek(buf, offset) == 0xD8FF ) : offset += 2 ; signature if ( wpeek(buf, offset) == 0xC0FF ) { ; info w = wBigEndianInt(wpeek(buf, offset+5)) h = wBigEndianInt(wpeek(buf, offset+7)) _break } offset += 2 + wBigEndianInt(wpeek(buf, offset+2)) wend mes strf("%d x %d", w, h) stop



スペース

リンク

2016/1/24(Sun) 00:06:39|NO.74262

>窓月ららさん、kanamaruさん、catsさん、KAさん、GENKIさん
皆さんのお陰で随分と改善しました。
とりあえずこれで完成とします。

#module #define ctype BigEndianInt(%1) (%1 >> 24 & 255) | ((%1 >> 16 & 255) << 8) | ((%1 >> 8 & 255) << 16) | (( %1 & 255) << 24) #define ctype wBigEndianInt(%1) (%1 >> 8) | (%1 & 255) << 8 /* p1 画像のパス jpegの場合、正常に完了するとstatが0。 jpegでサイズが取得できなかった場合は-1が返ります。 pngは1、bmpは2が返ります。 つまり正常に終了した場合、0以上が返ります。 jpeg以外はサイズが書き込まれている位置が固定なので、 取得できないことはあり得ませんが、 正常な値が取得されているとは限りません。 */ #deffunc GetImg str p1 if ct=0:{//初回に限り変数を初期化。 //ファイルの0バイト目から取得する長さ。 //明確に何バイト目までにサイズが書かれているか調べてもわからなかったので、 //手元のファイルを元に適当に設定。 長さ=1024 sdim Fbin,長さ sdim bin,長さ*2+1 ct=1 } bload p1,Fbin,長さ,0//ロード repeat 3//バイナリデータを扱いやすいように変換 poke bin,cnt*2,strf("%02x",peek(Fbin,cnt)) loop 状態=0 形式=-2 //0バイト目~数バイトを調べて形式を判定。 if instr(bin,0,"ffd8")=0:{//jpeg repeat 長さ-3//バイナリデータを扱いやすいように変換 poke bin,6+(cnt*2),strf("%02x",peek(Fbin,cnt+3)) loop in=instr(bin,0,"ffc00011")//サイズの位置が可変なので、サイズの始まりを示す文字を検索。 if in!-1:{//発見できたら処理。 in+10 //sx=strf("%d",int("$"+strmid(bin,in+4,4))) //sy=strf("%d",int("$"+strmid(bin,in+0,4))) sx=strf("%d",wBigEndianInt(wpeek(Fbin,(in/2)+2)))//これでいいはず・・・? sy=strf("%d",wBigEndianInt(wpeek(Fbin,(in/2))))//でも何故かうまくいかない。 }else:状態=-1//出来なかったら 形式=0 } //jpeg以外はサイズが書き込まれている位置が固定なのでjpegより簡単。 if instr(bin,0,"89504e")=0:{//png sx=strf("%d",BigEndianInt(lpeek(Fbin,16))) sy=strf("%d",BigEndianInt(lpeek(Fbin,20))) 形式=1 } if instr(bin,0,"424d")=0:{//bmp //リトルエンディアンで書かれているので注意。 sx=strf("%d",wpeek(Fbin,18)) sy=strf("%d",wpeek(Fbin,3)) sy=int(double(sy)/22.5) 形式=2 } if instr(bin,0,"474946")=0:{//gif //リトルエンディアン sx=strf("%d",wpeek(Fbin,6)) sy=strf("%d",wpeek(Fbin,8)) 形式=3 } if instr(bin,0,"00000a")=0:{//tga //リトルエンディアン sx=strf("%d",wpeek(Fbin,12)) sy=strf("%d",wpeek(Fbin,14)) 形式=4 } return (状態+形式) //取得できたサイズを返す。 #defcfunc Gsx return sx #defcfunc Gsy return sy #global #include "winmm.as"//pcの起動時間を測定 n=1000.0 button "開く",*開く stop *開く dialog "",16,"画像" if stat=1:{ timegettime:開始時間=stat //n回の平均 repeat n GetImg refstr st=stat if st=-2:形式="未知" if st=-1:形式="JPEG(サイズの取得に失敗)" if st=0:形式="JPEG" if st=1:形式="PNG" if st=2:形式="BMP" if st=3:形式="GIF" if st=4:形式="TGA" loop timegettime 終了時間=stat Color 255,255,255:boxf:Color 0,0,0 pos 10,50:mes "形式"+形式+"\n"+gsx()+"x"+gsy()+"\n"+(double(終了時間-開始時間)/n)+"" }



ONION software Copyright 1997-2023(c) All rights reserved.