-
-
Save practicingruby/481e7290ed8f1a16afd5 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# coding: binary | |
class BMP | |
class Reader | |
PIXEL_ARRAY_OFFSET = 54 | |
BITS_PER_PIXEL = 24 | |
DIB_HEADER_SIZE = 40 | |
def initialize(bmp_filename) | |
File.open(bmp_filename, "rb") do |file| | |
read_bmp_header(file) | |
read_dib_header(file) | |
read_pixels(file) | |
end | |
end | |
def [](x,y) | |
@pixels[y][x] | |
end | |
attr_reader :width, :height | |
def read_pixels(file) | |
@pixels = Array.new(@height.abs) { Array.new(@width.abs) } | |
if @height < 0 | |
enum = 0.upto(@height.abs - 1) | |
else | |
enum = (@height-1).downto(0) | |
end | |
enum.each do |y| | |
0.upto(@width - 1) do |x| | |
@pixels[y][x] = file.read(3).unpack("C3")#.first | |
end | |
advance_to_next_row(file) | |
end | |
end | |
def advance_to_next_row(file) | |
padding_bytes = @width % 4 | |
return if padding_bytes == 0 | |
file.pos += padding_bytes | |
end | |
def read_bmp_header(file) | |
header = file.read(14) | |
magic_number, file_size, reserved1, | |
reserved2, array_location = header.unpack("A2Vv2V") | |
fail "Not a bitmap file!" unless magic_number == "BM" | |
unless file.size == file_size | |
fail "Corrupted bitmap: File size is not as expected" | |
end | |
@pixel_array_offset = array_location | |
end | |
def read_dib_header(file) | |
header = file.read(40) | |
header_size, width, height, planes, bits_per_pixel, | |
compression_method, image_size, hres, | |
vres, n_colors, i_colors = header.unpack("Vl<2v2V2l<2V2") | |
unless header_size == DIB_HEADER_SIZE | |
fail "Corrupted bitmap: DIB header does not match expected size" | |
end | |
unless planes == 1 | |
fail "Corrupted bitmap: Expected 1 plane, got #{planes}" | |
end | |
unless bits_per_pixel == BITS_PER_PIXEL | |
fail "#{bits_per_pixel} bits per pixel bitmaps are not supported" | |
end | |
unless compression_method == 0 | |
fail "Bitmap compression not supported" | |
end | |
unless image_size.zero? || (image_size + PIXEL_ARRAY_OFFSET == file.size) | |
fail "Corrupted bitmap: pixel array size isn't as expected" | |
end | |
@width, @height = width, height | |
end | |
end | |
end | |
class BMP | |
class Writer | |
PIXEL_ARRAY_OFFSET = 54 | |
BITS_PER_PIXEL = 24 | |
DIB_HEADER_SIZE = 40 | |
PIXELS_PER_METER = 2835 # 2835 pixels per meter is basically 72dpi | |
def initialize(width, height) | |
@width, @height = width, height | |
@pixels = Array.new(@height) { Array.new(@width) { [0,0,0] } } | |
end | |
def [](x,y) | |
@pixels[y][x] | |
end | |
def []=(x,y,value) | |
@pixels[y][x] = value | |
end | |
def save_as(filename) | |
File.open(filename, "wb") do |file| | |
write_bmp_file_header(file) | |
write_dib_header(file) | |
write_pixel_array(file) | |
end | |
end | |
private | |
def write_bmp_file_header(file) | |
file << ["BM", file_size, 0, 0, PIXEL_ARRAY_OFFSET].pack("A2Vv2V") | |
end | |
def file_size | |
PIXEL_ARRAY_OFFSET + pixel_array_size | |
end | |
def pixel_array_size | |
((BITS_PER_PIXEL*@width)/32.0).ceil*4*@height | |
end | |
def write_dib_header(file) | |
file << [DIB_HEADER_SIZE, @width, @height, 1, BITS_PER_PIXEL, | |
0, pixel_array_size, PIXELS_PER_METER, PIXELS_PER_METER, | |
0, 0].pack("Vl<2v2V2l<2V2") | |
end | |
def write_pixel_array(file) | |
@pixels.reverse_each do |row| | |
row.each do |color| | |
file << color.pack("C3")#pixel_binstring(color) | |
end | |
file << row_padding | |
end | |
end | |
#def pixel_binstring(rgb_string) | |
# raise ArgumentError unless rgb_string =~ /\A\h{6}\z/ | |
# [rgb_string].pack("H6") | |
#end | |
def row_padding | |
"\x0" * (@width % 4) | |
end | |
end | |
end | |
t = Time.now | |
i1 = BMP::Reader.new("urban954bw.bmp") | |
i2 = BMP::Reader.new("par185bw.bmp") | |
w, h = i1.width.abs, i2.height.abs | |
bmp = BMP::Writer.new(w, h) | |
diff = Hash.new | |
require "matrix" | |
w.times.to_a.product(h.times.to_a) do |x,y| | |
diff[[x,y]] = (Vector[*i2[x,y]] - Vector[*i1[x,y]]).to_a | |
end | |
temp = Hash.new | |
11.times do |i| | |
w.times do |x| | |
h.times do |y| | |
bmp[x, y] = (Vector[*i1[x,y]] + (Vector[*diff[[x,y]]]/10.0*i).map { |e| e.round }).to_a | |
end | |
end | |
bmp.save_as("composite-#{i}.bmp") | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment