twain(スキャナ)での画像取得と表示

[戻る]
シオン 2005/09/02(金) 11:37:17
はじめましてシオンといいます。

WindowsXP Professional、VC++6.0 SDK でC言語を使って作成しています。

現在twainを使ってスキャナから画像を直接取得するプログラムを作成しています。
しかし、twainのサンプルが非常に少なく困っています。

twainの使い方を説明しているサイトを調べてみたら、以下のページを見つけました。
http://hp.vector.co.jp/authors/VA011973/prg_twain.htm

スキャナを動作させ、画像データを取り込むまではできたのですが、
肝心の画像を表示する部分の説明がなく、表示することができなくて困っています。

スキャナから取得したビットマップハンドルから画像を表示させるにはどうしたらいい
でしょうか。
また、画像表示部分のソースが公開されているサイトなど、
知っていらっしゃる方がいましたら教えてください。

よろしくお願いします。

dairygoods 2005/09/02(金) 12:52:51
twain については分かりませんが、
その「ビットマップハンドル」というのがtwain 固有のものでなく、
一般的なビットマップハンドルを表すものでしたら、
たとえば、DrawState で描画できます。

そもそも、画面に描画する方法自体がさっぱりわからない
ということでしたら、このあたりが参考になるのでは。
http://www.kumei.ne.jp/c_lang/sdk/sdk_23.htm

シオン 2005/09/02(金) 13:51:23
返信ありがとうございます。
現在、以下のプログラムを作って試しているのですが、



    TW_UINT16 rc;
    TW_UINT32 hBitmap = NULL;
    HANDLE hbm_acq = NULL;
    HDC hMemDC;
    BITMAP bmp;

    :(スキャナの設定など)

    // スキャナへ画像取得するよう命令する
    rc = lpfnDSM_Entry(&pAppId, &pSourceId, DG_IMAGE, DAT_IMAGENATIVEXFER, 
MSG_GET, (TW_MEMREF)&hBitmap);

    // 戻値のチェック
    switch (rc) {
        case TWRC_XFERDONE: // 成功
            hbm_acq = (HBITMAP)hBitmap;

            hMemDC = CreateCompatibleDC(NULL);
            if (NULL == hMemDC) {
                // CreateCompatibleDC失敗
            }

            if (NULL == SelectObject(hMemDC, hbm_acq)) {
                // SelectObject失敗
            }

            if (0 == GetObject(hbm_acq, sizeof(BITMAP), &bmp)) {
                // GetObject失敗
            }

            if (0 == DeleteObject(hbm_acq)) {
                // DeleteObject失敗
            }

            // 表示
            BitBlt(表示用hdc, 0, 0, bmp.bmWidth, bmp.bmHeight, hMemDC, 0, 0, 
SRCCOPY);

            :(画面更新)
            break;

        case TWRC_CANCEL: // キャンセル
            :
            break;

        case TWRC_FAILURE: // 転送中にエラーが発生
            :
            break;
    }



どうしてもSelectObject関数で失敗してしまうのです。
twainのビットマップハンドルとwindows(?)のビットマップハンドルの型が違うからでし
ょうか。
スキャナから取得したビットマップハンドルはTW_UINT32です。

dairygoods 2005/09/02(金) 14:39:05
「12.データ転送(状態5←→状態6)」を見ると、

| LPBITMAPINFOHEADER    lpDib;
| 
| // 画像転送
| hBitmap = NULL;
| rc = lpfnDSM_Entry(pAppId,pSourceId,DG_IMAGE,
|            DAT_IMAGENATIVEXFER,MSG_GET,(TW_MEMREF)&hBitmap);
| 
| lpDib = (LPBITMAPINFOHEADER)GlobalLock((void*)hBitmap);

とやって DIB を得ているようですから、
CreateDIBitmap とかでビットマップを作るんじゃないでしょうか。

