Что нового

Как конвертировать между форматом графического объекта и массивом цветов пикселей (в обе стороны)?

Oki

Продвинутый
Сообщения
452
Репутация
62
Привожу черновик скрипта, требующего доработки, в комментариях к которому изложены мысли насчёт того, что в нём пока что неправильно, а также какого рода требуется доработка. Собственные попытки разобраться в том, какие требуются дополнительные функции и в каком порядке, пока что не увенчались успехом. Вероятно, мне не хватило деталей в справке.
Код:
#include <GDIPlus.au3>
#include <Color.au3>
$sFileIn = FileOpenDialog("Select an image", "", "Image (*.jpg;*.png;*.bmp;*.gif;*.tif)") ; Предполагается работа с любым графическим форматом.
$sFileOut = FileSaveDialog("Save the result image", "", "All (*.*)") ; Предполагается сохранение в любом графическом формате.
$hTime = TimerInit()
_GDIPlus_Startup()
$hBitmapIn = _GDIPlus_ImageLoadFromFile($sFileIn)
$aDim = _GDIPlus_ImageGetDimension($hBitmapIn)
$iHResolution = _GDIPlus_ImageGetHorizontalResolution($hBitmapIn)
$iVResolution = _GDIPlus_ImageGetVerticalResolution($hBitmapIn)
Dim $aPixelsIn[$aDim[0]][$aDim[1]]
Dim $aPixelsOut[1][1]
For $iCoordX = 0 To $aDim[0] - 1
    For $iCoordY = 0 To $aDim[1] - 1
        $aPixelsIn[$iCoordX][$iCoordY] = _GDIPlus_BitmapGetPixel($hBitmapIn, $iCoordX, $iCoordY) ; Функция даёт некорректные данные. Вероятно, нужно сменить формат объекта.
    Next
Next
_Transform($aPixelsIn, $aPixelsOut)
$iHeightOut = UBound($aPixelsOut, 1)
$iWidthOut = UBound($aPixelsOut, 2)
$hBitmapOut = _GDIPlus_ImageResize($hBitmapIn, $iHeightOut, $iWidthOut) ; Явно не самая подходящая функция, ведь фактически на этом этапе нужен лишь пустой шаблон заданного размера.
For $iCoordX = 0 To $iHeightOut - 1
    For $iCoordY = 0 To $iWidthOut - 1
        _GDIPlus_BitmapSetPixel($hBitmapOut, $iCoordX, $iCoordY, $aPixelsOut[$iCoordX][$iCoordY]) ; Нет уверенности в том, что функция даст нужный результат. Вероятно, требуются преобразования до и/или после.
    Next
Next
_GDIPlus_BitmapSetResolution($hBitmapOut, 2 * $iHResolution, 3 * $iVResolution) ; Установка новой резолюции для примера.
_GDIPlus_ImageSaveToFile($hBitmapOut, $sFileOut)
_GDIPlus_BitmapDispose($hBitmapIn)
_GDIPlus_BitmapDispose($hBitmapOut)
_GDIPlus_Shutdown()
MsgBox(4096, "", "Script terminated in " & TimerDiff($hTime) & " milliseconds.")
Func _Transform(ByRef $aIn, ByRef $aOut) ; Функция обработки только для примера, чтобы было от чего оттолкнуться при попытках запустить скрипт.
    $iHeight = UBound($aIn, 1)
    $iWidth = UBound($aIn, 2)
    ReDim $aOut[$iHeight + 2][$iWidth + 2]
    For $iCoordX = 0 To $iHeight + 1
        For $iCoordY = 0 To $iWidth + 1
            If $iCoordX = 0 Or $iCoordX = $iHeight + 1 Or $iCoordY = 0 Or $iCoordY = $iWidth + 1 Then
                $aPixelsOut[$iCoordX][$iCoordY] = 0xFFFFFF
            Else
                $iColour = $aPixelsIn[$iCoordX - 1][$iCoordY - 1]
                $aRGB = _ColorGetRGB($iColour)
                For $iIndexRGB = 0 To 2
                    If $aRGB[$iIndexRGB] > 200 Then
                        $aRGB[$iIndexRGB] = 200
                    EndIf
                Next
                $aPixelsOut[$iCoordX][$iCoordY] = _ColorSetRGB($aRGB)
            EndIf
        Next
    Next
    Return(0)
