mercredi 3 mai 2017

RedCV: How Red stores pixel values in memory

Each ARGB pixel component is represented by a byte, i.e. a 8-bit value. The byte! datatype's purpose is to represent unsigned integers in the 0-255 range.

Many image processing libraries use a byte pointer to access ARGB components of a pixel, but Red proposes an optimized approach which uses an integer to store ARGB values in a single value.
Since the memory size of an integer is 32 bits, is really easy to store 4 bytes (8-bit) value within an integer. Consequently a int-ptr! will be used to access pixel value.

Now to access to ARGB values stored in the integer, Red applies right shift operator, both unsigned right shift (>>>) and signed right shift (>>).  

a: pix1/value >>> 24                ; byte 1 [0-255]
r: pix1/value and 00FF0000h >> 16   ; byte 2 [0-255]
g: pix1/value and FF00h >> 8        ; byte 3 [0-255]
b: pix1/value and FFh               ; byte 4 [0-255]


Then to write back  a modified pixel, Red uses signed left shift operator (<<) to make a signed integer with 4 bytes.

pixD/value: (a << 24) OR (r << 16) OR (g << 8) OR b

This approach using an integer to store pixels is very smart and very efficient. The access to any pixel is really easy with a simple int-ptr! You just need to know to base address of the image in memory and use pointer arithmetic to access the pixel you want.  Of course, to be efficient this implementation requires Red/System DSL which is faster than Red in image processing. Fortunately, Red/System objects give us access to useful macros and methods. With routine! it is simple to call Red/System code inside a pure Red/Code.

Reading image

Let's start by an example to illustrate how we can copy an image with pointers.

_rcvCopy: routine [
    src1 [image!]
    dst  [image!]
    /local
        pix1 [int-ptr!]
        pixD [int-ptr!]
        handle1 handleD h w x y
][
    handle1: 0
    handleD: 0
    pix1: image/acquire-buffer src1 :handle1
    pixD: image/acquire-buffer dst :handleD
    w: IMAGE_WIDTH(src1/size)
    h: IMAGE_HEIGHT(src1/size)
    x: 0
    y: 0
    while [y < h] [
       while [x < w][
            pixD/value: pix1/value
            x: x + 1
            pix1: pix1 + 1
            pixD: pixD + 1
       ]
       x: 0
       y: y + 1
    ]
    image/release-buffer src1 handle1 no
    image/release-buffer dst handleD yes
]

This RedCV routine has 2 parameters: the source and the destination images and uses 2 int-ptr to access to the source and destination memory address. 
There is no control on images and you need to ensure that both images have the same size.
The image/acquire-buffer method gives access to the memory base address for each image and returns a pointer. Since Red uses a classical row-major order for accessing pixel, it's really easy to read the whole image. First of all, get the image size with IMAGE_WIDTH and IMAGE_HEIGHT macros and just call 2 imbricated while instructions for reading image line by line and column by column.
pixD/value: pix1/value affectation simply copies source image value to destination image and then you just need to increase both source and destination images pointers for accessing the next pixel.

Modifying image

Of course you can modified pixel values as you want. The next (incomplete) example illustrates how create a grayscale image.

while [y < h] [
       while [x < w][
            a: pix1/value >>> 24
            r: pix1/value and 00FF0000h >> 16 
            g: pix1/value and FF00h >> 8 
            b: pix1/value and FFh 
            s: (r + g + b) / 3 
            pixD/value: (a << 24) OR (s << 16 ) OR (s << 8) OR s]
            x: x + 1
            pix1: pix1 + 1
            pixD: pixD + 1
        ]
    x: 0
    y: y + 1
]
Here, we read argb values and then calculate the mean value for rbg values with  s: (r + g + b) / 3. Then we udpate destination image pixel value:

pixD/value: (a << 24) OR (s << 16 ) OR (s << 8) OR s] ;RGB2Gray average




Red is fantastic!

Aucun commentaire:

Enregistrer un commentaire