Экспорт в JPEG
JPEG – это сжатые графические файлы. Они поддерживают 24-битовый цвет, и их часто выбирают для использования в Интернет, так как размер JPEG-файла обычно меньше, чем у аналогичных файлов других форматов изображений. Но алгоритм сжатия JPEG выполняется с потерями и не рекомендуется для многих картографических изображений, так как линейные рисунки, текст и значки приобретают признаки сжатия. Так что PNG для картографических изображений подходит лучше всего. Файлы JPEG могут быть сгенерированы с сопроводительным файлом привязки для использования в качестве пространственно привязанных растровых данных.
Чтобы экспортировать файл JPEG, убедитесь, что вид карты или вид компоновки активен. На вкладке Общий доступ щелкните Экспорт Карты или Экспорт Компоновки
, в зависимости от активного вида, чтобы открыть панель Экспорт . Измените параметр Тип файла на JPEG . Задайте имя и расположение для файла, а также любые другие свойства, затем нажмите кнопку Экспорт .
Примечание:
Некоторые опции доступны только при экспорте карты или компоновки. При экспорте карты вы можете установить размер экспортируемой карты. При экспорте компоновки вы можете вырезать по экстенту графики.
При экспорте в файл JPEG доступны следующие свойства:
Степень сжатия изображения, применяемая к экспортируемому файлу. При низком качестве создается файл меньшего размера, но с менее четкими данными, при максимальном качестве создается файл большего размера, но с более четкими данными.
Разрешение экспортированного файла в точках на дюйм (dpi). DPI доступно только при экспорте компоновки. При экспорте карты DPI вычисляется автоматически. Для экспорта карты увеличьте значения Ширины и Высоты , чтобы добавить больше пикселей и получить выходные данные с более высоким разрешением.
Число битов для описания цвета в пикселе. Выберите из следующего:
- 8-bit Grayscale – 256 оттенков серого. Все цвета конвертируются в шкалу оттенков серого.
- 24-bit True Color – возможно 16 777 216 цветов. Этот вариант хорош для максимальной точности цветопередачи.
Встроить цветовой профиль
Включить цветовой профиль, установленный для карты или компоновки, в экспортируемый файл. Это помогает добиться согласованности цветов, когда файл просматривается на разных устройствах. Если профиль не является встроенным, или используется вьюер без управления цветом, цвета могут значительно отличаться от отображаемых на экране.
Опция доступна только при включенном управлении цветом. Если управление цветом не включено или выбрано цветовое пространство CMYK, пространство CMYK также входит в экспортируемые данные. Это может привести к изменению некоторых цветов при просмотре экспортированного файла.
Связанные разделы
- Введение в экспорт карты или компоновки
- Работа с предварительными настройками экспорта
- Печать карты или компоновки
Сохранение изображения с помощью cv2.imwrite() в OpenCV Python
Чтобы сохранить изображение в локальное хранилище с помощью Python, используйте функцию cv2.imwrite() в библиотеке OpenCV.
Синтаксис
cv2.imwrite(path, image)
Где, path – это полный путь к выходному файлу, в который вы хотите записать массив numpy изображений.
cv2.imwrite() возвращает логическое значение. true, если изображение успешно записано, и false, если изображение не было успешно записано по указанному локальному пути.
Пример 1
В этом примере мы прочитаем изображение, преобразуем его, а затем сохраним изображение в постоянном файловом хранилище с помощью метода imwrite().
import cv2 #read image as grey scale img = cv2.imread('D:/image-1.png') #do some transformations on img #save matrix/array as image file isWritten = cv2.imwrite('D:/image-2.png', img) if isWritten: print('Image is successfully saved as file.')
Image is successfully saved as file.
Пример 2: со случайными значениями
В этом примере мы напишем массив numpy как изображение, используя функцию cv2.imwrite(). Для этого мы создадим массив с тремя каналами для красного, зеленого и синего, содержащий случайные значения. В общих случаях мы читаем изображение, применяем некоторые преобразования к массиву и затем записываем изображение в локальное хранилище. Но в этом примере мы будем придерживаться массива со случайными значениями.
import cv2 import numpy as np img = np.random.randint(255, size=(300, 600, 3)) isWritten = cv2.imwrite('D:/image-2.png', img) if isWritten: print('The image is successfully saved.')
The image is successfully saved.
Ниже приведено изображение, созданное со случайными значениями.