EndFunc
 
Последнее редактирование:

InnI

AutoIT Гуру
Сообщения
4,912
Репутация
1,429
Если вы изучаете новую для себя тему, то проблемы лучше решать по частям.
Например, сначала разобраться, почему "Функция даёт некорректные данные"?
Функция работает правильно, просто у вас массив перевёрнут. Поэтому вы видите не то, что ожидаете.
Код:
Dim $aPixelsIn[$aDim[1]][$aDim[0]] ; то есть массив из H строк по W столбцов
For $iCoordH = 0 To $aDim[1] - 1
    For $iCoordW = 0 To $aDim[0] - 1
        $aPixelsIn[$iCoordH][$iCoordW] = _GDIPlus_BitmapGetPixel($hBitmapIn, $iCoordW, $iCoordH) ; для функции X - ширина, Y - высота
    Next
Next

Но функции _GDIPlus_BitmapGetPixel и _GDIPlus_BitmapSetPixel очень медленные. Поэтому, если хотите работать с пикселями напрямую, то лучше сразу изучать
Код:
_GDIPlus_BitmapLockBits() ; в английской справке хороший пример
 
Автор
Oki

Oki

Продвинутый
Сообщения
452
Репутация
62

InnI

Если бы проблема была лишь в транспонировании матрицы, то это легко бы определилось на уровне первичных тестов. Дело в ином: попытки получать цвет пикселей приводят к 32-битным числам, а не ограничиваются 24 битами, как полагается. Видимо, требуется какая-то конвертация. Судя по тому, что в примерах прибегают даже к конвертации между различными типами bitmap, обычный графический объект ещё скорее требует чего-то подобного.

Что касается попиксельного чтения и записи, мне тоже казалось, что это должно быть слишком долго, но в списке функций не нахожу чтения и записи целиком всего объекта.

Что касается функций, в названии которых присутствует упоминание bitmap в названии, мне казалось, что они не подходят для работы с любым графическим объектом произвольного формата, как это умеют некоторые функции. Если это так, то требуется конвертация. Конечно, можно сохранить графический объект в BMP-файл и прочитать его обратно в память в качестве bitmap. И наоборот, сохранять bitmap-объект в BMP-файл и читать его в обычный графический объект, когда требуется, но это тоже как-то нехорошо. Лучше конвертацию делать в оперативной памяти, если возможно.

Вероятно, мой неправильный скрипт вообще уводит в сторону от собственно вопроса, и не надо было этот скрипт даже приводить, а просто спросить, как из графического объекта получать цвета пикселей в массив, а графический объект создавать по массиву цветов пикселей.
 

InnI

AutoIT Гуру
Сообщения
4,912
Репутация
1,429
попытки получать цвет пикселей приводят к 32-битным числам, а не ограничиваются 24 битами, как полагается
Кем полагается? Формат пикселя определяется значением поля Format структуры $tagGDIPBITMAPDATA
Код:
_GDIPlus_ImageGetPixelFormat()

Но некоторые функции принудительно преобразуют в ARGB


конвертации между различными типами bitmap, обычный графический объект
Различные типы - это какие? Обычный графический объект - это что?


То, что вы называете "массив пикселей" - это непрерывный кусок памяти, расположенный по указателю, находящемуся в поле Scan0 структуры $tagGDIPBITMAPDATA. Следовательно
как из графического объекта получать цвета пикселей в массив
Код:
_GDIPlus_BitmapLockBits()

графический объект создавать по массиву цветов пикселей
Код:
_GDIPlus_BitmapCreateFromScan0() ; последний параметр - указатель на "массив пикселей"


Что касается функций, в названии которых присутствует упоминание bitmap в названии
Я тоже начинал изучение GDI+ по названиям функций, пока не понял, что это тупиковый путь. Начинать нужно с теории работы с GDI+. А ещё лучше - с принципов работы с изображениями в Windows. Тем более, если ваша цель
работы с любым графическим объектом произвольного формата
По секрету... Не существует произвольного формата. Все форматы определены. А GDI+ умеет работать лишь с ничтожным их количеством.
 