Tonny 2005/09/02(金) 18:16:41
twainが出力するのはHDIBです。
dairygoodsさんの書かれている様にCreateDIBtimapでビットマップも作れますし、
そのままでもSetDIBitsToDeviceで描画できます。

>lpDib = (LPBITMAPINFOHEADER)GlobalLock((void*)hBitmap);
これで幅や高さは取得できますし、

(BYTE)lpDib + sizeof(BITMAPINFOHEADER) (+ あるのならパレットサイズ)
で、画像情報の先頭のアドレスも取得できます。

そのままの方が、bmpファイルに書き出すのには便利です。
(BITMAPFILEHEADERをつけるだけ)
MSDNのDIBLOOKが参考になります。

シオン 2005/09/08(木) 12:49:18
dairygoodsさん、Tonnyさんありがとうございます。
以下のソースを見てください。

---------------------------------------------------------------------
TW_UINT16 rc;
TW_UINT32 hBitmap = NULL;
LPBITMAPINFOHEADER lpDIB;

:

// 画像取得
rc = lpfnDSM_Entry(&pAppId, &pSourceId, DG_IMAGE, DAT_IMAGENATIVEXFER, 
MSG_GET, (TW_MEMREF)&hBitmap);

// DIBの情報を取得
lpDIB = (LPBITMAPINFOHEADER)GlobalLock((void*)hBitmap);

// 戻値のチェック
switch (rc) {
    case TWRC_XFERDONE:
        if (lpDIB != NULL) {
            // 取得した画像のサイズを取得
            size_x = lpDIB->biWidth;
            size_y = lpDIB->biHeight;

            // 表示
            SetDIBitsToDevice(
                win_hdc,
                10, 0,
                size_x, size_y,
                0, 0,
                0, size_y,
                lpDIB,                
        // ←ここ
                (BITMAPINFO *)lpDIB,
                DIB_RGB_COLORS
            );

            // 画面更新
            :
        }
        break;

    case TWRC_CANCEL: // キャンセル
        break;
    case TWRC_FAILURE: // エラー
        break;
}

:

---------------------------------------------------------------------

SetDIBitsToDevice関数を使って表示することができたのですが、
第10引数(ピクセルビットの配列の先頭へのポインタ)を上記のまま実行すると、
色がおかしくなってしまいました。
赤が青になって、黄色が紫色になってしまうのです。
RGBが、(255, 0, 0) → (0, 0, 255)となって1バイトずれているみたいです。
(取得した画像は24ビットです)
また、画像も左の一部分が右側に表示されてしまいます。


表示する関数を、
            SetDIBitsToDevice(
                win_hdc,
                10, 0,
                size_x, size_y,
                0, 0,
                0, size_y,
                lpDIB + 1,            
            // ←ここ
                (BITMAPINFO *)lpDIB,
                DIB_RGB_COLORS
            );

+ 1 すると正常に表示できました。

一応表示できたのですが、なぜ+1しないと正常に表示できないかわかりません。
もう少し情報をいただけないでしょうか。お願いします。

dairygoods 2005/09/08(木) 13:18:12
まず、SetDIBitsToDevice の引数

  CONST VOID *lpvBits,     // DIB ビットからなる配列
  CONST BITMAPINFO *lpbmi, // ビットマップ情報

は違う情報を求めているのに、
両方に同じポインタを指定することは変だとは思いませんか?
#キャストしても指す先は同じです。
#ビットマップ情報がビットデータに化けるわけではありません。

では、lpvBits に指定すべき「DIB ビットからなる配列」は
どこにあるのかということについては、
DIBの構造を調べてみてください。

例:http://www.kk.iij4u.or.jp/~kondo/bmp/

Tonny 2005/09/08(木) 18:45:04
>+ 1 すると正常に表示できました。
1番下の列にノイズが入っている + 1番上の列が切れている状態で、正常でないはずです。
たまたまバイト区切りが一致した為そう見えているだけです。