В этом руководстве на примерах Python мы узнали, как использовать cv2.imwrite() для сохранения массива numpy в виде изображения.
Как подружить PyTorch и C++. Используем TorchScript
Около года назад разработчики PyTorch представили сообществу TorchScript — инструмент, который позволяет с помощью пары строк кода и нескольких щелчков мыши сделать из пайплайна на питоне отчуждаемое решение, которое можно встроить в систему на C++. Ниже я делюсь опытом его использования и постараюсь описать встречающиеся на этом пути подводные камни. Особенное внимание уделю реализации проекта на Windows, поскольку, хотя исследования в ML обычно делаются на Ubuntu, конечное решение часто (внезапно!) требуется под «окошками».
Примеры кода для экспорта модели и проекта на C++, использующего модель, можно найти в репозиториии на GitHub.

Разработчики PyTorch не обманули. Новый инструмент действительно позволяет превратить исследовательский проект на PyTorch в код, встраиваемый в систему на С++, за пару рабочих дней, а при некотором навыке и быстрее.
TorchScript появился в PyTorch версии 1.0 и продолжает развиваться и меняться. Если первая версия годичной давности была полна багов и являлась скорее экспериментальной, то актуальная на данный момент версия 1.3 как минимум по второму пункту заметно отличается: экспериментальной ее уже не назовешь, она вполне пригодна для практического использования. Я буду ориентироваться на нее.
В основе TorchScript лежит собственный автономный (не требующий наличия Python) компилятор питон-подобного языка, а также средства для конвертации в него программы, написанной на Python и PyTorch, методы сохранения и загрузки получившихся модулей и библиотека для их использования в C++. Для работы придется добавить в проект несколько DLL общим весом около 70MB (для Windows) для работы на CPU и 300MB для GPU версии. TorchScript поддерживает большинство функций PyTorch и основные возможности языка python. А вот о сторонних библиотеках, таких как OpenCV или NumPy, придется забыть. К счастью, у многих функций из NumPy есть аналог в PyTorch.
Конвертируем пайплайн на PyTorch модель на TorchScript
TorchScript предлагает два способа преобразования кода на Python в его внутренний формат: tracing и scripting (трассировка и скриптование). Зачем два? Нет, понятно, конечно, что два лучше чем один.