Последнее редактирование:
Автор
Oki

Oki

Продвинутый
Сообщения
452
Репутация
62
По секрету... Не существует произвольного формата. Все форматы определены. А GDI+ умеет работать лишь с ничтожным их количеством.
Это я понимаю, но всё же вот такой скрипт на практике успешно справляется с конвертацией между 5 форматами файлов, не требуя от меня копаться в многообразии представлений и упаковки пикселей (которых даже не 5, а намного более, чем это количество расширений).
Код:
#include <GDIPlus.au3>
$sFileIn = FileOpenDialog("Select an image", @ScriptDir, "All (*.*)|Image (*.jpg;*.png;*.bmp;*.gif;*.tif)")
$sFileOut = FileSaveDialog("Save the result image", @ScriptDir, "All (*.*)|Image (*.jpg;*.png;*.bmp;*.gif;*.tif)|JPG (*.jpg)|PNG (*.png)|BMP (*.bmp)|GIF (*.gif)|TIF (*.tif)")
_GDIPlus_Startup()
$hImage = _GDIPlus_ImageLoadFromFile($sFileIn)
_GDIPlus_ImageSaveToFile($hImage, $sFileOut)
_GDIPlus_BitmapDispose($hImage)
_GDIPlus_Shutdown()
Это дало мне надежду на то, что не надо разбирать отдельно все форматы, а прийти к простой работе с массивом пикселей, как это делается со стандартной кодировкой в BMP-файле. Я имею немалый опыт без использования библиотек ковырять BMP-файлы, в которых только не указано, что используется нестандартная кодировка (формат предусматривает много чего, что редко используется на практике) и создавать BMP-файлы такого же свойства, но без библиотек это выглядит кустарно. Неужели надежда на то, что для поставленной задачи есть простой путь, тщетна, а вся премудрость разбора различных форматов наглухо скрыта от скриптера AutoIt в единственной функции _GDIPlus_ImageSaveToFile(), которая внутри себя устраивает эту всю разборку, и лишь редактирование этой функции под себя может создать требуемые возможности?

Впрочем, пожалуй, вряд ли только эта функция умеет справляться с разными форматами единообразно, ведь этот скрипт также успешно работал, когда между чтением и записью добавлялись функции масштабирования графического объекта и изменения его резолюции. Если последнее ещё может задаваться сменой единого данного, то первое вряд ли может работать без копания в формате, там же даже предусмотрена возможная смена алгоритма преобразования.
 

InnI

AutoIT Гуру
Сообщения
4,912
Репутация
1,429
прийти к простой работе с массивом пикселей
Так понятно?
Код:
#include <GDIPlus.au3>

$sFileIn = FileOpenDialog("Select an image", @ScriptDir, "All (*.*)|Image (*.jpg;*.png;*.bmp;*.gif;*.tif)")
$sFileOut = FileSaveDialog("Save the result image", @ScriptDir, "All (*.*)|Image (*.jpg;*.png;*.bmp;*.gif;*.tif)|JPG (*.jpg)|PNG (*.png)|BMP (*.bmp)|GIF (*.gif)|TIF (*.tif)")

_GDIPlus_Startup()

$hImage = _GDIPlus_ImageLoadFromFile($sFileIn)
$iW = _GDIPlus_ImageGetWidth($hImage)
$iH = _GDIPlus_ImageGetHeight($hImage)

; Получаем буфер с данными картинки. Если передаём "0, 0, $iW, $iH", то получим картинку целиком.
$tBitmapData = _GDIPlus_BitmapLockBits($hImage, 0, 0, $iW, $iH, BitOR($GDIP_ILMWRITE, $GDIP_ILMREAD), $GDIP_PXF32ARGB)
; Фактически $tBitmapData - это структура, определённая в StructureConstants.au3 следующим образом
; Global Const $tagGDIPBITMAPDATA = "uint Width;uint Height;int Stride;uint Format;ptr Scan0;ptr Reserved"

