before i reply to the above i’d also suggest checking out narray - it features
ultra fast and pretty easy to use very large array manipulation. it is a c
extension available from the raa…
re: above
yes - i have example code - and yes mmap is a c extension (written by ts). my
code is ultra specific to a statelite image format we use here at ngdc that’s
something like
number of records = 3021
number of header records = 1
number of data records = 3020
record size = 7231
…
…
…
…
…
lots_of_binary_satelite_data32409132483124083209840238402983…
409132483124083209840238402983…
409132483124083209840238402983…
i have classes for header, scanlines, scanline header, and to get at
row/pixels.
excerpt follows (contact me offline and i will send you sources); this should
give you the general flavor of what i’m talking about. below shows the idea
behind a row level ‘munging’ method (#map!) that makes use of mmap to change
pixels in place w/o really doing anything explict. pixel operations would be
similar. the speed is gained by mapping the file ‘rw’ and never hanging on to
more data than required at a time - in this case a scanline (record) of data.
RAA
require ‘mmap’
…
module DMSP
module OLS
class Header < Array
#{{{
…
…
def k
#{{{
idx = index_of k
raise IndexError, “#{ k.class } : #{ k.inspect }” unless idx
ret = super(idx)
raise IndexError, "#{ k.class } : #{ k.inspect }" unless
ret and ret.size == 2
Numeric === k ? ret : ret[1]
#}}}
end
# new
…
def = k, v
#{{{
if Numeric === k
raise TypeError, “#{ v.class } : #{ v.inspect }” unless Array === v
@modified = true
return super(k,v)
end
idx = index_of k
if idx
k = self[idx].first if Regexp === k
else
raise IndexError, "#{ k.class } : #{ k.inspect }" if Regexp === k
idx = self.size # append
end
raise TypeError, "#{ k.class } : #{ k.inspect }" unless String === k
pair = [k.strip, v]
# recurse with numeric argument!
self[idx] = pair
v
#}}}
end
…
#}}}
end # class Header
class Mmap < ::Mmap
#{{{
…
…
# methods
def initialize(*args)
#{{{
@args = DMSP::hashify(*args)
@path, @mode, @protection =
@args.values_at :path, :mode, :protection
@type = DMSP::file_type @path
@mode ||= DEFAULT_MODE
@protection ||= DEFAULT_PROTECTION
super(path, mode, protection)
@header = Header.new :io => self
self
#}}}
end
…
def get_record n
#{{{
n = n.to_i
raise(IndexError, “INDEX [%s]” % [n]) if
n < 0 or n >= number_of_records
rb = header.record_bytes
start, length = n * rb, rb
self[start, length]
#}}}
end
…
def set_record n, data
#{{{
n = n.to_i
raise(IndexError, “INDEX [%s]” % [n]) if
n < 0 or n >= number_of_records
rb = header.record_bytes
raise(RangeError, "DATA SIZE [%d]" % [data.size]) if
data.size != rb
start, length = n * rb, rb
self[start, length] = data
#}}}
end
…
def map!
#{{{
changed = false
r = header.number_of_header_records … header.number_of_records
r.each do |n|
data = yield(get_record(n))
if data
set_record(n, data)
changed = true
end
end
changed ? self : nil
#}}}
end
alias collect! map!
…
#}}}
end # class MmappedFile
end # module DMSP
i would think that, for an image, you may end up with something like
(un-tested)
class Image < Mmap
# class to support double ([i][j]) style indexing
class Row
attr :row
attr :image
def initialize row, image; @row = row; @image = image; end
def col
image[row * image.n_cols + col]
end
def = col, pixel
image[row * image.n_cols + col] = pixel
end
end
attr :n_rows
attr :n_cols
def initialize path, n_rows, n_cols
super path, 'rw', Mmap::MAP_SHARED
end
def [] row
Row.new row, self
end
def pixel i, j, pix = nil
pix ? (self[i * n_cols + j] = pix) : self[i * n_cols + j]
end
end
which would allow you to do something like
p(image.pixel i, j)
image.pixel i, j, 0b101010
p(image.pixel i, j)
or
p(image.pixel[i][j])
image.pixel[i][j] = 0b101010
p(image.pixel[i][j])
you would proably be better off using rmagick or narray. the reason i chose
this approach is that the ONLY thing i really needed was pixel level access
since we are applying our own algorithims to the pixels and installing rmagick
or narray for this was a bit overkill. mmap is lightweight and fast if all
you desire is pixel level access.
food for thought.
-a
···
On Mon, 2 Feb 2004, Charles Comstock wrote:
You don’t happen to have some code for example of this? I’m not quite
following is this through a C extension or just raw ruby? My solution I did
last time was wrap the get/set pixel methods so they only grabbed a 1x1
range, and then I could access them directly, but this somehow seems like it
probably incites horrible performance.
Charles Comstock
–
ATTN: please update your address books with address below!
===============================================================================
EMAIL :: Ara [dot] T [dot] Howard [at] noaa [dot] gov
PHONE :: 303.497.6469
ADDRESS :: E/GC2 325 Broadway, Boulder, CO 80305-3328
STP :: http://www.ngdc.noaa.gov/stp/
NGDC :: http://www.ngdc.noaa.gov/
NESDIS :: http://www.nesdis.noaa.gov/
NOAA :: http://www.noaa.gov/
US DOC :: http://www.commerce.gov/
The difference between art and science is that science is what we
understand well enough to explain to a computer.
Art is everything else.
– Donald Knuth, “Discover”
/bin/sh -c ‘for l in ruby perl;do $l -e “print "\x3a\x2d\x29\x0a"”;done’
===============================================================================