ublas::vector, ublas::matrix や OpenCV の CvMat を Visual Studio のデバッグウィンドウに表示する方法

boost.vector や boost.matrix, OpenCV の CvMat, IplImage, Vec, Mat は非常に便利ですが,一つ残念な事があります.
それは Visual C++ の“自動変数”や“ローカル”,“ウォッチ”ウィンドウ等にベクトルや行列の要素ではなく,そのクラスのメンバ変数が表示されてしまうことです.
そのため,ublas::vector や CvMat, cv::Vec, cv::Mat の要素や行数,列数を調べるには内部表現を辿っていき一つ一つ調べたり,あるいはわざわざ printf などで表示して調べるという非常に煩わしい作業が必要になります.
しかし実は,autoexp.dat というファイルを修正することによって,ublas.vectorOpenCV.CvMat, IplImage の要素を一目で分かるように表示させることができるようになります.今回はその方法を簡単に紹介します.
まず autoexp.dat ファイルを開きます(autoexp.dat は通常 C:\Program Files\Microsoft Visual Studio 9.0\Common7\Packages\Debugger にあります.9.0 の部分はインストールしている Visual Studio のバージョンによって変わりますので適宜変更してください).
次に autoexp.dat の

[Visualizer]
; This section contains visualizers for STL and ATL containers
; DO NOT MODIFY

と書かれている行のすぐ下,つまり“; DO NOT MODIFY”と書かれている行の次の行に以下の内容をペーストします.