; Теперь из этой структуры можем получать данные по картинке в буфере (фактически по нашей исходной)
$iWidth = $tBitmapData.Width   ; аналог _GDIPlus_ImageGetWidth()
$iHeight = $tBitmapData.Height ; аналог _GDIPlus_ImageGetHeight()
$iStride = $tBitmapData.Stride ; количество байт в линии (если разделить на $iWidth - получим число байт на пиксель)
$iFormat = $tBitmapData.Format ; аналог _GDIPlus_ImageGetPixelFormat()[0]

$iScan0 = $tBitmapData.Scan0
; $iScan0 - это указатель на начало последовательности пикселей
; Но для работы с пикселями их нужно получить следующим образом
$tPixel = DllStructCreate("int[" & $iW * $iH & "];", $iScan0)

; Теперь можно работать с $tPixel, как со структурой
; Например, заменить синий на прозрачный (пример из справки)
$iSearchPixel = 0xFF000080
$iReplaceColor = 0x00000000
For $iY = 0 To $iH - 1
  $iRowOffset = $iY * $iW + 1
  For $iX = 0 To $iW - 1 ; get each pixel in each line and row
    $iPixel = DllStructGetData($tPixel, 1, $iRowOffset + $iX) ; get pixel color
    If $iPixel = $iSearchPixel Then DllStructSetData($tPixel, 1, $iReplaceColor, $iRowOffset + $iX) ; compare and replace pixel color (blue with transparent color)
  Next
Next

; Теперь фиксируем изменения - переносим данные из буфера в картинку
_GDIPlus_BitmapUnlockBits($hImage, $tBitmapData)

; И сохраняем. Конвертацией в нужный формат занимается сама GDI+
_GDIPlus_ImageSaveToFile($hImage, $sFileOut)

_GDIPlus_ImageDispose($hImage)
_GDIPlus_Shutdown()
 
  • Like
Реакции: Oki
Автор
Oki

Oki

Продвинутый
Сообщения
452
Репутация
62

InnI

Спасибо, теперь намного более понятно, но не совсем ясны два аспекта.

Зачем требуется обязательно блокировка, если не ожидается внешнее вмешательство в изображение? Только потому, что нужный функционал есть лишь у функции блокировки? Или слово "блокировка" имеется в виду не в смысле препятствования изменениям, а в смысле выделения прямоугольного блока?

И второе. При таком подходе размеры изображения на выходе обязаны быть такими же, как у входного изображения. Не хочется применять костыль в виде масштабирования, как в стартовом посте, если это делается более естественно. Более того, это в данном примере существует входное изображение. В иной ситуации его может просто не быть.
 
Последнее редактирование:

Prog

Продвинутый
Сообщения
537
Репутация
65
Некоторое время назад тоже интересовался как получить массив пикселей.
В процессе изучения вопроса пришло понимание что проще и быстрее сделать на другом ЯП и скомпилировать dll которую вызывать из autoit скрипта.
Работа с графикой не сильная сторона autoit.
 
Автор
Oki

Oki

Продвинутый
Сообщения
452
Репутация
62
Некоторое время назад тоже интересовался как получить массив пикселей.
Вот два рабочих скрипта, дающих одинаковые результаты.
Код:
#include <GDIPlus.au3>
#include <Array.au3>
$sFileIn = FileOpenDialog("Select an image", @ScriptDir, "Image (*.jpg;*.png;*.bmp;*.gif;*.tif)")
$hTime = TimerInit()
_GDIPlus_Startup()
$hImage = _GDIPlus_ImageLoadFromFile($sFileIn)
$aDim = _GDIPlus_ImageGetDimension($hImage)
Dim $aPixels[$aDim[1]][$aDim[0]]
For $iCoordY = 0 To $aDim[1] - 1
    For $iCoordX = 0 To $aDim[0] - 1
        $aPixels[$iCoordY][$iCoordX] = Hex(_GDIPlus_BitmapGetPixel($hImage, $iCoordX, $iCoordY), 8)
    Next