Но в случае с этими методами получается как в известном афоризме про левый и правый уклон: оба хуже. Что ж, мир несовершенен. Просто в конкретной ситуации надо выбирать тот, который больше подходит.
Метод трассировки очень прост. Берется некий образец данных (обычно инициализированный случайными числами), отправляется в интересующую нас функцию или метод класса, и PyTorch строит и запоминает граф вычислений примерно так же, как делает это обычно при обучении нейросети. Вуаля — скрипт готов:
import torch import torchvision model = torchvision.models.resnet34(pretrained = True) model.eval() sample = torch.rand(1, 3, 224, 224) scripted_model = torch.jit.trace(model, sample)
В примере выше получается объект класса ScriptModule. Его можно сохранить
scripted_model.save('my_script.pth')
и загрузить потом в программу на C++ (об этом ниже) или в код на Python вместо исходного объекта:
Пример кода на Python, использующего сохраненную модель
import cv2 from torchvision.transforms import Compose, ToTensor, Normalize transforms = Compose([ToTensor(), Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])]) img = cv2.resize(cv2.imread('pics/cat.jpg'), (224,224)) img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) x = transforms(img).unsqueeze(0) # add batch dimension scripted_model = torch.jit.load('my_script.pth') y = scripted_model(x) print(y[0].argmax(), y[0][y[0].argmax()])
tensor(282) tensor(12.8130, grad_fn=)
Получающийся объект ScriptModule может выступать везде, где обычно используется nn.Module .
Описанным способом можно трассировать экземпляры класса nn.Module и функции (в последнем случае получается экземпляр класса torch._C.Function ).
Этот метод (tracing) имеет важное преимущество: так можно конвертировать почти любой питоновский код, не использующий внешних библиотек. Но есть и не менее важный недостаток: при любых ветвлениях будет запомнена только та ветка, которая исполнялась на тестовых данных:
def my_abs(x): if x.max() >= 0: return x else: return -x my_abs_traced = torch.jit.trace(my_abs, torch.tensor(0)) print(my_abs_traced(torch.tensor(1)), my_abs_traced(torch.tensor(-1)))
c:\miniconda3\lib\site-packages\ipykernel_launcher.py:2: TracerWarning: Converting a tensor to a Python boolean might cause the trace to be incorrect. We can't record the data flow of Python values, so this value will be treated as a constant in the future. This means that the trace might not generalize to other inputs! tensor(1) tensor(-1)
Упс! Кажется, это не то, что мы хотели бы, правда? Хорошо, что по этому поводу хотя бы выдаётся предупреждающее сообщение (TracerWarning). Относиться к таким сообщениям стоит внимательно.
Тут нам на помощь приходит второй метод — scripting:
my_abs_script = torch.jit.script(my_abs) print(my_abs_script(torch.tensor(1)), my_abs_script(torch.tensor(-1)))
tensor(1) tensor(1)
Ура, ожидаемый результат получен! Scripting рекурсивно анализирует код на Python и преобразует в код на собственном языке. На выходе получаем тоже класс ScriptModule (для модулей) или torch._C.Function (для функций). Казалось бы, вот оно, счастье! Но возникает другая проблема: внутренний язык TorchScript строго типизированный, в отличие от Python. Тип каждой переменной определяется первым присваиванием, тип аргументов функции по умолчанию — Tensor . Поэтому, например, привычный шаблон
def my_func(x): y = None if x.max() > 0: y = x return y my_func = torch.jit.script(my_func)
оттрассировать не удастся.
Ошибка трассировки выглядит так
RuntimeError Traceback (most recent call last) in () ----> 1 my_func = torch.jit.script(my_func) d:\programming\3rd_party\pytorch\pytorch_ovod_1.3.0a0_de394b6\torch\jit\__init__.py in script(obj, optimize, _frames_up, _rcb) 1224 if _rcb is None: 1225 _rcb = _gen_rcb(obj, _frames_up) -> 1226 fn = torch._C._jit_script_compile(qualified_name, ast, _rcb, get_default_args(obj)) 1227 # Forward docstrings 1228 fn.__doc__ = obj.__doc__ RuntimeError: Variable 'y' previously has type None but is now being assigned to a value of type Tensor : at :4:8 def my_func(x): y = None if x.max() > 0: y = x ~
Примечательно, что, хотя ошибка возникает при вызове torch.jit.script , указывается и вызвавшее ее место в скриптуемом коде.
Даже точки после констант начинают играть роль:
def my_func(x): if x.max() > 0: y = 1.25 else: y = 0 return y my_func = torch.jit.script(my_func)
выдаст ошибку
RuntimeError Traceback (most recent call last) in () 5 y = 0 6 return y ----> 7 my_func = torch.jit.script(my_func) d:\programming\3rd_party\pytorch\pytorch_ovod_1.3.0a0_de394b6\torch\jit\__init__.py in script(obj, optimize, _frames_up, _rcb) 1224 if _rcb is None: 1225 _rcb = _gen_rcb(obj, _frames_up) -> 1226 fn = torch._C._jit_script_compile(qualified_name, ast, _rcb, get_default_args(obj)) 1227 # Forward docstrings 1228 fn.__doc__ = obj.__doc__ d:\programming\3rd_party\pytorch\pytorch_ovod_1.3.0a0_de394b6\torch\jit\__init__.py in _rcb(name) 1240 # closure rcb fails 1241 result = closure_rcb(name) -> 1242 if result: 1243 return result 1244 return stack_rcb(name) RuntimeError: bool value of Tensor with more than one value is ambiguous
Потому что надо писать не 0 , а 0. , чтобы тип в обеих ветках был одинаковым! Избаловались, понимаешь, со своим питоном!
Это только начало списка тех изменений, которые требуется внести в код на python, чтобы его можно было успешно превратить в модуль TorchScript. Более подробно самые типичные случаи перечислю чуть позже. В принципе, никакой rocket science тут нет и свой код вполне можно поправить соответствующим образом. А вот исправлять сторонние модули, включая стандартные из torchvision , чаще всего править не хочется, а "как есть" для скриптования они обычно не пригодны.
К счастью, обе технологии можно совмещать: то, что скриптуется — скриптовать, а что не скриптуется — трассировать:
class MyModule(torch.nn.Module): def __init__(self): super(MyModule, self).__init__() self.resnet = torchvision.models.resnet34(pretrained = True) # без следующих двух строк попытка сделать torch.jit.script(my_module) # ниже выдаст ошибку где-то в недрах resnet34. # Поэтому заблаговременно сами заменим self.resnet на ScriptModule. self.resnet.eval() # NB: это надо сделать до трассировки! После - не сработает! self.resnet = torch.jit.trace(self.resnet, torch.rand((1,3,224,224), dtype=torch.float)) def forward(self, x): if x.shape[2] < 224 or x.shape[3] < 224: return torch.tensor(0) else: return self.resnet(x) my_module = MyModule() my_module = torch.jit.script(my_module)
В примере выше трассировка используется, чтобы включить модуль, не поддающийся скриптованию, в модуль, где не достаточно трассировки и необходимо скриптование. Бывает и обратная ситуация. Например, если нам надо выгрузить модель в ONNX, при этом используется трассировка. Но трассируемая модель может включать функции на TorchScript, поэтому логику, требующую ветвлений и циклов, можно реализовать там! Пример приведен в официальной документации по torch.onnx.
Более подробно возможности, предоставляемые PyTorch для создания модулей на TorchScript описаны в официальной документации и руководстве по torch.jit . В частности, я не упомянул об удобном способе использования torch.jit.trace и torch.jit.script в виде декораторов, об особенностях отладки скриптованного кода. Это и многое другое есть в документации.
Включаем модель в проект на C++
К сожалению, официальная документация ограничивается примерами вида "сложить 2 тензора, сгенерированных с помощью torch.ones ". Я подготовил пример более приближенного к реальности проекта, отправляющего в нейросеть картинку из OpenCV и получающего обратно результаты в виде тензора откликов, кортежа переменных, картинки с результатами сегментации.
Для работы примера потребуются сохраненные скрипты классификации c помощью ResNet34 и сегментации с помощью DeepLabV3. Для подготовки этих скриптов надо запустить этот jupyter блокнот.
Нам потребуется библиотека torchlib . Получить ее можно несколькими путями:
- Если у вас уже стоит PyTorch, поставленный с помощью pip install , то ее можно найти в каталоге Python: \Lib\site-packages\torch ;
- Если у вас PyTorch собран из исходников, то она там: \build\lib.win-amd64-3.6\torch ;
- Наконец, можно скачать с pytorch.org отдельно библиотеку, выбрав Language = C++, и распаковать архив.
Код на C++ достаточно прост. Надо:
-
Включить заголовочный файл
#include
torch::jit::script::Module module = torch::jit::load("../resnet34_infer.pth");
torch::Tensor tensor = torch::from_blob(img.data, < img.rows, img.cols, 3 >, torch::kByte);
auto output = module.forward( < tensor >)
float* data = static_cast(output.toTensor().data_ptr());
torch::NoGradGuard no_grad;
Как собрать проект с помощью CMake, описано в официальном руководстве. А вот тема проекта на Visual Studio там не раскрыта, поэтому опишу это подробнее. Придется вручную подкрутить настройки проекта:
- Я тестировал на Visual Studio 2017. Про другие версии сказать не могу.
- Должна быть установлена v14.11 тулсета v141 (галочка "VC++ 2017 version 15.4 v14.11 toolset" в инсталляторе VS).
- Платформа должна быть x64 .
- В General → Platform Toolset выбрать v141(Visual Studio 2017)
- В C/C++ → General → Additional Include Directories добавить \include
- В Linker → General → Additional Library Directories добавить \lib
- В Linker → Input → Additional Dependencies добавить torch.lib; c10.lib . В интернетах пишут, что еще может потребоваться caffe2.lib , а для GPU и еще что-нибудь из \lib , но в текущей версии мне хватало добавления этих двух библиотек. Возможно, это устаревшая информация.
- Пишут также, что надо ставить C/C++ → Language → Conformance Mode = No , но я не увидел разницы.
Также в проекте НЕ должна быть объявлена переменная __cplusplus . Попытка добавить опцию компилятора /Zc:__cplusplus приведет к ошибкам при компиляции в файле ivalue.h .
В прилагаемом проекте настройки путей (не только к TorchLib, но и к OpenCV и CUDA) вынесены в props файл, перед сборкой надо прописать их там в соответствии с вашей локальной конфигурацией. Вот, собственно, и все.
Что ещё следует иметь в виду
Если описанный процесс показался вам слишком простым, интуиция вас не обманула. Есть целый ряд нюансов, которые надо учитывать, чтобы преобразовать модель на PyTorch, написанную на Python, в TorchScript. Перечислю ниже те, с которыми приходилось сталкиваться. Некоторые я уже упоминал, но повторюсь, чтобы собрать все в одном месте.

