vendredi 10 mai 2024

Laplace Operator

Laplacian operator is a basic function used to find edges in image. You'll find here https://docs.opencv.org/3.4/d5/db5/tutorial_laplace_operator.html all mathematical explanations about this operator. This code illustrates how it's easy to use Lapace operator with Rebol-OpenCV extension.

#!/usr/local/bin/r3
Rebol [
]
;-- see: https://docs.opencv.org/3.4/d5/db5/tutorial_laplace_operator.html
 
kSize: 3
scale: 1
delta: 0
cv: import opencv
with cv [
ddepth: CV_16S
img: imread %../../image/lena.jpg ;--read image as matrix
gray: Matrix img ;--make grayscale matrix
        dst:  Matrix img ;--make destination matrix
namedWindow  src: "Source" ;--create source window
namedWindow  lap: "Laplacian" ;--create destination windows
moveWindow src 0x10 ;--move source window
moveWindow lap 260x10 ;--move destination window
GaussianBlur img img 3x3 0 0 ;--reduce noise with a Gaussian filter
cvtColor :img :gray COLOR_BGR2GRAY ;--make a grayscale matrix
Laplacian :gray :dst ddepth kSize scale delta         ;--Laplacian filter
convertScaleAbs :dst :dst 1 0 ;--we need a 8-bit image
imshow/name img src ;--show source window
imshow/name dst lap ;--show destination window
waitKey 0 ;--wait for keypress
    destroyAllWindows ;--clear all window
]

Result:



jeudi 9 mai 2024

Morphological Transformations

You'll find here https://towardsdatascience.com/understanding-morphological-image-processing-and-its-operations-7bcf1ed11756 a very clear explanation of morphological operators.

For Red, operators are defined in RedCV/libs/imgproc/rcvMorphology.red.

Rebol-OpenCV supports some basic morphological operators such as erode or dilate. Basically these operators are used with a binary image, but in reality they can be used with any image as illustrated in this code:

#!/usr/local/bin/r3
Rebol [
]
;--see https://docs.opencv.org/4.x/d4/d76/tutorial_js_morphological_ops.html
cv: import opencv ;--import Rebol-OpenCV extension
with cv [
    src: imread "../../image/lena.png"         ;--source image
    namedWindow win1: "Source" ;--create window 1
    namedWindow win2: "Erosion" ;--create window 2
    namedWindow win3: "Dilatation" ;--create window 3
    moveWindow win1 0x0 ;--move window 1
    moveWindow win2 260x0 ;--move window 2
    moveWindow win3 520x0 ;--move window 2
    kernel1: getStructuringElement MORPH_CROSS 5 1      ;--prepare the kernel for erosion 
    kernel2: getStructuringElement MORPH_CROSS 5 -1     ;--prepare the kernel for dilatation
    dst1: erode src none kernel1 -1x-1 1                                  ;--erode the source image
    dst2: dilate src none kernel2 -1x-1 1                                  ;--dilate the source image
    imshow/name src win1 ;--show the source image
    imshow/name dst1 win2 ;--show the eroded image
    imshow/name dst2 win3 ;--show the dilated image
    waitKey 0 ;--any key to close
    destroyAllWindows ;--delete all windows
]




mardi 7 mai 2024

Rebol-OpenCV convertTo function

ConvertTo function converts an array to another data type with optional scaling.

For example in a few LOC you can modify the brightness of any image as illustrated below. 

#!/usr/local/bin/r3
Rebol [
]
cv: import opencv
with cv [
alpha: 1.0 ;--scale factor
beta: 64 ;--delta added to the scaled values
filename: %../../image/lena.tiff         ;--file name
img: imread/with filename IMREAD_UNCHANGED ;--load image as is
mat1: convertTo img none CV_8U alpha beta ;--increase the brightness by 64 
mat2: convertTo img none CV_8U alpha negate beta    ;--decrease the brightness by 64 
imshow/name img  "Source" ;--show image 
imshow/name mat1 "High Brightness" ;--show image
imshow/name mat2 "Low Brightness" ;--show image
moveWindow "Source"  260x0 ;--move image
moveWindow "High Brightness" 520x0 ;--move image
moveWindow "Low Brightness" 0x0 ;--move image
waitKey 0 ;--any key to close
]


The result: 



Making your own linear filters with Rebol-OpenCV

 In his Rebol-OpenCV extension, Oldes has implemented a filter2D function which is very useful for constructing various types of linear filters, such as a Sobel filter, a Kurawhara filter, etc.

In this code, I've just used the example given in the OpenCV documentation.

You will find here a complete documentation about the use of filter2D : https://docs.opencv.org/3.4/d4/dbd/tutorial_filter_2d.html