Next
_GDIPlus_ImageDispose($hImage)
_GDIPlus_Shutdown()
$nTime = TimerDiff($hTime)
_ArrayDisplay($aPixels, "Executed in " & $nTime & "milliseconds.")
Код:
#include <GDIPlus.au3>
#include <Array.au3>
$sFileIn = FileOpenDialog("Select an image", @ScriptDir, "Image (*.jpg;*.png;*.bmp;*.gif;*.tif)")
$hTime = TimerInit()
_GDIPlus_Startup()
$hImage = _GDIPlus_ImageLoadFromFile($sFileIn)
$aDim = _GDIPlus_ImageGetDimension($hImage)
Dim $aPixels[$aDim[1]][$aDim[0]]
$tBitmapData = _GDIPlus_BitmapLockBits($hImage, 0, 0, $aDim[0], $aDim[1], BitOR($GDIP_ILMWRITE, $GDIP_ILMREAD), $GDIP_PXF32ARGB)
$tPixel = DllStructCreate("int[" & $aDim[0] * $aDim[1] & "];", $tBitmapData.Scan0)
For $iCoordY = 0 To $aDim[1] - 1
    For $iCoordX = 0 To $aDim[0] - 1
        $aPixels[$iCoordY][$iCoordX] = Hex(DllStructGetData($tPixel, 1, $iCoordY * $aDim[0] + $iCoordX + 1), 8)
    Next
Next
_GDIPlus_BitmapUnlockBits($hImage, $tBitmapData)
_GDIPlus_ImageDispose($hImage)
_GDIPlus_Shutdown()
$nTime = TimerDiff($hTime)
_ArrayDisplay($aPixels, "Executed in " & $nTime & "milliseconds.")
Второй работает пошустрее минимум раза в 4. Сейчас ещё разберёмся, как быстро сохранять, и всё будет работать.
В процессе изучения вопроса пришло понимание что проще и быстрее сделать на другом ЯП и скомпилировать dll которую вызывать из autoit скрипта.
Ещё как бы не получилось с точностью до наоборот, что именно сложную обработку массива переведу в другой язык программирования, а конвертацию в массив и назад всё-таки оставлю на AutoIt.
Но некоторые функции принудительно преобразуют в ARGB
Как оказалось, именно на этом спотыкались мои тесты, поэтому до ошибки с транспонированием даже не доходило. Выше два скрипта для конвертации в одну сторону. Оба рабочие и дают идентичные результаты. Но второй выполняется в разы быстрее, это очень хорошо. Вопрос обратной конвертации остаётся открытым, подробности в моём предыдущем (вчерашнем) ответе.
 
Последнее редактирование:

InnI

AutoIT Гуру
Сообщения
4,912
Репутация
1,429
Или слово "блокировка" имеется в виду
Уточните в Microsoft.

размеры изображения на выходе обязаны быть такими же, как у входного изображения
Никто вам ничего не обязан. В примере выделен буфер с форматом пикселя $GDIP_PXF32ARGB. Если говорить о bmp-файлах, то они, как правило, используют $GDIP_PXF24RGB. Поэтому сохранённый файл будет больше исходного. А jpeg и png файлы вообще имеют настраиваемое сжатие.
Если вам нужно работать с оригинальным форматом пикселя, то нужно его запросить. Но тогда и структуру придётся создавать соответствующую, и работать с этой структурой побайтово.
Код:
$iFormat = _GDIPlus_ImageGetPixelFormat($hImage)[0]
$tBitmapData = _GDIPlus_BitmapLockBits($hImage, 0, 0, $iW, $iH, BitOR($GDIP_ILMWRITE, $GDIP_ILMREAD), $iFormat)
$BPP = $tBitmapData.Stride / $tBitmapData.Width
$tPixel = DllStructCreate("byte[" & $iW * $iH * $BPP & "];", $tBitmapData.Scan0)


его может просто не быть
Я уже отвечал на этот вопрос.
Смотрите в английской справке второй пример для функции
Код:
_GDIPlus_BitmapCreateFromScan0()
 
Автор
Oki

Oki

Продвинутый
Сообщения
452
Репутация
62
Поэтому сохранённый файл будет больше исходного.
При чём тут размер файла?! Речь же русским по белому шла о размерах изображения, которые остаются такими же, как были у исходного изображения.
 

