bmp长宽比问题 [英] bmp aspect ratio issue

查看:99
本文介绍了bmp长宽比问题的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我一直试图了解bmp文件的工作方式,因此我可以渲染一些Mandelbrot设置的图片并将其输出为bmp文件,因为这似乎是最简单的方法之一,但是由于某些原因,当我使用的宽高比不是即使是4的幂,也不会达到1:1(因此不需要填充),我得到了类似这些

注意:在BMP中,Y轴从底部开始.我比较习惯计算机图形学中从上到下的Y轴(所以我是这样写的.)

感谢HAX的代码.

I've been trying to understand how bmp files work so I can render some Mandelbrot set pictures and output them as bmp files since that seems to be one of the easiest methods but for some reason when I use an aspect ratio that isn't 1:1 even though its something to the power of 4 (so no padding is needed) I get weird artifacts like these 200:100 48:100 what I'm trying to do is turning an array of pixels that has white for even numbers and black for odd numbers into a bmp, this (100:100) is what it looks like with 1:1 aspect ratio. I've tried reading through the wikipedia article to see if I can figure out what I'm doing wrong but I still don't get what I'm missing.

This is the script I've written in Lua so far:

ResolutionX = 100
ResolutionY = 100

local cos, atan, sin, atan2, sqrt, floor = math.cos, math.atan, math.sin, math.atan2, math.sqrt, math.floor
local insert, concat = table.insert, table.concat
local sub, char, rep = string.sub, string.char, string.rep

io.output("Test.bmp")

