samedi 20 juillet 2024

Gems from Rebol

Each version of Rebol includes pearls that make image processing easy.

In Rebol 2, for example, you can find an extremely fast convolution function.  

You'll find here http://www.rebol.com/view/demos/convolve.r, the demo of convolution effect (By Cyphre).

I remember presenting Rebol at the Hanoi Polytechnic University (https://bachkhoahanoi.edu.vn/) a long time ago, and colleagues were impressed by the speed of a simple interpreted script designed for convolution.

Basically, this function uses a 3 by 3 kernel and offers various filters such as emboss and others. But you can also create your own filter.


These ideas have been implemented in redCV and you can find several examples in the RedCV/samples/image_convolution directory.

Recently, I did a little digging into the Rebol 3 version developed by Oldes (https://github.com/Siskin-framework/Rebol) and also found a few gems. 

The first is blur function which allows a Gaussian blurring of any image.
USAGE:
     BLUR image radius

DESCRIPTION:
     Blur (Gaussian) given image. 
     BLUR is a native! value.

ARGUMENTS:
     image         [image!]   Image to blur (modified).
     radius         [number!]  Blur amount.

radius must be positive and > 1. If radius = 1 you'll get an un-blurred image as result.


The second one is rgb-to-hsv function: 

USAGE:
     RGB-TO-HSV rgb

DESCRIPTION:
     Converts RGB value to HSV (hue, saturation, value). 
     RGB-TO-HSV is a native! value.

ARGUMENTS:
     rgb           [tuple!] 
     
Of course yo'll get the inverse function hsv-to-rgb:
USAGE:
     HSV-TO-RGB hsv

DESCRIPTION:
     Converts HSV (hue, saturation, value) to RGB. 
     HSV-TO-RGB is a native! value.

ARGUMENTS:
     hsv           [tuple!]   






Thanks to these fabulous developers for offering us such easy-to-use tools.






vendredi 12 juillet 2024

Red 64-bit Image

More than once while developing redCV, I regretted that Red didn't offer the possibility of creating images in 8, 16, 32 and 64 bits, with a channel number from 1 to 4 as is the case with OpenCV.  Such formats are sometimes very useful for speeding up image processing and improving precision. When we developed Matrix with Toomas Vooglaid and Qingtian Xie in 2020, we solved some of the problems. But I was left wanting more, so I did a bit of digging to find out whether Red objects could answer this question. The following code is just one illustration of such an approach.


#!/usr/local/bin/red

Red [

]


rcv: object [

create: func [

type [integer!] ;--1:byte 2:integer 3:float

bit [integer!] ;--8, 16, 32 or 64-bit

isize [pair!] ;--image size as pair!

channels [integer!] ;--1 to 4 channels

return: [vector!] ;--image data

/local

size width height [integer!]

data [vector!]

][

width: isize/x

height: isize/y

size: width * height * channels 

switch bit [

8  [data: make vector! reduce ['char! 8 (size)]]

16 [data: make vector! reduce ['integer! 16 (size)]]

32 [data: make vector! reduce ['integer! 32 (size)]]

64 [data: make vector! reduce ['float! 64 (size)]]

]

data

]

]

;********************************* tests ************************************

iSize: 256x256

random/seed now/precise

img1: rcv/create 3 64 iSize 1 ;--create a float image with 1 channel

n: length? img1

repeat i n [img1/:i: round/to random 1.0 0.01] ;--random values [0..1]

repeat i n [img1/:i: img1/:i / 1.0 * 255]         ;--random values [0..255]

bin1: copy #{} ;--binary string

repeat i n [append/dup bin1  to integer! img1/:i 3]         ;--integer values

dest1: make image! reduce [iSize bin1] ;--a grayscale Red image with 3 channels


img2: rcv/create 3 64 iSize 3 ;--create a float image with 3 channels

n: length? img2

repeat i n [img2/:i: round/to random 1.0 0.01] ;--random values [0..1]

repeat i n [img2/:i: img2/:i / 1.0 * 255]         ;--random values [0..255]

bin2: copy #{} ;--binary string

foreach [r g b] img2 [

append bin2 to-integer r ;--red channel 

append bin2 to-integer g ;--green channel

append bin2 to-integer b ;--blue channel

]

dest2: make image! reduce [iSize bin2] ;--a rgb Red image


view [

title "64-bit image test"

below

image dest1

image dest2

pad 100x0

button "Quit" [quit]




Red Image datatype

REBOL/View 1.3

In REBOL/View 1.3 it was necessary to redesign the image datatype in order to make image operations more consistent and less system dependent.The image datatype is structured as a standard REBOL series. It has a head, a tail, and can be positioned to any point in-between. In additional, the new image datatype allows two dimensional positioning and sizing through the use of an X Y pair. (http://www.rebol.com/docs/image.html#section-1). Attention: first pixel coordinates is 0x0 and first pixel index is 1!

Red

In Red, image datatype is a series-like. This means that some series functions are not working. 

Making images

img1: make image! reduce [200x100 red]         :--REBOL/View and Red

With transparency

img3: make image! reduce [50x50 blue 128]                 :--REBOL/View

img3: make image! reduce [50x50 blue + 0.0.0.128]    ;--Red

Copy images

The COPY function works in the standard way as it does with all series, making an exact copy of the image provided.

i1: make image! reduce [200x100 red]

i2: copy i1 ;--REBOL/View and Red

i2: copy/part i1 5x5                 ;--REBOL/View and Red

i2: copy/part skip i1 4x4 5x5                 ;--REBOL/View and Red

i2: copy/part skip i1 2x2 (i1/size - 4x4)                         ;--REBOL/View and Red

Traversing Images (Indexing) 

You can use all the standard series functions to index to any position within an image series. The functions include: HEAD, TAIL, NEXT, BACK, SKIP, AT, and others. 

i2: next i2 ; move to next pixel                                         ;--REBOL/View and Red

i2: back i2 ; move to prior pixel                                        ;--REBOL/View and Red

i2: head i2 ; move to first pixel                                         ;--REBOL/View and Red 

i2: tail i2 ; move just past the last pixel                            ;--REBOL/View and Red

i2: back tail i2 ; move to the last pixel                             ;--REBOL/View and Red

Modifying Images 

Images can be modified in two ways. They can be modified as individual pixels (with poke function) or they can be modified as a series of pixels. 

 change at img1 1x1 img2                                                   --REBOL/View and Red

change/dup at img1 10x20 blue 40x30                               ; --REBOL/View

append img1 img2 ; uses insert ;                                         ;--REBOL/View

remove/part img1 img1/size/x                                             ;--REBOL/View

remove/part tail img1 negate img1/size/x                            ;--REBOL/View

 insert/part img1 blue img1/size/x                                        ;--REBOL/View


Searching Images

pos: find img1 red             ;--REBOL/View


Image Comparison

The EQUAL?, NOT-EQUAL?, and SAME? series comparison functions work for images in the same way as they do for other series datatypes.

ANDing, ORing, and XORing Images

Partially supported by Red, but supported by redCV

img1 AND img2 ;--REBOL/View

img1/rgb AND img2/rgb ;--Red


mardi 9 juillet 2024

32-bit Integer overflow

We often get error messages related to overflow. This is annoying, because the code is interrupted and for Red beginners, it's not always easy to understand the nature of the errors. A classic example is overflow when you multiply 2 32-bit integers and the result exceeds the minimum or maximum integer value supported by Red (-2147483648 or 2147483648).

You will find 2 functions (Red/System and Red) that are an attempt to solve this type of problem.

First with Red/System

Red/System [

Author:  "ldci"
]
;--system/cpu/overflow? checks if the last integer math operation has overflown.
;--as many CPU operations can change this state, it is only reliable if used immediatly after the math operation.
isMulOverflow?: func [
"function to multiply two integers and check for overflow"
a [integer!]
b [integer!]
return: [logic!]
][
    a * b ;--Perform the multiplication
    system/cpu/overflow? ;--Immediately return the cpu overflow status
]
;--tests
print-wide ["1000 * 70000   overflow?:" isMulOverflow? 1000 70000 lf]
print-wide ["740000 * 70000 overflow?:" isMulOverflow? 740000 70000 lf]

And now with Red

Red [
]
isMulOverflow?: func [
a [integer!]
b [integer!]
return: [logic!]
] [
if ((a = 0) or (b = 0)) [return false]
attempt [result:  a * b] 
either a = (result / b) [return false] [return true] 
]
;--tests
print ["1000 * 5000 Overflow?:"    isMulOverflow? 1000 5000]
print ["-1000 * 1000 Overflow?:"   isMulOverflow? -1000 1000]
print ["740000 * 70000 Overflow?:" isMulOverflow? 740000 70000]