Привет Хабровчанам! На Хабре уже обсуждался вопрос интеграции .Net c Matlab'ом. Цель же этой статьи — показать, как можно быстро и удобно решить обратную задачу: вызывать управляемый код из произвольных .Net библиотек в Matlab.
Несмотря на богатый набор алгоритмов в функционале Matlab'а, основным сценарием, в котором это может понадобиться, является необходимость задействовать в вычислениях уже имеющиеся и обладающие известными показателями качества .Net библиотеки, в которых реализованы математические алгоритмы.
Пример, который рассматривается в статье, описывает типичный набор случаев возникающих при интеграции, достаточный для проведения широкого класса вычислений, однако, не охватывает всех возможностей интеграции с .Net, которые присутствуют в Matlab.
Код из данной статьи был сделан и протестирован на Windows платформе и в конкретной версии Matlab 2013a. В качестве версии .Net Framework использовалась 4.5, IDE — VS 2012.
В качестве простого примера рассмотрим создание объекта стандартного класса
Получение текущей даты-времени из .Net может быть записано следующим Matlab кодом
Сразу же обратим внимание, что это полный код. От нас не требуется явного подключения каких-либо системных библиотек .Net, и CLR переменная dateTimeNow автоматически становится переменной Matlab'a. Если же данный вызов завершился с ошибкой, можно проверить булевский результат команды, которая проверяет поддержку .Net в среде.
В качестве примера вызова метода CLR объекта, прибавим к текущей дате 10 минут, используя привычный дотнетчикам метод
В результате запуска данных команд в выводе отображается содержимое полученных объектов
Прежде чем научиться загружать произвольную сборку в Matlab, займемся ее подготовкой.
В качестве простого примера реализуем в целевой .Net библиотеке c именем
Это можно сделать с помощью такого кода на C#:
Данный код является специфическим: мы будем рассматривать вызов нестатических public методов из Matlab, которые возвращаются значения с помощью ключевого слова
В директории Matlab проекта создадим новый Matlab-файл

С помощью вызова функции
просмотрим список классов загруженной сборки
результатом будет
с помощью команды
включаем namespace Algorithms.
Создаем объект класса
файл
Команда
покажет нам список доступных методов данного объекта
Теперь, запускаем целевой метод
Если бы у нас было несколько
Отметим, что результат является CLR объектом типа
Обратная конвертация возможна с помощью функции
Итого, получаем следующий листинг:
Мы решили задачу создания .Net объекта в Matlab, запуска методов .Net классов и получения результатов.
Отдельно стоит заметить, что согласно документации Matlab выгрузка загруженных .Net модулей не предусмотрена явно. Поэтому, для замены DLL файла потребуется как минимум перезагрузка IDE Matlab. В остальном, в версии 2013a представлена достаточно полная поддержки интеграции .Net в смысле возможностей работы с различными элементами CLR и их атрибутами.
Зачем это нужно?
Несмотря на богатый набор алгоритмов в функционале 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 минут, используя привычный дотнетчикам метод
AddMinutesdateTimeNow = 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 координаты левого верхнего угла бинарной картинки /// на изображении. Если их нет - {-1,-1} /// </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(); coordinates = new int[2]{-1,-1}; //пиксели записаны по строкам for (int i = 0; i < bitmap.Height*bitmap.Width; i++) { if( (*p & 0xFFFFFF) == 0) //нашли первый черный пиксель { coordinates[0] = i / bitmap.Width; break; } p++; } p = (uint*) bitmapData.Scan0.ToPointer(); for (int i = 0; i < bitmap.Height*bitmap.Width; i++) { if( (p[( i % bitmap.Height) * bitmap.Width + ( i / bitmap.Height) ] & 0xFFFFFF) == 0) //нашли первый черный пиксель { coordinates[1] = i / bitmap.Height; break; } } } 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:\Work\MatlabNetIntegrationExample\Algorithms.dll')
просмотрим список классов загруженной сборки
netAssembly.Classes
результатом будет
ans = 'Algorithms.ImageProcessor'
с помощью команды
import Algorithms.*
включаем namespace Algorithms.
Создаем объект класса
ImageProcessor и загружаем входное изображение в переменную bitmapimageProcessor = 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:\Work\MatlabNetIntegrationExample\Algorithms.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 и их атрибутами.