InnI

AutoIT Гуру
Сообщения
4,912
Репутация
1,429
Речь же русским по белому шла о размерах изображения, которые остаются такими же, как были у исходного изображения.
Вообще не понял. У вас меняется размер (ширина, высота) картинки? Или вы хотите изменить размер (ширину, высоту) картинки?
 
Автор
Oki

Oki

Продвинутый
Сообщения
452
Репутация
62
Пытаюсь написать скрипт по образцу.
Код:
#include <GDIPlus.au3>
_GDIPlus_Startup()
Dim $aPixels[100][200]
Local $tPixel = DllStructCreate("uint[" & UBound($aPixels, 1) * UBound($aPixels, 2) & "];")
For $iCoordX = 0 To UBound($aPixels, 1) - 1
    For $iCoordY = 0 To UBound($aPixels, 2) - 1
        $iAlpha = 0
        $iRed = Round(255 * $iCoordX / (UBound($aPixels, 1) - 1))
        $iGreen = Round(255 * $iCoordY / (UBound($aPixels, 2) - 1))
        $iBlue = 0
        DllStructSetData($tPixel, 1, 256 * (256 * (256 * $iAlpha + $iRed) + $iGreen) + $iBlue, $iCoordY * UBound($aPixels, 1) + $iCoordX + 1)
    Next
Next
$hBitmap = _GDIPlus_BitmapCreateFromScan0(UBound($aPixels, 2), UBound($aPixels, 1), $GDIP_PXF32ARGB, UBound($aPixels, 2), $tPixel)
$sFile = FileSaveDialog("Save the result image", @ScriptDir, "All (*.*)|Image (*.jpg;*.png;*.bmp;*.gif;*.tif)|JPG (*.jpg)|PNG (*.png)|BMP (*.bmp)|GIF (*.gif)|TIF (*.tif)")
_GDIPlus_ImageSaveToFile($hBitmap, $sFile)
_GDIPlus_BitmapDispose($hBitmap)
_GDIPlus_Shutdown()
Почему-то на выходе пустой файл.
Сообщение автоматически объединено:

Вообще не понял. У вас меняется размер (ширина, высота) картинки? Или вы хотите изменить размер (ширину, высоту) картинки?
Требуется назначать размеры произвольно, а не копировать их из другого изображения, как было в одном из примеров выше.
Сообщение автоматически объединено:

Если четвёртый параметр функции _GDIPlus_BitmapCreateFromScan0() увеличить в 4 раза, то файл создаётся непустым, но изображение не соответствует массиву пикселей совершенно. Если домножить на 4 также первое слагаемое третьего параметра функции DllStructSetData(), то изображение становится более похожим на заданное, но всё же что-то идёт не так. В приведённых примерах о множителе 4 не было даже намёка, это моя интуитивная догадка с учётом того, что ARGB требует 4 байт.
 
Последнее редактирование:

InnI

AutoIT Гуру
Сообщения
4,912
Репутация
1,429
Почему-то на выходе пустой файл.
Потому что Stride - это ширина умноженная на количество байт на пиксель. В примере указана только ширина. Недостающее добавляется из контекста устройства (монитора).

увеличить в 4 раза, то файл создаётся непустым, но изображение не соответствует массиву пикселей совершенно
Ну, не знаю. Я в вашем примере увеличил на 4 и получил такое (см. вложение). А как должно быть? Может с расчётами ошиблись?

Требуется назначать размеры произвольно
И в чём проблема? Если вы создаёте картинку с нуля, то задайте нужные W и H.
А для изменения картинок-файлов есть функции
Код:
_GDIPlus_ImageResize()
_GDIPlus_ImageScale()
 

Вложения

  • 1.jpg
    1.jpg
    2.2 КБ · Просмотры: 4
Автор
Oki

Oki