#!/usr/local/bin/r3
Rebol [
]
;-- see https://docs.opencv.org/3.4/d4/dbd/tutorial_filter_2d.html 
cv: import opencv
with cv [
    src: imread "../../image/lena.jpg" ;--use your own image
    dst: Matrix :src ;--create matrices from image
    anchor: -1x-1 ;--kernel center
    delta: 0.0 ;--default value
    ddepth: -1 ;-- output image depth as same as the input image
    ind: 0 ;--counter for forever loop
    print "Press any key to stop the animation!"
    forever [
    kernelSize: 3 + (2 * (ind % 5)) ;--odd values in the range [3..11]
    kSize2: to-integer kernelSize ** 2 ;--update kernel size for a normalized filter
    vec: make vector! reduce ['float! kSize2] ;--create Rebol vector 
    vec: vec + (1.0 / kSize2) ;--update vector with normalized values
    kSize: as-pair kernelSize kernelSize ;--use a pair! for matrix creation
    kernel: Matrix [CV_32FC1 :kSize :vec] ;--create kernel 
    filter2D src dst ddepth kernel anchor delta ;--call OpenCV filter2D 
    imshow/name dst "Normalized Kernel" ;--show result
    if 0 <= waitkey 500 [break] ;--exit if any key
    ++ ind ;--increment counter
    ]
    destroyAllWindows ;--close window
]

This linear filter creates a blurring effect on the source image according to the kernel size.
You can also have a look to gaussianBlur and medianBlur in Rebol-Opencv extension.

lundi 6 mai 2024

Reading Animated GIF Files with Rebol-Opencv

GIF (Graphics Interchange Format) is used to store multiple bitmap images in a single file for exchange between platforms and systems. In terms of number of files in existence, GIF is perhaps the most widely used format for storing multi-bit graphics and image data.

GIF is different from many other common bitmap formats in the sense that it is stream-based. It consists of a series of data packets, called blocks, along with additional protocol information. Because of this arrangement, GIF files must be read as if they are a continuous stream of data. The various blocks and sub-blocks of data defined by GIF may be found almost anywhere within the file. This uncertainty makes it difficult to encapsulate every possible arrangement of GIF data in the form of data structures.

In the past, I spent a lot of time, with the help of Toomas Vooglaid (https://github.com/toomasv/LZW), writing functions to enable Red at Rebol 3 to decode these GIF files. We now have a much simpler solution with Rebol-OpenCV. Just think of the GIF file as a simple video. 

This a sample of code :

#!/usr/local/bin/r3
Rebol [
]
cv: import opencv ;--import Rebol-OpenCV extension
with cv [
    movie: VideoCapture %../../image/dance.gif ;--use your gif file
    unless movie [quit]
    ;--get some information about the gif file
    print ["Width :" w: get-property :movie CAP_PROP_FRAME_WIDTH]
    print ["Height:" h: get-property :movie CAP_PROP_FRAME_HEIGHT]
    print ["FPS   :" fps: get-property :movie CAP_PROP_FPS]
    print ["Frames:" nbFrames: to integer! get-property :movie CAP_PROP_FRAME_COUNT]
    print "ESC to close animation"
    delay: 1.5 / fps
    count: 0
    frame: read :movie ;--first image 
    imshow :frame 
    forever [
        read/into :movie :frame ;--no need to create a new matrix for each frame
        setWindowTitle "Image" join "Frame: " count + 1 ;--Redbol languages are one-based
        imshow :frame 
        wait delay        
        count: count + 1
        ;--when we are at the end of the gif go back to first image
        if count = nbFrames [set-property :movie CAP_PROP_POS_FRAMES 0 count: 0]
        k: pollKey           
    if k = 27 [break]  
    ]  
    waitKey 0
    print "closing.."
    destroyAllWindows
    free :movie ;--we must free the memory: no GC for VideoCapture 
    print "done"
]

And a sample result







Using Rebol3 for image and movie processing

As I'm eagerly awaiting a 64-bit version of Red that could be used with Apple's new processors (m1 and m2), I've gone back to Rebol 3 and in particular the superb version maintained by Oldes that you can find here: https://github.com/Oldes/Rebol3/releases

Oldes has also written a fabulous extension for Rebol3 that allows you to use the latest versions of OpenCV: https://github.com/Oldes/Rebol-OpenCV.

Admittedly, the extension isn't yet complete, but it's already capable of good image processing and easy management of video streams.

My idea is to make available to everyone a few code examples that illustrate the power of Rebol3 and OpenCV. 

Thanks again to Oldes for his hard work.