ExcelからOpenCVで画像を開く

はじめに

Excelにはプログラミングのできる環境としてVBAが用意されています。ただあまり使いやすいとはいえないので、別の方法があるか調べてみました。
COMというインターフェースを使うとVisual Studioで作成したDLLを呼べるみたいなので試してみます。

実装

ここではC++/CLIOpenCVで取得したカメラ画像をExcelで表示するプログラムを作ってみます。VBAから見えてほしいインターフェースは、画像の座標を入れるとRGBの値が返ってくるものです。これを実装すると以下のようになります。
参考
Extend your VBA code with C#, VB.Net or C++/CLI | Pragmateek

#pragma once
using namespace System::Runtime::InteropServices;

#include <opencv2/highgui/highgui.hpp>

[Guid("FCB93926-3C39-447E-81D5-A1FB109A6EF3")]
public interface class ICamera
{
  array<int>^ GetPixelColor(int r, int c);
};

[Guid("A1AFD711-99A3-42C2-B6CD-1A2B2B1B3CB8")]
[ClassInterface(ClassInterfaceType::None)]
public ref class Camera : ICamera
{
public:
  Camera();
  ~Camera();

  virtual array<int>^ GetPixelColor(int r, int c);
private:
  cv::VideoCapture *capture_;
  cv::Mat_<cv::Vec3b> *image_;
};

VBAから呼びたい関数はインターフェースクラスで宣言します。ここだと座標rとcを入れると輝度値の配列が返ってきます。
GUIDは他とかぶらない値でVisual Studioの  ツール->GUIDの作成 からコピペできます。cv::VideoCaptureとcv::Matをポインタにしているのは、C++/CLIがアンマネージドはポインタじゃないと動かないからです。これ結構つらい制約なんですよね。

#include "Camera.h"

#include <iostream>

#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>

Camera::Camera()
{
  capture_ = new cv::VideoCapture(CV_CAP_DSHOW);

  image_ = new cv::Mat_<cv::Vec3b>();
  *capture_ >> *image_;

  // 小さめにしてドット絵ぽく
  cv::resize(*image_, *image_, cv::Size(640 / 10, 480 / 10), 0, 0, cv::INTER_NEAREST);
}

Camera::~Camera()
{
  delete capture_;
  delete image_;
}

array<int>^ Camera::GetPixelColor(int r, int c)
{
  array<int>^ pixel = gcnew array<int>(3);
  pixel[0] = (*image_)(r, c)[0];
  pixel[1] = (*image_)(r, c)[1];
  pixel[2] = (*image_)(r, c)[2];

  return pixel;
}

作ったDLLはregasm.exeで登録してVBAから見えるようにします。Visual StudioのTool Commandから管理者権限で
RegAsm /codebase /tlb path-to-dll
を実行します。そうするとVBAツール->参照設定から登録したDLLが見えます。

Option Explicit

Sub Display()
    Dim camera As ExcelCamera2.camera
    Set camera = New ExcelCamera2.camera
    
    Dim dataSheet As Worksheet
    Set dataSheet = Sheets("Sheet1")
    
    Dim r As Long
    Dim c As Long
    For r = 1 To 480 / 10
        For c = 1 To 640 / 10
            Dim pixel As Variant
            pixel = camera.GetPixelColor(r - 1, c - 1)
            dataSheet.Cells(r, c).Interior.Color = RGB(pixel(2), pixel(1), pixel(0))
        Next
    Next
End Sub

F5キー押して実行するとこんな感じになります。めでたし。
f:id:wildpie:20141129185141p:plain
(続き書きました:パワポのスライド上でプログラムを動かす - wildpieの日記