Привет Хабровчанам! На Хабре уже обсуждался вопрос интеграции .Net c Matlab'ом. Цель же этой статьи — показать, как можно быстро и удобно решить обратную задачу: вызывать управляемый код из произвольных .Net библиотек в Matlab.
Зачем это нужно?
Несмотря на богатый набор алгоритмов в функционале Matlab'а, основным сценарием, в котором это может понадобиться, является необходимость задействовать в вычислениях уже имеющиеся и обладающие известными показателями качества .Net библиотеки, в которых реализованы математические алгоритмы.
Disclaimer
Пример, который рассматривается в статье, описывает типичный набор случаев возникающих при интеграции, достаточный для проведения широкого класса вычислений, однако, не охватывает всех возможностей интеграции с .Net, которые присутствуют в Matlab.
Код из данной статьи был сделан и протестирован на Windows платформе и в конкретной версии Matlab 2013a. В качестве версии .Net Framework использовалась 4.5, IDE — VS 2012.
Создание объектов .Net в Matlab
В качестве простого примера рассмотрим создание объекта стандартного класса DateTime
из .Net.
Получение текущей даты-времени из .Net может быть записано следующим Matlab кодом
dateTimeNow = System.DateTime.Now
Сразу же обратим внимание, что это полный код. От нас не требуется явного подключения каких-либо системных библиотек .Net, и CLR переменная dateTimeNow автоматически становится переменной Matlab'a. Если же данный вызов завершился с ошибкой, можно проверить булевский результат команды, которая проверяет поддержку .Net в среде.
isnetsupported = NET.isNETSupported
В качестве примера вызова метода CLR объекта, прибавим к текущей дате 10 минут, используя привычный дотнетчикам метод AddMinutes
dateTimeNow = dateTimeNow.AddMinutes(10)
В результате запуска данных команд в выводе отображается содержимое полученных объектов
Date: [1x1 System.DateTime]
Day: 21
DayOfWeek: [1x1 System.DayOfWeek]
DayOfYear: 111
Hour: 13
Kind: [1x1 System.DateTimeKind]
Millisecond: 160
Minute: 49
Month: 4
Now: [1x1 System.DateTime]
UtcNow: [1x1 System.DateTime]
Second: 56
Ticks: 635021489961600559
TimeOfDay: [1x1 System.TimeSpan]
Today: [1x1 System.DateTime]
Year: 2013
MinValue: [1x1 System.DateTime]
MaxValue: [1x1 System.DateTime]
Готовим DLL
Прежде чем научиться загружать произвольную сборку в Matlab, займемся ее подготовкой.
В качестве простого примера реализуем в целевой .Net библиотеке c именем Algorithms.dll
алгоритм поиска левого верхнего угла ограничивающего прямоугольника бинарного изображения.
Это можно сделать с помощью такого кода на C#:
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Algorithms
{
public class ImageProcessor
{
public ImageProcessor() {}
/// <summary>
/// Возвращает в массиве coordinates координаты левого верхнего угла бинарной картинки
/// на изображении. Если их нет - null
/// </summary>
/// <param name="bitmap">Входное бинарное изображение</param>
/// <param name="coordinates">Координаты левого верхнего угла</param>
public void GetLeftUpperCornerBB(Bitmap bitmap, out int[] coordinates)
{
coordinates = null;
var bitmapData = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadOnly,
PixelFormat.Format32bppRgb);
unsafe
{
//получаем указатель на память изображения
uint* p = (uint*) bitmapData.Scan0.ToPointer();
//пиксели записаны по строкам
for (int i = 0; i < bitmap.Height*bitmap.Width; i++)
{
if( (*p & 0xFFFFFF) == 0) //нашли первый черный пиксель
{
coordinates = new int[] { i / bitmap.Width , i % bitmap.Width };
break;
}
p++;
}
}
bitmap.UnlockBits(bitmapData);
}
}
}
Данный код является специфическим: мы будем рассматривать вызов нестатических public методов из Matlab, которые возвращаются значения с помощью ключевого слова out
. Метод GetLeftUpperCornerBB
принимает на вход объект класса Bitmap
, в котором содержится бинарное изображение, и возвращает в массиве coordinates
координаты первого левого верхнего черного пикселя (а если такого на изображении нет, например, в случае пустого изображения, то возвращается null
).
Загрузка custom'ных .Net библиотек в Matlab
В директории Matlab проекта создадим новый Matlab-файл Example.m
, рядом с которым поместим полученную на предыдущем шаге библиотеку Algorithms.dll
(см. скриншот ниже)
С помощью вызова функции addAssembly
загрузим сборку в Matlab.
netAssembly = NET.addAssembly('D:WorkMatlabNetIntegrationExampleAlgorithms.dll')
просмотрим список классов загруженной сборки
netAssembly.Classes
результатом будет
ans =
'Algorithms.ImageProcessor'
с помощью команды
import Algorithms.*
включаем namespace Algorithms.
Создаем объект класса ImageProcessor
и загружаем входное изображение в переменную bitmap
imageProcessor = ImageProcessor();
bitmap = System.Drawing.Bitmap('picture.bmp')
файл picture.bmp
при этом располагается в текущей рабочей директории.
Команда
methods (imageProcessor)
покажет нам список доступных методов данного объекта
Methods for class Algorithms.ImageProcessor:
Equals delete le
GetHashCode eq lt
GetLeftUpperCornerBB findobj ne
GetType findprop notify
ImageProcessor ge
ToString gt
addlistener isvalid
Теперь, запускаем целевой метод GetLeftUpperCornerBB
и получаем результат
coords = imageProcessor.GetLeftUpperCornerBB(bitmap);
Если бы у нас было несколько out
параметров (предположим, целых три массива с координатами), то мы бы написали такой код для их получения
[coords, cords2, cords3] = imageProcessor.GetLeftUpperCornerBB(bitmap);
Отметим, что результат является CLR объектом типа System.Int32[]
, поэтому для возможного удобства работы с ним существует возможность конвертировать данный массив в native массив Matlab'a. Например:
arrayOfDoubles = coords.double;
arrayOfIntegers = coords.int32;
Обратная конвертация возможна с помощью функции NET.convertArray
.
Итого, получаем следующий листинг:
netAssembly = NET.addAssembly('D:WorkMatlabNetIntegrationExampleAlgorithms.dll');
netAssembly.Classes;
import Algorithms.*;
imageProcessor = ImageProcessor();
bitmap = System.Drawing.Bitmap('picture.bmp');
methods (imageProcessor);
coords = imageProcessor.GetLeftUpperCornerBB(bitmap);
arrayOfIntegers = coords.int32;
Заключение
Мы решили задачу создания .Net объекта в Matlab, запуска методов .Net классов и получения результатов.
Отдельно стоит заметить, что согласно документации Matlab выгрузка загруженных .Net модулей не предусмотрена явно. Поэтому, для замены DLL файла потребуется как минимум перезагрузка IDE Matlab. В остальном, в версии 2013a представлена достаточно полная поддержки интеграции .Net в смысле возможностей работы с различными элементами CLR и их атрибутами.
Используемые материалы
Автор: MrEsp