boost::numeric::ublas::vector<*,*>{
	children
	(
		#array
		(
			expr :	($c.data_.data_)[$i],
			size :	$c.data_.size_
		)
	)
	preview
	(
		#(
			"[",
			$c.data_.size_,
			"](",
			#array
			(
				expr : 	($c.data_.data_)[$i],
				size : 	$c.data_.size_
			),
			")"
		)
	)
}
boost::numeric::ublas::matrix<*,*,*>{
	children
	(
		#array
		(
			expr :	($c.data_.data_)[$i],
			size :	$c.data_.size_
		)
	)
	preview
	(
		#(
			"[",
			$c.size1_,
			",",
			$c.size2_,
			"](",
			#array
			(
				expr : 	($c.data_.data_)[$i],
				size : 	$c.data_.size_
			),
			")"
		)
	)
}
CvMat{
	children
	(
		#switch($c.type & 0x7)
		#case 0
		(
			#array
			(
				expr :	($c.data.ptr)[$i],
				size :	$c.rows * $c.cols
			)
		)
		#case 1
		(
			#array
			(
				expr :	(char)($c.data.ptr)[$i],
				size :	$c.rows * $c.cols
			)
		)
		#case 2
		(
			#array
			(
				expr :	(unsigned short)($c.data.s)[$i],
				size :	$c.rows * $c.cols
			)
		)
		#case 3
		(
			#array
			(
				expr :	($c.data.s)[$i],
				size :	$c.rows * $c.cols
			)
		)
		#case 4
		(
			#array
			(
				expr :	($c.data.i)[$i],
				size :	$c.rows * $c.cols
			)
		)
		#case 5
		(
			#array
			(
				expr :	($c.data.fl)[$i],
				size :	$c.rows * $c.cols
			)
		)
		#case 6
		(
			#array
			(
				expr :	($c.data.db)[$i],
				size :	$c.rows * $c.cols
			)
		)
	)
	preview
	(
		#switch($c.type & 0x7)
		#case 0
		(
			#(
				"[",
				$c.rows,
				",",
				$c.cols,
				"](",
				#array
				(
					expr : 	($c.data.ptr)[$i],
					size : 	$c.rows * $c.cols
				),
				")"
			)
		)
		#case 1
		(
			#(
				"[",
				$c.rows,
				",",
				$c.cols,
				"](",
				#array
				(
					expr : 	(char)($c.data.ptr)[$i],
					size : 	$c.rows * $c.cols
				),
				")"
			)
		)
		#case 2
		(
			#(
				"[",
				$c.rows,
				",",
				$c.cols,
				"](",
				#array
				(
					expr : 	(unsigned short)($c.data.s)[$i],
					size : 	$c.rows * $c.cols
				),
				")"
			)
		)
		#case 3
		(
			#(
				"[",
				$c.rows,
				",",
				$c.cols,
				"](",
				#array
				(
					expr : 	($c.data.s)[$i],
					size : 	$c.rows * $c.cols
				),
				")"
			)
		)
		#case 4
		(
			#(
				"[",
				$c.rows,
				",",
				$c.cols,
				"](",
				#array
				(
					expr : 	($c.data.i)[$i],
					size : 	$c.rows * $c.cols
				),
				")"
			)
		)
		#case 5
		(
			#(
				"[",
				$c.rows,
				",",
				$c.cols,
				"](",
				#array
				(
					expr : 	($c.data.fl)[$i],
					size : 	$c.rows * $c.cols
				),
				")"
			)
		)
		#case 6
		(
			#(
				"[",
				$c.rows,
				",",
				$c.cols,
				"](",
				#array
				(
					expr : 	($c.data.db)[$i],
					size : 	$c.rows * $c.cols
				),
				")"
			)
		)
	)
}
_IplImage{
	children
	(
		#switch((unsigned int)$c.depth)
		#case 1
		(
			#array
			(
				expr : 	((bool*)$c.imageData)[$i],
				size : 	$c.width * $c.height * $c.nChannels
			)
		)
		#case 8
		(
			#array
			(
				expr : 	((unsigned char*)$c.imageData)[$i + $i / ($c.width * $c.nChannels) * ($c.widthStep / sizeof(unsigned char) - $c.width * $c.nChannels)],
				size : 	$c.width * $c.height * $c.nChannels
			)
		)
		#case 0x80000008
		(
			#array
			(
				expr : 	((char*)$c.imageData)[$i + $i / ($c.width * $c.nChannels) * ($c.widthStep / sizeof(char) - $c.width * $c.nChannels)],
				size : 	$c.width * $c.height * $c.nChannels
			)
		)
		#case 16
		(
			#array
			(
				expr : 	((unsigned short*)$c.imageData)[$i + $i / ($c.width * $c.nChannels) * ($c.widthStep / sizeof(unsigned short) - $c.width * $c.nChannels)],
				size : 	$c.width * $c.height * $c.nChannels
			)
		)
		#case 0x80000010
		(
			#array
			(
				expr : 	((short*)$c.imageData)[$i + $i / ($c.width * $c.nChannels) * ($c.widthStep / sizeof(short) - $c.width * $c.nChannels)],
				size : 	$c.width * $c.height * $c.nChannels
			)
		)
		#case 0x80000020
		(
			#array
			(
				expr : 	((int*)$c.imageData)[$i + $i / ($c.width * $c.nChannels) * ($c.widthStep / sizeof(int) - $c.width * $c.nChannels)],
				size : 	$c.width * $c.height * $c.nChannels
			)
		)
		#case 32
		(
			#array
			(
				expr : 	((float*)$c.imageData)[$i + $i / ($c.width * $c.nChannels) * ($c.widthStep / sizeof(float) - $c.width * $c.nChannels)],
				size : 	$c.width * $c.height * $c.nChannels
			)
		)
		#case 64
		(
			#array
			(
				expr : 	((double*)$c.imageData)[$i + $i / ($c.width * $c.nChannels) * ($c.widthStep / sizeof(double) - $c.width * $c.nChannels)],
				size : 	$c.width * $c.height * $c.nChannels
			)
		)
	)
	preview
	(
		#switch((unsigned int)$c.depth)
		#case 1
		(
			#(
				"[",
				$c.width,
				",",
				$c.height,
				",",
				$c.nChannels,
				",",
				"IPL_DEPTH_1U",
				"](",
				#array
				(
					expr : 	((bool*)$c.imageData)[$i],
					size : 	$c.width * $c.height * $c.nChannels
				),
				")"
			)
		)
		#case 8
		(
			#(
				"[",
				$c.width,
				",",
				$c.height,
				",",
				$c.nChannels,
				",",
				"IPL_DEPTH_8U",
				"](",
				#array
				(
					expr : 	((unsigned char*)$c.imageData)[$i + $i / ($c.width * $c.nChannels) * ($c.widthStep / sizeof(unsigned char) - $c.width * $c.nChannels)],
					size : 	$c.width * $c.height * $c.nChannels
				),
				")"
			)
		)
		#case 0x80000008
		(
			#(
				"[",
				$c.width,
				",",
				$c.height,
				",",
				$c.nChannels,
				",",
				"IPL_DEPTH_8S",
				"](",
				#array
				(
					expr : 	expr : 	((char*)$c.imageData)[$i + $i / ($c.width * $c.nChannels) * ($c.widthStep / sizeof(char) - $c.width * $c.nChannels)],
					size : 	$c.width * $c.height * $c.nChannels
				),
				")"
			)
		)
		#case 16
		(
			#(
				"[",
				$c.width,
				",",
				$c.height,
				",",
				$c.nChannels,
				",",
				"IPL_DEPTH_16U",
				"](",
				#array
				(
					expr : 	((unsigned short*)$c.imageData)[$i + $i / ($c.width * $c.nChannels) * ($c.widthStep / sizeof(unsigned short) - $c.width * $c.nChannels)],
					size : 	$c.width * $c.height * $c.nChannels
				),
				")"
			)
		)
		#case 0x80000010
		(
			#(
				"[",
				$c.width,
				",",
				$c.height,
				",",
				$c.nChannels,
				",",
				"IPL_DEPTH_16S",
				"](",
				#array
				(
					expr : 	expr : 	((short*)$c.imageData)[$i + $i / ($c.width * $c.nChannels) * ($c.widthStep / sizeof(short) - $c.width * $c.nChannels)],
					size : 	$c.width * $c.height * $c.nChannels
				),
				")"
			)
		)
		#case 0x80000020
		(
			#(
				"[",
				$c.width,
				",",
				$c.height,
				",",
				$c.nChannels,
				",",
				"IPL_DEPTH_32S",
				"](",
				#array
				(
					expr : 	((int*)$c.imageData)[$i + $i / ($c.width * $c.nChannels) * ($c.widthStep / sizeof(int) - $c.width * $c.nChannels)],
					size : 	$c.width * $c.height * $c.nChannels
				),
				")"
			)
		)
		#case 32
		(
			#(
				"[",
				$c.width,
				",",
				$c.height,
				",",
				$c.nChannels,
				",",
				"IPL_DEPTH_32F",
				"](",
				#array
				(
					expr : 	((float*)$c.imageData)[$i + $i / ($c.width * $c.nChannels) * ($c.widthStep / sizeof(float) - $c.width * $c.nChannels)],
					size : 	$c.width * $c.height * $c.nChannels
				),
				")"
			)
		)
		#case 64
		(
			#(
				"[",
				$c.width,
				",",
				$c.height,
				",",
				$c.nChannels,
				",",
				"IPL_DEPTH_64F",
				"](",
				#array
				(
					expr : 	((double*)$c.imageData)[$i + $i / ($c.width * $c.nChannels) * ($c.widthStep / sizeof(double) - $c.width * $c.nChannels)],
					size : 	$c.width * $c.height * $c.nChannels
				),
				")"
			)
		)
	)
}
cv::Vec<*,*>{
	children
	(
		#array
		(
			expr :	($c.val)[$i],
			size :	$T2
		)
	)
	preview
	(
		#(
			"[",
			$T2,
			"](",
			#array
			(
				expr :	($c.val)[$i],
				size :	$T2
			),
			")"
		)
	)
}
cv::Mat{
	children
	(
		#switch($c.flags & 0x7)
		#case 0
		(
			#array
			(
				expr :	((unsigned char*)$c.data)[$i],
				size :	$c.rows * $c.cols
			)
		)
		#case 1
		(
			#array
			(
				expr :	((char*)$c.data)[$i],
				size :	$c.rows * $c.cols
			)
		)
		#case 2
		(
			#array
			(
				expr :	((unsigned short*)$c.data)[$i],
				size :	$c.rows * $c.cols
			)
		)
		#case 3
		(
			#array
			(
				expr :	((short*)$c.data)[$i],
				size :	$c.rows * $c.cols
			)
		)
		#case 4
		(
			#array
			(
				expr :	((int*)$c.data)[$i],
				size :	$c.rows * $c.cols
			)
		)
		#case 5
		(
			#array
			(
				expr :	((float*)$c.data)[$i],
				size :	$c.rows * $c.cols
			)
		)
		#case 6
		(
			#array
			(
				expr :	((double*)$c.data)[$i],
				size :	$c.rows * $c.cols
			)
		)
	)
	preview
	(
		#switch($c.flags & 0x7)
		#case 0
		(
			#(
				"[",
				$c.rows,
				",",
				$c.cols,
				"](",
				#array
				(
					expr :	((unsigned char*)$c.data)[$i],
					size :	$c.rows * $c.cols
				),
				")"
			)
		)
		#case 1
		(
			#(
				"[",
				$c.rows,
				",",
				$c.cols,
				"](",
				#array
				(
					expr :	((char*)$c.data)[$i],
					size :	$c.rows * $c.cols
				),
				")"
			)
		)
		#case 2
		(
			#(
				"[",
				$c.rows,
				",",
				$c.cols,
				"](",
				#array
				(
					expr :	((unsigned short*)$c.data)[$i],
					size :	$c.rows * $c.cols
				),
				")"
			)
		)
		#case 3
		(
			#(
				"[",
				$c.rows,
				",",
				$c.cols,
				"](",
				#array
				(
					expr :	((short*)$c.data)[$i],
					size :	$c.rows * $c.cols
				),
				")"
			)
		)
		#case 4
		(
			#(
				"[",
				$c.rows,
				",",
				$c.cols,
				"](",
				#array
				(
					expr :	((int*)$c.data)[$i],
					size :	$c.rows * $c.cols
				),
				")"
			)
		)
		#case 5
		(
			#(
				"[",
				$c.rows,
				",",
				$c.cols,
				"](",
				#array
				(
					expr :	((float*)$c.data)[$i],
					size :	$c.rows * $c.cols
				),
				")"
			)
		)
		#case 6
		(
			#(
				"[",
				$c.rows,
				",",
				$c.cols,
				"](",
				#array
				(
					expr :	((double*)$c.data)[$i],
					size :	$c.rows * $c.cols
				),
				")"
			)
		)
	)
}