- Типом переменных, передаваемых в функцию, по умолчанию считается Tensor. Если в каких-то (весьма частых) случаях это окажется неприемлемым, придется объявить типы вручную, используя MyPy-style type annotations, примерно так:
def calc_letter_statistics(self, cls_preds: List[Tensor], cls_thresh: float)->Tuple[int, Tuple[Tensor, Tensor, Tensor]]
def calc_letter_statistics(self, cls_preds, cls_thresh): # type: (List[Tensor], float)->Tuple[int, Tuple[Tensor, Tensor, Tensor]]
- Переменные строго типизированы и тип, если не указан явно, определяется первым присваиванием. Привычные конструкции вида x=[]; for . x.append(y) придется отредактировать, т.к. в момент присваивания [] компилятор не может понять, какой тип будет в списке. Поэтому придется указать тип явно, например:
from typing import List x: List[float] = []
или (другое "например")
from torch import Tensor from typing import Dict, Tuple, List x: Dict[int: Tuple[float, List[Tensor], List[List[int]]]] = <>
- В примере выше надо импортировать именно имена, поскольку эти имена зашиты в код TorchScript. Альтернативный, казалось бы, законный, подход
import torch import typing x: typing.List[torch.Tensor] = []
приведет при скриптовании к ошибке Unknown type constructor typing.List
- Еще одна привычная конструкция, с которой придется расстаться:
x = None if smth: x = torch.tensor([1,2,3])
Тут есть два варианта. Или оба раза присваивать Tensor (то, что он разной размерности, не страшно):
x = torch.tensor(0) if smth: x = torch.tensor([1,2,3])
и не забыть поискать, что сломается после такой замены. Или попытаться честно написать:
x: Optional[Tensor] = None if smth: x = torch.tensor([1,2,3])
но тогда при дальнейшем использовании x там, где ожидается тензор, мы, скорее всего, получим ошибку: Expected a value of type 'Tensor' for argument 'x' but instead found type 'Optional[Tensor]'.
- Не забываем при первом присваивании писать, например, x=0. вместо привычного x=0 и т.п., если переменная x должна иметь тип float .
- Если где-то использовалась старомодная инициализация тензора через x = torch.Tensor(. ) , с ней придется расстаться и заменить на более молодежный вариант с маленькой буквы x = torch.tensor(. ) . Иначе при скриптовании прилетит: Unknown builtin op: aten::Tensor. Here are some suggestions: aten::tensor. Вроде бы, даже объясняют, в чем проблема, и понятно, что надо делать. Впрочем, понятно, если уже знаешь правильный ответ.
- Код скриптуется в контексте того модуля, где вызван torch.jit.script . Поэтому если где-то в недрах скриптуемого класса или функции используется, например, math.pow , придется в компилирующий модуль добавить import math . А лучше скриптовать класс там же, где он объявлен: или с помощью декоратора @torch.jit.script , или объявив рядом с ним дополнительную функцию, делающую из него ScriptModule. Иначе получим сообщение об ошибке undefined value math при попытке скомпилировать класс из модуля, в котором, казалось бы, сделан импорт math .
- Если где-то у вас есть конструкция вида my_tensor[my_tensor < 10] = 0 или подобная, то при скриптовании вы получите загадочную ошибку:
*aten::index_put_(Tensor(a!) self, Tensor?[] indices, Tensor values, bool accumulate=False) -> (Tensor(a!)):* *Expected a value of type 'Tensor' for argument 'values' but instead found type 'int'.* *aten::index_put_(Tensor(a!) self, Tensor[] indices, Tensor values, bool accumulate=False) -> (Tensor(a!)):* *Expected a value of type 'List[Tensor]' for argument 'indices' but instead found type 'List[Optional[Tensor]]'.*
cls_thresh = 0.3 class MyModule(torch.nn.Module): . x = r < cls_thresh .
приведет при скриптовании к ошибке вида python value of type 'float' cannot be used as a value. Надо сделать переменную атрибутом в конструкторе:
cls_thresh = 0.3 class MyModule(torch.nn.Module): def __init__(self): . self.cls_thresh = cls_thresh . x = r < self.cls_thresh .
- Еще одна тонкость возникает, если атрибут класса используется в качестве параметра среза:
class FPN(nn.Module): def __init__(self, block, num_blocks, num_layers =5): . self.num_layers = num_layers def forward(self, x): . return (p3, p4, p5, p6, p7)[:self.num_layers]
приводит при скриптовании к ошибке tuple slice indices must be integer constants. Надо указать, что атрибут num_layers — константа и меняться не будет:
class FPN(nn.Module): num_layers: torch.jit.Final[int] def __init__(self, block, num_blocks, num_layers =5): .
- В некоторых случаях там, где раньше нормально подходил тензор, требуется в явном виде передать число:
xx1 = x1.clamp(min=x1[i])
выдает при скриптовании ошибку Expected a value of type 'Optional[number]' for argument 'min' but instead found type 'Tensor'. . Ну, тут из сообщения об ошибке понятно что делать:
xx1 = x1.clamp(min=x1[i].item())
Перечисленные выше проблемы возникают при трассировке. Именно из-за них просто скомпилировать готовые решения в TorchScript обычно не получается, и приходится или долго заниматься массажом исходного кода (если исходный код уместно редактировать), или использовать трассировку. Но и в трассировке есть свои нюансы:
- В трассировке не работают конструкции вида
tensor_a.to(tensor_b.device)
Устройство, на которое загружается тензор, фиксируется в момент трассировки и в процессе выполнения не меняется. Частично справиться с этой проблемой можно, если объявить тензор членом nn.Module с типом Parameter . Тогда при загрузке модели он загрузится на то устройство, которое указано в функции torch.jit.load .
Эпилог
Все перечисленное, конечно, создает проблемы. Но TorchScript позволяет объединить и отправить в решение как единое целое собственно модель и питоновский код, обеспечивающий пред- и постобработку. Да и время на подготовку решения к компиляции, даже несмотря на перечисленные трудности, несравнимо меньше, чем затраты на создание решения, а здесь PyTorch дает большие преимущества, так что игра стоит свеч.
Saved searches
Use saved searches to filter your results more quickly
Cancel Create saved search
You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session. You switched accounts on another tab or window. Reload to refresh your session.
pytorch / vision Public
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Reading PNG/JPG images into a torch::tensor and saving a torch::tensor to PNG/JPG in C++ without OpenCV #2691
ghost opened this issue Sep 20, 2020 · 7 comments · Fixed by #2696
Reading PNG/JPG images into a torch::tensor and saving a torch::tensor to PNG/JPG in C++ without OpenCV #2691
ghost opened this issue Sep 20, 2020 · 7 comments · Fixed by #2696
Comments
ghost commented Sep 20, 2020
Feature
After integrating Siv3D with Libtorch (https://github.com/QuantScientist/Siv3DTorch) I am now trying to read and write images from and to Siv3D in C++, not Python. The way it works is:
Motivation
In C++ I need to do the following:
- An image is read from disk (usually using OpenCV which is easy but I am trying to avoid)
- The image is converted to torch::tensor
- A DL model is applied on the tensor
- A tensor is returned from the model
- The tensor is converted to an image for display/saving purposes.
Pitch
Alternatives
For reference this is the OpenCV to Libtorch conversion utils which I use, I would like something very similiar:
at::Tensor matToTensor(cv::Mat frame, int h, int w, int c) < cv::cvtColor(frame, frame, CV_BGR2RGB); frame.convertTo(frame, CV_32FC3, 1.0f / 255.0f); auto input_tensor = torch::from_blob(frame.data, ); input_tensor = input_tensor.permute(); torch::DeviceType device_type = torch::kCPU; // if (torch::cuda::is_available()) < device_type = torch::kCUDA; // >input_tensor = input_tensor.to(device_type); return input_tensor; > cv::Mat tensorToOpenCv(at::Tensor out_tensor, int h, int w, int c) < out_tensor = out_tensor.squeeze().detach().permute(); out_tensor = out_tensor.mul(255).clamp(0, 255).to(torch::kU8); out_tensor = out_tensor.to(torch::kCPU); cv::Mat resultImg(h, w, CV_8UC3); // cv::Mat resultImg(h, w, CV_8UC1); std::memcpy((void *) resultImg.data, out_tensor.data_ptr(), sizeof(torch::kU8) * out_tensor.numel()); return resultImg; >
Additional context
The text was updated successfully, but these errors were encountered:
fmassa added help wanted module: io labels Sep 21, 2020
ghost commented Sep 21, 2020
The author provided two methods based on png++ that I am going to test.
#include #include #include torch::Tensor ConvertRGBintoTensor(png::image &image); png::image ConvertTensorintoRGB(torch::Tensor &tensor_); int main(void) < // Input PNG-image png::imageimageI("input.png"); // Convert png::image into torch::Tensor torch::Tensor tensor = ConvertRGBintoTensor(imageI); std::cout << "C:" << tensor.size(0) << " H:" << tensor.size(1) << " W:" << tensor.size(2) << std::endl; // Convert torch::Tensor into png::image png::imageimageO = ConvertTensorintoRGB(tensor); // Output PNG-image imageO.write("output.png"); return 0; > torch::Tensor ConvertRGBintoTensor(png::image &image) < size_t width = image.get_width(); size_t height = image.get_height(); unsigned char *pointer = new unsigned char[width * height * 3]; for (size_t j = 0; j < height; j++)< for (size_t i = 0; i < width; i++)< pointer[j * width * 3 + i * 3 + 0] = image[j][i].red; pointer[j * width * 3 + i * 3 + 1] = image[j][i].green; pointer[j * width * 3 + i * 3 + 2] = image[j][i].blue; >> torch::Tensor tensor = torch::from_blob(pointer, , torch::kUInt8).clone(); // copy tensor = tensor.permute(); // ===> delete[] pointer; return tensor; > png::image ConvertTensorintoRGB(torch::Tensor &tensor_)< torch::Tensor tensor = tensor_.permute(); // ===> size_t width = tensor.size(1); size_t height = tensor.size(0); unsigned char *pointer = tensor.data_ptr(); png::image image(width, height); for (size_t j = 0; j < height; j++)< for (size_t i = 0; i < width; i++)< image[j][i].red = pointer[j * width * 3 + i * 3 + 0]; image[j][i].green = pointer[j * width * 3 + i * 3 + 1]; image[j][i].blue = pointer[j * width * 3 + i * 3 + 2]; >> return image; >