Продвинутый
Сообщения
452
Репутация
62
А для изменения картинок-файлов есть функции
Код:
_GDIPlus_ImageResize()
_GDIPlus_ImageScale()
Это как раз тот путь, который у меня был в изначальном скрипте в стартовом сообщении темы. И там в комментарии к соответствующей строке как раз отмечено, что это выглядит похоже на кривой костыль, потому что работает не по такому же принципу, как ReDim, оставляя данные на месте, и их всё равно требуется заполнять заново. Короче говоря, лучше сразу рассматривать, как построить изображение с нуля. С расчётами у себя не вижу ошибок. Может быть, что-то концептуальное в применении синтаксиса некорректно.
 

InnI

AutoIT Гуру
Сообщения
4,912
Репутация
1,429
работает не по такому же принципу, как ReDim, оставляя данные на месте
А разве ReDim перемещает данные? Его особенность именно в том, чтобы сохранить данные при изменении массива. И при чём тут GDI+?
Нет, не понимаю, чего вы хотите.
 
Автор
Oki

Oki

Продвинутый
Сообщения
452
Репутация
62
А разве ReDim перемещает данные? Его особенность именно в том, чтобы сохранить данные при изменении массива. И при чём тут GDI+?
Нет, не понимаю, чего вы хотите.
Зачем акцентировать внимание на непонятом, если этот путь в любом случае неуниверсален? Говорю же, что лучше сразу рассматривать, как построить изображение с нуля. Если же просто очень любопытно понять, о чём шла речь в контексте применения функций изменения размера картинки, то повторюсь, что таким путём нельзя просто изменить размеры точно таким же способом, как это делает ReDim, с сохранением данных в старых пределах и последующим дозаполнением только отсутствующих пикселей. В любом случае лучше уметь строить картинку с нуля из массива, а не этим костыльным путём.
 

InnI

AutoIT Гуру
Сообщения
4,912
Репутация
1,429
изменить размеры точно таким же способом, как это делает ReDim, с сохранением данных в старых пределах и последующим дозаполнением только отсутствующих пикселей
Так?
Код:
#include <GDIPlus.au3>

$sFileIn = FileOpenDialog("Select an image", @ScriptDir, "All (*.*)|Image (*.jpg;*.png;*.bmp;*.gif;*.tif)")
$sFileOut = FileSaveDialog("Save the result image", @ScriptDir, "All (*.*)|Image (*.jpg;*.png;*.bmp;*.gif;*.tif)|JPG (*.jpg)|PNG (*.png)|BMP (*.bmp)|GIF (*.gif)|TIF (*.tif)")

_GDIPlus_Startup()

$hImage = _GDIPlus_ImageLoadFromFile($sFileIn)
$iW = _GDIPlus_ImageGetWidth($hImage)
$iH = _GDIPlus_ImageGetHeight($hImage)

; Создаём новую картинку нужного размера
$iWn = $iW * 2
$iHn = $iH + 100
$hImgNew = _GDIPlus_BitmapCreateFromScan0($iWn, $iHn)
; Создаём поле рисования
$hGraph = _GDIPlus_ImageGetGraphicsContext($hImgNew)
; Копируем исходную по заданным координатам
_GDIPlus_GraphicsDrawImage($hGraph, $hImage, 0, 0)

; Создаём буфер с данными новой картинки
$tBitmapData = _GDIPlus_BitmapLockBits($hImgNew, 0, 0, $iWn, $iHn, BitOR($GDIP_ILMWRITE, $GDIP_ILMREAD), $GDIP_PXF32ARGB)
; Обрабатываем пиксели (все или только новые)
Transform()
; Фиксируем изменения в новой картинке
_GDIPlus_BitmapUnlockBits($hImgNew, $tBitmapData)
; Сохраняем новую картинку
_GDIPlus_ImageSaveToFile($hImgNew, $sFileOut)

_GDIPlus_ImageDispose($hImage)
_GDIPlus_ImageDispose($hImgNew)
_GDIPlus_GraphicsDispose($hGraph)
_GDIPlus_Shutdown()

Func Transform()
  ConsoleWrite("Transform" & @CRLF)
EndFunc
 
  • Like
Реакции: Oki

Prog

Продвинутый
Сообщения
537
Репутация
65
Второй работает пошустрее минимум раза в 4.
Потребовалось 6 секунд на обработку jpg файла с размерами 1280x960.

