|
|
|
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で読み込めますし対応してたほうが何かと便利そうですね。
後で試してみます。
|
|
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です。
|
|
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なので存在意義はある・・・はず?
皆さん報告有難うございます。まだまだ受け付けているので、暇がある時にでもお願いします。
|
|
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)+""
}
| |
|
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)
|
|
2016/1/23(Sat) 19:03:45|NO.74257
|
|
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も前者です。
デバッグウインドウのメモリダンプで確認できます。
|
|
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の処理はどのように変えれば良いのでしょうか?
|
|
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)+""
}
| |
|