lpvBits = (BYTE)lpDib + sizeof(BITMAPINFOHEADER) (+ あるのならパレットサイズ)
って書いたのに。

Tonny 2005/09/08(木) 18:56:23
void* GetLpBits(void* lpDib)
{
   BITMAPINFOHEADER* lpHeader = (BITMAPINFOHEADER*)lpDIB ;

   // biClrUsedに数値が入っていれば、それがパレットの数
   DWORD pal =  lpHeader->biClrUsed ;

   // biClrUsedが0でもパレットがある場合がある。
   if(pal == 0 )
   {
      switch(lpHeader->biBitCount)
      {
      case 1:    pal=  2;    break;
      case 4:    pal= 16;    break;
      case 8:    pal=256;    break;
      default :    pal= 0;
      }
   }

   // BI_BITFIELDSの場合はビットフィールドを持っている
   DWORD fields = 0 ;
   if( lpHeader->biCompression == BI_BITFIELDS && 
    (lpHead->biBitCount == 32 || pHead->biBitCount == 16 ) )
      fields = 3 * sizeof(DWORD) ;

   return  (BYTE*)lpDIB + sizeof(BITMAPINFOHEADER)
        + pal * sizeof(RGBQUAD) + fields;
}

dairygoods 2005/09/08(木) 20:01:25
> 1番下の列にノイズが入っている + 1番上の列が切れている状態で、正常でないはずです。

LPBITMAPINFOHEADER lpDIB;
なので、パレットがない場合は、lpDIB+1 は正しい値だったりします。

Tonny 2005/09/08(木) 20:24:40
>LPBITMAPINFOHEADER lpDIB;
>なので、パレットがない場合は、lpDIB+1 は正しい値だったりします。
ああ、+1 がsizeof(BITMAPINFOHEADER) に相当するということですね。

シオン 2005/09/09(金) 15:39:03
> 両方に同じポインタを指定することは変だとは思いませんか?
> #キャストしても指す先は同じです。
> #ビットマップ情報がビットデータに化けるわけではありません。
よく考えてみればそうでした。キャストの意味がわっていなかったみたいです。ごめん
なさい。


Tonnyさん、すみません。見逃していました。
関数のところを以下に変更しました。
それから、biBitCountが違う場合のソースを書いていただきありがとうございます。

-------------------------------------------------------------------
BYTE *lpvBits;

:

lpvBits = (BYTE *)lpDIB + sizeof(BITMAPINFOHEADER);
SetDIBitsToDevice(
    win_hdc,
    0, 0,
    size_x, size_y,
    0, 0,
    0, size_y,
    lpvBits,
    (BITMAPINFO *)lpDIB,
    DIB_RGB_COLORS
);
-------------------------------------------------------------------



> >LPBITMAPINFOHEADER lpDIB;
> >なので、パレットがない場合は、lpDIB+1 は正しい値だったりします。
> ああ、+1 がsizeof(BITMAPINFOHEADER) に相当するということですね。
それはどういうことでしょうか。
sizeof(BITMAPINFOHEADER)は40バイトですよね。
ということは、+ 1 は40バイトずらしたことと同じことなのでしょうか。

dairygoods 2005/09/09(金) 20:29:51
> ということは、+ 1 は40バイトずらしたことと同じことなのでしょうか。

はい。

Tonny 2005/09/10(土) 11:01:08
char* c = XX ;
char* cc = c + 1 ; // ccは1バイトずれる。

int* i = XX ;
int* ii = i + 1 ; // iiは4バイトずれる。

BITMAPINFOHEADER* pInfo = XX ;
BITMAPINFOHEADER* pInfo2 = pInfo + 1 ; //pInfo2は40バイトずれる。

です。

シオン 2005/09/12(月) 14:19:03
[[解決]]
大変よくわかりました。
その型によって+1でずれるバイト数が変わってくるのですね。

dairygoodsさん、Tonnyさんどうもありがとうございました。
画像も無事表示できました。

古いスレッドには追加発言できません。