function Basen(n,b)
    n = floor(n)
    if not b or b == 10 then return tostring(n) end
    local digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
    local t = {}
    repeat
        local d = (n % b) + 1
        n = floor(n / b)
        insert(t, 1, digits:sub(d,d))
    until n == 0
    return rep("0",32-#t)..concat(t,"")
end

FileSize = Basen(ResolutionY*ResolutionX*3 + 54,2)
FileSize4 = tonumber(sub(FileSize,1,8),2) or 0
FileSize3 = tonumber(sub(FileSize,9,16),2) or 0
FileSize2 = tonumber(sub(FileSize,17,24),2) or 0
FileSize1 = tonumber(sub(FileSize,25,32),2) or 0

Width = Basen(ResolutionX,2)
print("Width:   ",Width)
Width4 = tonumber(sub(Width,1,8),2) or 0
Width3 = tonumber(sub(Width,9,16),2) or 0
Width2 = tonumber(sub(Width,17,24),2) or 0
Width1 = tonumber(sub(Width,25,32),2) or 0

Height = Basen(ResolutionY,2)
print("Height: ",Height)
Height4 = tonumber(sub(Height,1,8),2) or 0
Height3 = tonumber(sub(Height,9,16),2) or 0
Height2 = tonumber(sub(Height,17,24),2) or 0
Height1 = tonumber(sub(Height,25,32),2) or 0

BMPSize = Basen(ResolutionY*ResolutionX*3,2)
BMPSize4 = tonumber(sub(BMPSize,1,8),2) or 0
BMPSize3 = tonumber(sub(BMPSize,9,16),2) or 0
BMPSize2 = tonumber(sub(BMPSize,17,24),2) or 0
BMPSize1 = tonumber(sub(BMPSize,25,32),2) or 0

print("TotalSize: ",FileSize1,FileSize2,FileSize3,FileSize4,"\nWidth:   ",Width1,Width2,Width3,Width4,"\nHeight: ",Height1,Height2,Height3,Height4,"\nImage data: ",BMPSize1,BMPSize2,BMPSize3,BMPSize4)

Result = {"BM"..char(                       --File type
    FileSize1,FileSize2,FileSize3,FileSize4,--File size
    0,0,0,0,                                --Reserved
    54,0,0,0,                               --Where the pixel data starts
    40,0,0,0,                               --DIB header
    Width1,Width2,Width3,Width4,            --Width
    Height1,Height2,Height3,Height4,        --Height
    1,0,                                    --Color planes
    24,00,                                  --Bit depth
    0,0,0,0,                                --Compression
    BMPSize1,BMPSize2,BMPSize3,BMPSize4,    --The amount of bytes pixel data will consume
    Width1,Width2,Width3,Width4,
    Height1,Height2,Height3,Height4,
    0,0,0,0,                                --Number of colors in palatte
    0,0,0,0
)}

for X = 0, ResolutionX - 1 do
    for Y = 0, ResolutionY - 1 do
        insert(Result,rep(char(255 * ((X + 1) % 2) * ((Y + 1) % 2)),3))
    end
end

io.write(table.concat(Result))

解决方案

Ok, here's a BMP version. I've put things in a module so it might be easier to use.

local writeBMP = {}

local floor = math.floor
local insert, concat = table.insert, table.concat
local sub, char, rep = string.sub, string.char, string.rep

local function Basen(n,b)
    n = floor(n)
    if not b or b == 10 then return tostring(n) end
    local digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
    local t = {}
    repeat
        local d = (n % b) + 1
        n = floor(n / b)
        insert(t, 1, digits:sub(d,d))
    until n == 0
    return rep("0",32-#t)..concat(t,"")
end

local function nextMul4(x)
    if ( x % 4 == 0 ) then
        return x
    else
        return x+4-(x%4)
    end
end

local function clamp(x)
    local y = x
    if ( x > 255 ) then
        y = 255
    elseif ( x < 0 ) then
        y = 0
    end
    return floor(y)
end

-- Accepts array of type pixelsXYC[X][Y][C] of numbers 0-255
-- C=1,2,3 are the red, green and blue channels respectively
-- X increases left to right, and Y increases top to bottom
function writeBMP.data(pixelsXYC, resolutionX, resolutionY)
    local Pixels = pixelsXYC
    local ResolutionX = resolutionX
    local ResolutionY = resolutionY
    assert(#Pixels == ResolutionX, "Table size and X resolution mismatch")
    assert(#Pixels[1] == ResolutionY, "Table size and Y resolution mismatch")

    local FileSize = Basen(ResolutionY*nextMul4(3*ResolutionX) + 54,2)
    local FileSize4 = tonumber(sub(FileSize,1,8),2) or 0
    local FileSize3 = tonumber(sub(FileSize,9,16),2) or 0
    local FileSize2 = tonumber(sub(FileSize,17,24),2) or 0
    local FileSize1 = tonumber(sub(FileSize,25,32),2) or 0

    local Width = Basen(ResolutionX,2)
    local Width4 = tonumber(sub(Width,1,8),2) or 0
    local Width3 = tonumber(sub(Width,9,16),2) or 0
    local Width2 = tonumber(sub(Width,17,24),2) or 0
    local Width1 = tonumber(sub(Width,25,32),2) or 0

    local Height = Basen(ResolutionY,2)
    local Height4 = tonumber(sub(Height,1,8),2) or 0
    local Height3 = tonumber(sub(Height,9,16),2) or 0
    local Height2 = tonumber(sub(Height,17,24),2) or 0
    local Height1 = tonumber(sub(Height,25,32),2) or 0

    local BMPSize = Basen(ResolutionY*nextMul4(3*ResolutionX),2)
    local BMPSize4 = tonumber(sub(BMPSize,1,8),2) or 0
    local BMPSize3 = tonumber(sub(BMPSize,9,16),2) or 0
    local BMPSize2 = tonumber(sub(BMPSize,17,24),2) or 0
    local BMPSize1 = tonumber(sub(BMPSize,25,32),2) or 0

    local Result = {}
    Result[1] = "BM" .. char(                   --File type 
        FileSize1,FileSize2,FileSize3,FileSize4,--File size
        0,0,                                    --Reserved
        0,0,                                    --Reserved
        54,0,0,0,                               --Where the pixel data starts
        40,0,0,0,                               --DIB header
        Width1,Width2,Width3,Width4,            --Width
        Height1,Height2,Height3,Height4,        --Height
        1,0,                                    --Color planes
        24,0,                                   --Bit depth
        0,0,0,0,                                --Compression
        BMPSize1,BMPSize2,BMPSize3,BMPSize4,    --The amount of bytes pixel data will consume
        37,22,0,0,                              --Pixels per meter horizontal
        37,22,0,0,                              --Pixels per meter vertical
        0,0,0,0,                                --Number of colors in palatte
        0,0,0,0
    )

    local Y = ResolutionY
    while( Y >= 1 ) do
        for X = 1, ResolutionX do
            local r = clamp( Pixels[X][Y][1] )
            local g = clamp( Pixels[X][Y][2] )
            local b = clamp( Pixels[X][Y][3] )
            Result[#Result+1] = char(b)
            Result[#Result+1] = char(g)
            Result[#Result+1] = char(r)
        end
        -- byte alignment
        if ( ( (3*ResolutionX) % 4 ) ~= 0 ) then
            local Padding = 4 - ((3*ResolutionX) % 4)
            Result[#Result+1] = rep(char(0),Padding)
        end
        Y = Y - 1
    end

    return table.concat(Result)
end

function writeBMP.write(pixelsXYC, resolutionX, resolutionY, filename)
    local file = io.open(filename,"wb")
    local data = writeBMP.data(pixelsXYC, resolutionX, resolutionY)
    file:write(data)
end

return writeBMP

A simple test:

-- writeBMP example
local writeBMP = require "writeBMP"

local resolutionX = 100
local resolutionY = 100
-- Pixel data
local pixels = {}
for x=1,resolutionX do
    pixels[x] = {}
    for y=1, resolutionY do
        pixels[x][y] = {}
        local red = 255*(resolutionX-x+resolutionY-y)/(resolutionX+resolutionY)
        local green = 255*y/resolutionY
        local blue = 255*x/resolutionX
        pixels[x][y][1] = red
        pixels[x][y][2] = green
        pixels[x][y][3] = blue
    end
end

writeBMP.write(pixels,resolutionX,resolutionY,"testwritebmp.bmp")
return

Note: In BMP, the Y axis starts on the bottom. I am more used to Y axis going from top down in computer graphics (so I wrote it that way).

Thanks HAX for the code.

这篇关于bmp长宽比问题的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