Скачать можно здесь.
image processing
dlib 0.3
- Добавлены абстрактные потоки ввода/вывода (dlib.core.stream), независимые от Phobos, а также интерфейс файловой системы (dlib.filesystem) с готовыми реализациями для POSIX и Windows – этот интерфейс можно использовать, например, для построения виртуальных ФС.
- Добавлена начальная поддержка HDRI в dlib.image (реализация формата изображений с плавающей запятой в dlib.image.hdri). Кроме того, обеспечена поддержка распараллеливания обработки изображений (dlib.image.parallel), добавлена поддержка чтения форматов TGA и BMP. Чтение/запись графических форматов теперь основаны на потоках, поэтому имеется возможность загружать изображения, например, напрямую из архивов.
- Элементы матриц (dlib.math.matrix) теперь располагаются по столбцам, а не по строкам. Это серьезно нарушило обратную совместимость, но если вы не используете внутренние данные матриц и пользуетесь только внешним API, то это изменение не должно повлечь никаких проблем.
Более полный чейнджлог, а также исходники релиза вы можете найти на GitHub:
https://github.com/gecko0307/dlib/releases/tag/v0.3.0
Распараллеливание обработки изображений
API dlib.image позволяет создавать фильтры, которые легко распараллеливать на несколько процессоров. Изображение условно разбивается на несколько блоков заданного размера, которые затем обрабатываются фильтром через std.parallelism.
import std.parallelism;
import dlib.functional.range;
import dlib.image.image;
struct Block
{
uint x1, y1;
uint x2, y2;
}
alias Range!uint PixRange;
void parallelFilter(
SuperImage img,
void delegate(PixRange blockRow, PixRange blockCol) ffunc,
uint bw = 100,
uint bh = 100)
{
if (bw > img.width)
bw = img.width;
if (bh > img.height)
bh = img.height;
uint numBlocksX = img.width / bw + ((img.width % bw) > 0);
uint numBlocksY = img.height / bh + ((img.height % bh) > 0);
Block[] blocks = new Block[numBlocksX * numBlocksY];
foreach(x; 0..numBlocksX)
foreach(y; 0..numBlocksY)
{
uint bx = x * bw;
uint by = y * bh;
uint bw1 = bw;
uint bh1 = bh;
if ((img.width - bx) < bw)
bw1 = img.width - bx;
if ((img.height - by) < bh)
bh1 = img.height - by;
blocks[y * numBlocksX + x] = Block(bx, by, bx + bw1, by + bh1);
}
foreach(i, ref b; taskPool.parallel(blocks))
{
ffunc(range!uint(b.x1, b.x2),
range!uint(b.y1, b.y2));
}
}
Пример (закрашивание сплошным цветом):
SuperImage filterTestMultithreaded(SuperImage img)
{
auto res = img.dup;
img.parallelFilter((PixRange row, PixRange col)
{
foreach(x; row)
foreach(y; col)
{
res[x, y] = hsv(180.0f, 1.0f, 0.5f);
}
});
return res;
}
Для сравнения – однопоточный вариант:
SuperImage filterTestSinglethreaded(SuperImage img)
{
auto res = img.dup;
foreach(x; img.row)
foreach(y; img.col)
{
res[x, y] = hsv(180.0f, 1.0f, 0.5f);
}
return res;
}
На двухъядерном Intel Dual Core T2390 (1.86 ГГц) многопоточный вариант показывает прирост производительности на 70%.
Обновление dlib.image
В dlib.image появилась возможность отслеживать прогресс во время работы фильтров. Для этого используется многопоточность – необходимо создать класс-враппер, наследующий от FilteringThread. Прогресс (от 0 до 1) считывается из свойства progress для SuperImage. В данном примере показано, как использовать эту функциональность для вывода прогресса свертки в консоль:
import std.stdio;
import dlib.image.image;
import dlib.image.io.png;
import dlib.image.filters.convolution;
import dlib.image.fthread;
class ConvolutionThread: FilteringThread
{
float[] kernel;
this(SuperImage img, float[] k)
{
super(img);
kernel = k;
}
override void run()
{
output = image.convolve(kernel);
}
override void onRunning()
{
writef("Convolving %s%%", cast(uint)(image.progress * 100));
write("r");
stdout.flush();
}
override void onFinished()
{
writeln();
}
}
void main()
{
auto img = loadPNG("test.png");
img = (new ConvolutionThread(img, Kernel.Emboss)).filtered;
img.savePNG("output.png");
}