{"id":26397699,"url":"https://github.com/oldes/rebol-opencv","last_synced_at":"2025-03-17T12:18:44.633Z","repository":{"id":62616452,"uuid":"547268532","full_name":"Oldes/Rebol-OpenCV","owner":"Oldes","description":"Rebol/OpenCV extension","archived":false,"fork":false,"pushed_at":"2024-05-21T20:01:05.000Z","size":857,"stargazers_count":2,"open_issues_count":0,"forks_count":1,"subscribers_count":4,"default_branch":"master","last_synced_at":"2024-05-22T10:39:57.444Z","etag":null,"topics":["opencv","rebol","rebol-extension"],"latest_commit_sha":null,"homepage":"","language":"C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Oldes.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-10-07T12:04:45.000Z","updated_at":"2024-05-27T12:24:17.863Z","dependencies_parsed_at":"2023-12-05T13:28:44.330Z","dependency_job_id":"f75d4c37-46a9-4396-bc7e-90c68dbf948d","html_url":"https://github.com/Oldes/Rebol-OpenCV","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Oldes%2FRebol-OpenCV","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Oldes%2FRebol-OpenCV/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Oldes%2FRebol-OpenCV/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Oldes%2FRebol-OpenCV/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Oldes","download_url":"https://codeload.github.com/Oldes/Rebol-OpenCV/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":244031118,"owners_count":20386534,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["opencv","rebol","rebol-extension"],"created_at":"2025-03-17T12:18:44.017Z","updated_at":"2025-03-17T12:18:44.624Z","avatar_url":"https://github.com/Oldes.png","language":"C","readme":"\n[![Rebol-OpenCV CI](https://github.com/Oldes/Rebol-OpenCV/actions/workflows/main.yml/badge.svg)](https://github.com/Oldes/Rebol-OpenCV/actions/workflows/main.yml)\n[![Gitter](https://badges.gitter.im/rebol3/community.svg)](https://app.gitter.im/#/room/#Rebol3:gitter.im)\n\n# Rebol/OpenCV\n\nInitial implementation of the OpenCV extension for [Rebol3](https://github.com/Oldes/Rebol3) (3.14.0 or newer).\n\nSo far it is considered just like a proof of concept and tested only on macOS with [OpenCV installed using homebrew](https://formulae.brew.sh/formula/opencv)!\n\n\nWhen building on macOS, the setup expects, that OpenCV includes and libs are accessible. So it is recommanded to use something like:\n```\nln -s /opt/homebrew/Cellar/opencv/4.6.0/include/opencv4/opencv2 /usr/local/include/opencv2\nln -s /opt/homebrew/Cellar/opencv/4.6.0/lib /usr/local/lib/opencv\n```\nTo import the extension from Rebol on macOS, the Rebol must be signed with entitlemens containing `com.apple.security.cs.disable-library-validation` as `true`. \n\nFeature requests are welcome.\n\nFor list of currently supported extension commands and other values, read [Commands.md](Commands.md).\n\n## Usage\n\nAll folowing examples expect, that OpenCV extension was imported using one of these methods:\n1. Using direct path to the file: `cv: import %path/to/opencv.rebx`\n2. Using extension in the default location: `cv: import 'opencv`\n\n### Making a blank matrix and displaying it.\n\nMatrices are one of the main datatypes used by OpenCV extension.\n\n```rebol\nmat: cv/Matrix 250x140 ;; creates an empty black image handle\ncv/imshow mat          ;; open a window with default name \"Image\" displaying the image\ncv/waitKey 0           ;; and wait for any key\n```\n\n### Closing a window\n\nWindow is closed using `destroyWindow \"Window name\"`.\nIt is possible to close all windows using `destroyAllWindows`\n\n```rebol\ncv/destroyWindow \"Image\"       ;; because \"Image\" is the default window's name.\n```\n\n### Loading a matrix from file\n\nInstead of writting full paths to `cv` commands, like: `cv/imshow`, it is possible\nto bind the code to the `cv` context using `with cv [...]`\n\n```rebol\nwith cv [\n    filename: %image/mask.png\n    mat: imread filename\n    imshow/name mat filename   ;; displaying the image in the window with file name's title\n    waitKey 0\n]\n```\n\n### Moving/resizing an opened window\n\nHaving the window from the previous example still open, it is possible to move it using `moveWindow`.\n\n```rebol\ncv/moveWindow filename 300x50\ncv/waitKey 5000                ;; now there is only 5s wait time\n```\n\nWindows created using `namedWindow` may be resized using `resizeWindow`\n\n```rebol\ncv/namedWindow win: \"Resized\"  ;; creating a window with title/name \"Resized\"\ncv/imshow/name mat win         ;; displaying an image in it\ncv/resizeWindow win 500x280    ;; resized\ncv/waitKey 5000\ncv/destroyAllWindows           ;; closing both windows\n```\n\n### Getting matrix properties\n\nUsing still the matrix from above, resized using `resize` command so the output is not too long..\n\n```rebol\nwith cv [\n    mat: resize mat 10%\n    probe get-property mat MAT_SIZE     ;; image size\n    probe get-property mat MAT_TYPE     ;; CV type id\n    probe get-property mat MAT_CHANNELS ;; number of channels (3 = RGB)\n    probe get-property mat MAT_DEPTH    ;; CV depth id (0 = CV_8U)\n    probe get-property mat MAT_BINARY   ;; raw binary data\n    probe get-property mat MAT_VECTOR   ;; Rebol vector value\n    probe get-property mat MAT_IMAGE    ;; Rebol image value\n]\n```\nAbove is now also possible with direct getters, like:\n```rebol\nwith cv [\n    mat: resize mat 10%\n    print [\"Image size:\"         mat/size     ]\n    print [\"CV type:\"            mat/type     ]\n    print [\"Channels:\"           mat/channels ]\n    print [\"CV depth:\"           mat/depth    ]\n    print [\"Rebol binary data:\"  mat/binary   ]\n    print [\"Rebol vector value:\" mat/vector   ]\n    print [\"Rebol image value:\"  mat/image    ]\n]\n```\n\n### Manually releasing matrices\n\nNormally matrices are automatically released by Rebol's GC, but it is also possible to free them manually\n\n```rebol\ncv/free mat          ;; manually released matrix\n```\n\nIt should be noted, that such a matrix is not usable anymore! This will fail:\n\n```rebol\nprobe cv/imshow mat  ;; will return false!\n```\n\n### Color space conversion\n\n```rebol\nwith cv [\n    img: imread %image/mask.png\n    hls:  cvtColor img none COLOR_BGR2HLS\n    gray: cvtColor img none COLOR_BGR2GRAY\n    namedWindow win1: \"Original\"\n    namedWindow win2: \"HLS\"\n    namedWindow win3: \"Grayscale\"\n    imshow/name img  win1\n    imshow/name hls  win2\n    imshow/name gray win3\n    moveWindow win1 0x0\n    moveWindow win2 250x0\n    moveWindow win3 500x0\n    waitKey 0\n]\n```\n\n### Image threshold\n\nHaving the grayscale version from above, we can applie a fixed-level threshold.\n\n```rebol\nwith cv [\n    namedWindow win4: \"THRESH_BINARY\"\n    namedWindow win5: \"THRESH_TRUNC\"\n    namedWindow win6: \"THRESH_TOZERO\"\n    moveWindow  win4   0x150\n    moveWindow  win5 250x150\n    moveWindow  win6 500x150\n    thresh1: threshold gray none  0  255 THRESH_BINARY\n    thresh2: threshold gray none 100 255 THRESH_TRUNC\n    thresh3: threshold gray none 200 255 THRESH_TOZERO\n    imshow/name thresh1 win4\n    imshow/name thresh2 win5\n    imshow/name thresh3 win6\n    waitKey 0\n    destroyAllWindows\n]\n```\n\n### Using computed binary threshold as an opacity channel\n\n```rebol\nimage: cv/get-property img     cv/MAT_IMAGE   ;; get Rebol image\nalpha: cv/get-property thresh1 cv/MAT_BINARY  ;; get Rebol binary with alpha values\nimage/alpha: alpha                            ;; replace image alpha with the new value\nsave %tmp/masked.png image                    ;; using Rebol's PNG codec to save the new image\n```\n\n### Detecting edges in the image and their dilatation\n\n```rebol\nwith cv [\n    src: imread \"image/mask.png\"\n    namedWindow win1: \"Canny\"\n    namedWindow win2: \"Canny dilated\"\n    moveWindow win1 0x0\n    moveWindow win2 250x0\n\n    kernel: getStructuringElement MORPH_CROSS 3 -1 ;; preparing the kernel for dilatation \n\n    dst1: Canny src none 50 200                    ;; edge detecting\n    dst2: dilate dst1 none kernel -1x-1 1          ;; dilating the edges\n    imshow/name dst1 win1\n    imshow/name dst2 win2\n    waitKey 0\n    destroyAllWindows\n]\n```\n\n### Detecting edges using Gabor Filter\n\n```rebol\nwith cv [\n    lena: imread \"image/lena.jpeg\"\n    size: get-property :lena MAT_SIZE\n    gray: cvtColor :lena none COLOR_BGR2GRAY\n\n    num-filters: 16\n    sigma: 1.5    ;; The bandwidth or sigma controls the overall size of the Gabor envelope.\n    theta: 0      ;; The theta controls the orientation of the Gabor function.\n    lambd: 60     ;; The wavelength governs the width of the strips of the Gabor function.\n    gamma: 0.75   ;; The aspect ratio or gamma controls the height of the Gabor function.\n    psi: PI * 0.5 ;; The phase offset.\n\n    theta-step: pi / num-filters\n    kernels: make block! num-filters\n\n    loop num-filters [\n        ;- Generate new Gabor kernel\n        kern: getGaborKernel 35x35 :sigma :theta :lambd :gamma :psi CV_64F\n        normalize :kern :kern 1 0 NORM_L1 CV_64F ;- Brightness normalization\n        ;- Store it for later use\n        append kernels kern\n        ;- Update the orientation for the next kernel\n        theta: theta + theta-step\n    ]\n\n    temp: Matrix [:size CV_8UC1] ;; holds temporary filtered image\n    dest: Matrix [:size CV_8UC1] ;; detected edges\n\n    ;- Apply each Gabor kernel to filter the grayscale source\n    foreach kern kernels [\n        temp: filter2D :gray :temp -1 :kern -1x-1 0\n        normalize :temp :temp 0 255 NORM_MINMAX\n        ;- Compare our filter and cumulative image, taking the higher value\n        max :dest :temp :dest\n        ;- Display current detection state\n        imshow/name :dest \"Edges\"\n        waitKey 10\n    ]\n    waitKey 0\n]\n```\n\n### Blurring images\n\n```rebol\nwith cv [\n    src: imread \"image/mask.png\"\n    blured1: Matrix src\n    blured2: Matrix src\n    blured3: Matrix src\n    namedWindow win1: \"blur\"\n    namedWindow win2: \"gaussianBlur\"\n    namedWindow win3: \"medianBlur\"\n    moveWindow win1 0x0\n    moveWindow win2 250x0\n    moveWindow win3 500x0\n\n    size: 1\n    add-blur?: true\n    forever [\n        either add-blur? [\n            size: size + 2\n            add-blur?: size \u003c 100\n        ][\n            size: size - 2\n            add-blur?: size \u003c= 1\n        ]\n        blur         :src :blured1 :size\n        gaussianBlur :src :blured2 :size 0 0\n        medianBlur   :src :blured3 :size\n        imshow/name blured1 win1\n        imshow/name blured2 win2\n        imshow/name blured3 win3\n        if 0 \u003c= waitkey 50 [break]\n    ]\n    destroyAllWindows\n]\n```\n\n### Applying a color map\n\n```rebol\nwith cv [\n    src: imread \"image/lena.jpeg\"\n    result: Matrix :src\n    for i 0 21 1 [\n        applyColorMap :src :result :i \n        imshow :result\n        setWindowTitle \"Image\" join \"Colormap: \" i\n        if 0 \u003c waitKey 1000 [break]\n    ]\n    destroyAllWindows\n]\n```\n\n### Applying Sepia filter to an image\n\n```rebol\nwith cv [\n    src: imread \"image/taj.jpg\"\n    ; using a binary for the kernel, but it should be possible\n    ; to use vector directly later once implemented!\n    kernel: #(float! [\n        0.272 0.534 0.131\n        0.349 0.686 0.168\n        0.393 0.769 0.189\n    ])\n    sepia-filter: Matrix [3x3 :kernel]\n    transform src src sepia-filter\n    imshow src\n    waitKey 0\n    destroyAllWindows\n]\n```\n\n### Using shared buffer\n\nWhen constructing matrices from a Rebol's binary or vector value, the buffer may be shared.\n```rebol\nwith cv [\n    ;; allocate vector for a grayscale image of size 320x200\n    data: #(uint8! 64000)\n    ;; make an OpenCV metrix using the shared data\n    img: Matrix [320x200 :data]\n    ;; do some animation...\n    forever [\n        ;; manipulate the matrix data using Rebol code with the direct access\n        forall data [data/1: random 255]\n        ;; display the modified image using OpenCV\n        imshow img\n        if 0 \u003c waitKey 10 [break]\n    ]\n]\n```\n\n### Saving video from the camera\n\n```rebol\nwith cv [\n    ;; initialize video input from a file...\n    ;cam: VideoCapture %test.mp4\n    ;; or from a camera device...\n    cam: VideoCapture 0\n    unless cam [print \"Failed to initialize VideoCapture\" quit]\n\n    ;; resolve input frame size...\n    size: as-pair get-property cam CAP_PROP_FRAME_WIDTH\n                  get-property cam CAP_PROP_FRAME_HEIGHT\n\n    print [\"Input frame size:\" size]\n\n    ;set-property cam CAP_PROP_POS_FRAMES 2000.0 ;; can be used to set position in the video (file input)\n\n    if frame: read :cam [\n        ;; initialize VideoWriter (when 0 is used as codec parameter, than the output will be MJPG)\n        out: VideoWriter %tmp/out.avi 0 24 size\n        unless out [print \"Failed to initialize VideoWriter!\" quit]\n\n        ;; grab 50 frames maximum...\n        loop 50 [ \n            read/into :cam :frame    ;; reusing existing frame\n            write out :frame         ;; append the frame to the output video\n            imshow :frame            ;; and also show it in the window\n            if pollKey = 27 [break]  ;; exit on ESC key\n            wait 0.01                ;; let Rebol breath as well\n        ]\n        destroyAllWindows\n    ]\n    free :out  ;; release VideoWriter\n    free :cam  ;; release VideoCapture\n]\n```\n\nVideoWriter expects integer for its codec input. It is possible to use function like this for the conversion:\n```rebol\nfourcc: func[\n    \"Converts fourcc codec integer to string and back\"\n    codec [any-string! binary! number!]][\n    either number? codec [\n        to string! reverse trim to binary! to integer! codec\n    ][\n        to integer! reverse to binary! codec\n    ]\n]\nfourcc \"avc1\"    ;== 828601953\nfourcc 828601953 ;== \"avc1\"\n```\n\n### Computing absolute difference between 2 video frames\n\n`absdiff` is useful when tracking a moving objects (or to produce nice psychedelic video effects:).\n```rebol\nwith cv [\n    cam: VideoCapture 0\n    unless cam [print \"Failed to initialize VideoCapture\" quit]\n    if all [\n        frame1: read :cam       ;; try to get the first frame\n        frame2: Matrix :frame1  ;; make matrices with the same size and type\n        result: Matrix :frame1  ;; for reuse later\n    ][\n        print \"Press any key to quit.\"\n        forever [ \n            read/into :cam :frame1          ;; get first frame\n            if 0 \u003c waitKey 50 [break]       ;; wait some time\n            read/into :cam :frame2          ;; get second frame\n            absdiff :frame1 :frame2 :result ;; compute absolute difference\n            imshow :result                  ;; display it\n            if 0 \u003c waitKey 50 [break]       ;; for some time\n        ]\n        destroyAllWindows\n    ]\n    free :cam  ;; release VideoCapture\n]\n```\n\n### Encode and decode QR codes\n\n```rebol\nwith cv [\n    mat: qrcode-encode \"Hello Rebol, hello OpenCV!\"\n    mat: resize/with :mat 600% INTER_NEAREST\n    ;; display the result...\n    imshow :mat\n    waitKey 0\n    destroyAllWindows\n    ;; save as PNG image...\n    imwrite %tmp/test-qrcode.png :mat\n    ;; decode QRCode from file:\n    probe qrcode-decode %tmp/test-qrcode.png\n]\n```\n\n\n* * * *\nThis file was generated using [examples.r3](examples.r3) script.\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Foldes%2Frebol-opencv","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Foldes%2Frebol-opencv","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Foldes%2Frebol-opencv/lists"}