DS-3DW300 という、540 円で販売されているステレオカメラがある。

面白そうなので、一台購入してみた。

 

image

ざっくりレビュー

解像度は 640×480。YUY2、30fps。

撮影モードは、左カメラ、右カメラ、ステレオ(赤青)、ステレオ(side-by-side)の 4 つ。

撮影モードの切り替えは、専用ソフトを用いて行う。

カメラ間距離は 4cm。左右のカメラで同期して撮影ができる。

UVC デバイスであるため、デバイスドライバを別途導入する必要はない。

シャッター速度が速く、ブレが発生しにくい一方、ゲインが大きく、画はノイジーだ。

高速で移動させると、CMOSセンサー特有の歪みが見られる。

レンズのフォーカスは固定で、ピントの山がつかみにくく、全体的にボケている。

歪みは少なく、外周部でわずかなたる型歪みが見受けられる程度。

画角は、目測で水平 60 度くらい。

画質は、ひと昔の Web カメラという感じだ。

価格を考えれば、充分と言えると思う。

 

image

 

プログラムから使用する

UVC デバイスのなので、DirectShow で普通に使える。

試していないが、Video For Linux でも使えるだろう。

 

撮影モードの切り替えは、専用ソフトを利用する必要がある。

Web 上では、プログラムで専用ソフトを起動する方法を提案されている方がいる。

激安3D ウェブカメラ(DS-3DW300)をVC++のOpenCV上で使う – のびっこ日記

 

この方法の欠点は、複数の DS-3DW300 を同時に扱えないことと、

ソフトが勝手に立ち上がってきて、ちょっとだけ格好悪いことである。

何か、別のやり方で撮影モードを切り替えることはできないだろうか。

 

手段を選ばなければ・・・

撮影モードの切り替えは、USB 転送でデバイスに命令を送ることで実現してるようだ。

この命令を再現することで、撮影モードの切り替えが期待できる。

 

Windows での手順は以下となる。

1. DS-3DW300 のデバイスインターフェース名を得る(複数の DS-3DW300 が接続されていても、それぞれ異なるため区別できる)

2. CreateFile 関数でデバイスハンドルを得る

3. DeviceIoControl 関数で、デバイスハンドルと適切なパラメータを設定し、USB 転送を行う

 

やっかいなのは、1 と 3 である。

まず、1 についてだが、GUID から取得する方法が使える。

GUIDからデバイスドライバのデバイスインターフェース名を取得する – 金澤ソフト設計

 

複数の DS-3DW300 が接続されていることを考慮すると、例えば以下のように書ける。

 

#include <windows.h>
#include <setupapi.h>
#pragma comment(lib, "setupapi.lib")

static std::list<std::wstring> GetDeviceNamesFromGUID(const GUID* guid)
{
	std::list<std::wstring> nameList;

	HDEVINFO hDev = SetupDiGetClassDevs(guid, NULL, NULL, (DIGCF_PRESENT | DIGCF_INTERFACEDEVICE));

	for (int idx = 0;; idx++)
	{
		SP_INTERFACE_DEVICE_DATA info;
		info.cbSize = sizeof(SP_INTERFACE_DEVICE_DATA);

		if (SetupDiEnumDeviceInterfaces(hDev, 0, guid, idx, &info) == FALSE) break;

		DWORD size;
		SetupDiGetDeviceInterfaceDetail(hDev, &info, NULL, 0, &size, NULL);

		PSP_INTERFACE_DEVICE_DETAIL_DATA_W detail = reinterpret_cast<PSP_INTERFACE_DEVICE_DETAIL_DATA_W>(new UCHAR[size]);
		ZeroMemory(detail, size);
		detail->cbSize = sizeof(SP_INTERFACE_DEVICE_DETAIL_DATA);

		DWORD len;
		if (SetupDiGetInterfaceDeviceDetailW(hDev, &info, detail, size, &len, NULL))
		{
			nameList.push_back(std::wstring(detail->DevicePath));
		}

		delete[] detail;
	}

	SetupDiDestroyDeviceInfoList(hDev);

	return nameList;
}

 

GUID だけでは、関係ない複数のデバイスが重複してヒットするので、同時に VID/PID で絞り込む。

 

static std::list<std::wstring> GetDeviceNames(void)
{
	static const GUID GUID_DS3DW300 = { 0x65e8773d, 0x8f56, 0x11d0, { 0xa3, 0xb9, 0x00, 0xa0, 0xc9, 0x22, 0x31, 0x96 } };
	auto nameList = GetDeviceNamesFromGUID(&GUID_DS3DW300);

	// guid だけでは関係ないものも候補として出てくるので、vid/pid で絞込み
	std::wregex vidpidMathcer(L"vid_18e3&pid_5031");
	for (auto itr = nameList.begin(); itr != nameList.end();)
	{
		if (std::regex_search(itr->c_str(), vidpidMathcer) == false)
		{
			itr = nameList.erase(itr);
			continue;
		}
		itr++;
	}

	return nameList;
}

 

次に、3 についてだが、これは真っ当な手段では実現できない。

専用ソフトウェアのメモリをダンプし、必要となるパラメータを抽出する必要がある。

この行為は、リバースエンジニアリングに当たる可能性が高く、ライセンス上禁止されている。

 

使用制限
エンドユーザはソフトウェアを貸し出したり、リースまたはローンしたりすることはできません。ソフトウェアを変更、応用、翻訳、リバースエンジニアリング、逆コンパイル、解体することはできません。ソフトウェア全体またはその一部を転売して利益を得たり、配布したり、模造製品を作成したりすることはできません。

 

一方で、リバースエンジニアリングはフェアユースだという考え方もあるようだ。
(よくわからないので信用しないでほしい)

リバースエンジニアリング – Wikipedia

 

もしこのような考えを持ち、実行するのならば、自己責任でお願いしたい。

私は一切の責任を負えない。