Записать обработанное растровое изображение в файл
Я работаю над проектом, который читает содержимое файла BMP и выполняет плавный цифровой фильтр на изображении. Мой код ниже почти делает работу. Я просто не знаю, где мне хранить обработанные пиксели и как я могу записать обработанное изображение в файл. Мой код ниже закомментирован, поэтому любая помощь будет принята с благодарностью.
.586
.model flat, stdcall
option casemap :none
include ..\masm32\include\windows.inc
include ..\masm32\include\user32.inc
include ..\masm32\include\kernel32.inc
include ..\masm32\macros\macros.asm
include ..\masm32\include\masm32.inc
includelib ..\masm32\lib\user32.lib
includelib ..\masm32\lib\kernel32.lib
includelib ..\masm32\lib\masm32.lib
.data
FileName db "bitmap2.bmp", 0
filename db "bitmap_fil.bmp",0
errMsg BYTE "Cannot create file",0dh,0ah,0
hFile HANDLE ?
hwFile HANDLE ?
hMemory HANDLE ? ;incoming data
pMemory DWORD ?
hMemory_o HANDLE ? ;outgoing data
pMemory_o DWORD ?
ReadSize DWORD ?
bytesWritten DWORD ?
firstLine DWORD ?
FileSize DWORD ?
BDoff DWORD ?
BHSize DWORD ?
szTemp byte 16 dup (0) ;buffer for messages
szPrime byte "%08i", 0 ;message format string
szPrimeH byte "%08lx",0 ;message hexa format string
signature DD 0
MEMORYSIZE equ 65535 ;This is how much memory allocated
; to store the file.
im_offset dd ?
im_width dd ?
im_height dd ?
bits_pix dd ?
.code
;................................
show MACRO caption, value
print SADD(caption)
mov eax, value
invoke wsprintf, offset szTemp, offset szPrime, eax ;converts eax into string
print offset szTemp ;print string
print SADD(13,10)
ENDM
;..................................
start:
invoke CreateFile, addr FileName,GENERIC_READ,FILE_SHARE_READ,NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL
mov hFile, eax
;Allocate and lock the memory for incoming file.
invoke GlobalAlloc, GMEM_MOVEABLE or GMEM_ZEROINIT, MEMORYSIZE
mov hMemory, eax
invoke GlobalLock, hMemory
mov pMemory, eax
;Allocate and lock the memory for outgoing file.
invoke GlobalAlloc, GMEM_MOVEABLE or GMEM_ZEROINIT, MEMORYSIZE
mov hMemory_o, eax
invoke GlobalLock, hMemory_o
mov pMemory_o, eax
;Read file and save image parameters
invoke ReadFile, hFile, pMemory, MEMORYSIZE-1, addr ReadSize, NULL
mov esi, pMemory
add esi, 02 ;get filesize
mov edi, [esi]
mov FileSize,edi
invoke wsprintf, offset szTemp, offset szPrime, edi
print offset szTemp
print SADD(10,13)
add esi, 8 ; jump 8 bytes to get image offset
mov edi, [esi] ; get image offset
mov im_offset,edi
invoke wsprintf, offset szTemp, offset szPrimeH, edi
print offset szTemp
print SADD(10,13)
add esi,8 ;jump 8 bytes to get image width
mov edi, [esi] ; get image width
mov im_width, edi
invoke wsprintf, offset szTemp, offset szPrime, edi
print offset szTemp
print SADD(10,13)
add esi,4 ;jump 4 bytes to get image height
mov edi, [esi] ; get image height
mov im_height, edi
invoke wsprintf, offset szTemp, offset szPrime, edi
print offset szTemp
print SADD(10,13)
add esi,4 ;jump 4 bytes to get color plane
mov ebx, [esi]
shr ebx,16 ; get color plane and bit-pix
mov bits_pix,ebx ;
print SADD("bit-per-pix ")
invoke wsprintf, offset szTemp, offset szPrime, ebx
print offset szTemp
print SADD(10,13)
mov ebp, pMemory ; get ready to start processing the image
add ebp, im_offset ; esi now points to the first pix
;filtering process
;leave first row and first column and last row and last column untouched.
mov esi,1 ; esi is the row counter
mov edi,1 ; edi is the column counter
proc_pix:
;show "current column is: ",edi
;show "current row is: ",esi
xor ebx,ebx ; ebx = 0 ebx will accumulate intermediate values for averaging
mov eax, im_width ; eax is the pointer to the pixel
mul esi
add eax,edi
add ebx, [ebp+eax] ;get the pixel(I,J)
mov eax, im_width
add esi,1 ;next row (I+1)
mul esi
add eax,edi
add ebx, [ebp+eax] ;get the pixel(I+1,J)
mov eax, im_width
sub esi,2 ; prev row (I-1)
mul esi
add eax,edi
add ebx, [ebp+eax] ;get the pixel(I-1,J)
add esi,1 ;back to the current row
add edi,1 ;get next column (J+1)
mov eax, im_width
mul esi
add eax,edi
add ebx, [ebp+eax] ;get the pixel(I,J+1)
sub edi,2 ; get prev column (J-1)
mov eax, im_width
mul esi
add eax,edi
add ebx, [ebp+eax] ;get the pixel(I,J-1)
add edi,1 ;back current column
xor edx, edx ;clear upper part dividend
mov eax, ebx ; move data to eax to divide
mov ecx, 5
div ecx ;do the average (div 5)>>> result in eax
; where should I store the processed pixels?
inc edi ; do the next column
cmp edi, im_width
jl proc_pix
inc esi ; do the next row
mov edi,1 ; skip the first column
cmp esi, im_height
jl proc_pix
;....................................................................................
new_file:
invoke CreateFile,ADDR filename, GENERIC_WRITE, NULL, NULL,CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0
mov hwFile,eax ; save file handle
; handling of error if invalid file handle
cmp eax,INVALID_HANDLE_VALUE
jne writef
invoke StdOut, addr errMsg ; Display error message
jmp QuitNow
writef: invoke WriteFile, hwFile, pMemory_o, FileSize, ADDR bytesWritten, 0
QuitNow:
invoke GlobalUnlock, pMemory
invoke GlobalUnlock, pMemory_o
invoke GlobalFree, hMemory
invoke CloseHandle, hFile
invoke CloseHandle, hwFile
invoke ExitProcess, NULL
end start
;finish
1 ответ
У вас уже есть буфер для вывода; вам просто нужно вычислить смещение в этом буфере, чтобы сохранить его. Если отфильтрованный вами пиксель (I, J), из которого вы читаете pMemory + im_offset + J * im_width + I
, то вы хотите написать это pMemory_o + im_offset + J * im_width + I
, (Кроме того, ваш код, кажется, принимает 8 бит на пиксель; вы можете явно проверить это и выйти с ошибкой, если это не так, чтобы избежать неожиданного поведения в файлах изображений не-8bpp.)
Чтобы правильно написать целевой пиксель, вы можете выполнить то же умножение строк / столбцов, что и при чтении, чтобы получить смещение в eax
, добавлять im_offset
и добавить его в pMemory_o
(последние два вместо использования ebp
как в случае чтения).
Однако, как и запись пикселей в новое изображение, вы хотите скопировать заголовок из исходного файла (чтобы зрители и т. Д. Видели его как растровое изображение); сделать memcpy
(или уместно rep movsb
) от pMemory
в pMemory_o
длина im_offset
, чтобы сделать это. Вам также нужно скопировать в него первую строку и столбец (и, как я ожидаю, последнюю строку и столбец, что будет иметь ту же проблему, что и первый: вы не можете получить окружающие пиксели для некоторых сторон). "Тупой", но эффективный метод состоит в том, чтобы просто скопировать все содержимое старого изображения в новое, заголовок, пиксели и все, а затем просто изменить внутренние пиксели с помощью фильтра.
Ваш код для создания и записи выходного буфера кажется правильным; по-видимому, теперь он записывает заполненный нулем / случайным образом файл правильной длины.
Кроме того, как только вы это сделаете правильно, вы можете сэкономить много работы (предположительно, эффективность является одной из причин писать в сборку в первую очередь?), Используя сложение и вычитание вместо умножения: добавление im_width
переходит на следующую строку, вычитая ее на предыдущую и т. д. (возможно, вам придется вычислять шаг вместо использования im_width
непосредственно, округляя до ближайших 32 бит; и если ваше примерное изображение не кратно 4 пикселям в ширину, вы можете увидеть странные результаты из-за неправильного шага).
Ниже приведены полезные ссылки для других: BITMAPFILEHEADER (начиная со смещения файла 0), который содержит подпись, размер файла (ваш FileSize
) и смещение изображения (im_offset
), за которым следует BITMAPINFOHEADER, который имеет такую информацию, как ширина, высота и битовая глубина.