Если картинка большая эту задачу лучше решать на компилируемом ЯП, собрав dll и подключив к скрипту.
Этот код
Код:
UseJPEGImageDecoder()
UsePNGImageDecoder()
UseGIFImageDecoder()
UseTIFFImageDecoder()

Img.s = OpenFileRequester("", "", "Image (*.jpg;*.png;*.bmp;*.gif;*.tif)|*.jpg;*.png;*.bmp;*.gif;*.tif", 0)

If Img
  If LoadImage(0, Img)
 
    Dim arr.l(ImageWidth(0), ImageHeight(0))
   
    Time=ElapsedMilliseconds()
    StartDrawing(ImageOutput(0))
    w = OutputWidth()-1
    h = OutputHeight()-1
    For x=0 To w
      For y=0 To h
        arr(x, y) = Point(x, y)
      Next
    Next
    StopDrawing()
    MessageRequester("","Время работы "+Str(ElapsedMilliseconds()-t)+" миллисекунд")
 
  Else
    MessageRequester("", "Ошибка при загрузке картинки")
  EndIf
EndIf
ту же картинку (размер 1280x960) преобразует в массив пикселей за 20 миллисекунды. И при необходимости возможно еще ускорить заменив функцию Point() на прямую работу с буфером картинки.
 

Вложения

  • ImageToArray.7z
    258.2 КБ · Просмотры: 2
Последнее редактирование:
Автор
Oki

Oki

Продвинутый
Сообщения
452
Репутация
62
Спасибо, так работает, но только у меня не получилось приспособить ни этот скрипт, ни создание картинки с нуля к ситуации, когда исходный файл в стандартном 24-битном формате True Color. На выходе получается всё равно 32-битный. Это как-то можно исправить? В функции DllStructCreate() не предусмотрена возможность 24-битных элементов. Или это как-то с помощью ALIGN делается? Не знаю, как выкручиваться из этой ситуации.

Знаю, что в стандартном bitmap-формате на этот случай просто пихают все байты подряд в строку пиксель за пикселем по три байта, а в конце строки добавляют от 0 до 3 излишних байт, так чтобы дополнить количество байт на строку до кратного четырём. Но со структурами так, вроде бы, нелогично поступать.

Вот так если запускать, то почти вся картинка чёрной получается.
Код:
#include <GDIPlus.au3>
_GDIPlus_Startup()
Dim $aPixels[100][200]
Local $tPixel = DllStructCreate("BYTE[" & 3 * UBound($aPixels, 1) * UBound($aPixels, 2) & "];")
For $iCoordY = 0 To UBound($aPixels, 1) - 1
    For $iCoordX = 0 To UBound($aPixels, 2) - 1
        $iRed = Round(255 * $iCoordX / (UBound($aPixels, 2) - 1))
        $iGreen = Round(255 * $iCoordY / (UBound($aPixels, 1) - 1))
        $iBlue = 0
        DllStructSetData($tPixel, $iBlue, 3 * ($iCoordY * UBound($aPixels, 2) + $iCoordX) + 1)
        DllStructSetData($tPixel, $iGreen, 3 * ($iCoordY * UBound($aPixels, 2) + $iCoordX) + 2)
        DllStructSetData($tPixel, $iRed, 3 * ($iCoordY * UBound($aPixels, 2) + $iCoordX) + 3)
    Next
Next
$hBitmap = _GDIPlus_BitmapCreateFromScan0(UBound($aPixels, 2), UBound($aPixels, 1), $GDIP_PXF24RGB, 3 * UBound($aPixels, 2), $tPixel)
$sFile = FileSaveDialog("Save the result image", @ScriptDir, "All (*.*)|Image (*.jpg;*.png;*.bmp;*.gif;*.tif)|JPG (*.jpg)|PNG (*.png)|BMP (*.bmp)|GIF (*.gif)|TIF (*.tif)")
_GDIPlus_ImageSaveToFile($hBitmap, $sFile)
_GDIPlus_BitmapDispose($hBitmap)
_GDIPlus_Shutdown()
 
Последнее редактирование:
Верх