最後に変更内容を保存して終了です.デバッグウィンドウに vector や CvMat, cv::Mat の要素が一目で分かるように表示されるようになり,要素の修正や確認が容易になったことと思います.
ベクトルは第1要素から順に表示し,行列は第1行目の各要素を表示し,次に第2行目の各要素を表示といったように行優先で表示し,IplImage も同様に横方向優先で表示するように設定しています(画素値の参照、操作の第1要素から順に表示しているだけです.アライメントも考慮しています).
#array の rank 機能を使わなかったのは,この機能がC言語のメモリレイアウトに一致せず直感性に欠けるからです.
Vista などで UAC を有効にしている場合,通常権限で起動したエディタでは autoexp.dat の場所によっては保存に失敗してしまうので,そのような場合には一度 autoexp.dat をデスクトップなどの適当な場所にコピーしてから編集し,再度元の場所に移動し直すか,エディタ自体を管理者権限で起動してから修正するようにすれば大丈夫です.
また同様にして vector や matrix, CvMat, Vec, Mat 以外の構造体(例えば ublas::symmetric_matrix や ublas.diagonal_matrix)についても好きなように内容を表示させることができるので,試してみるとよいでしょう.

最後に本記事の変更を適用前の画像と適用後の画像を示します.
左が適用前,右が適用後です.

要素が一目でわかるようになったことが確認できます.