samedi 30 novembre 2024

Using json files with Red and Rebol

Json is an open standard file format and data interchange format that uses human-readable text to store and transmit data objects made of name–value pairs and arrays. Json and Red or Rebol are very similar in the way they represent data. I sincerely believe that the development of Json benefited from all the work done by Carl Sassenrath when he developed Rebol 2.

Between 2020 and 2023, I developed a major program at the Raymond Poincaré hospital (https://uniter2p2.fr/) with Red, which used a thermal camera to measure the body temperature of newborn babies. This was a bit tricky, as we had to extract the baby's body coordinates from the thermal image in order to measure body temperature. To do this, I used semantic segmentation algorithms such as those proposed by Ayoola Olafenwa with her PixelLib library (https://pixellib.readthedocs.io/en/latest/).

In this program, I also included an export of the babies' images in .jpg format, as well as an export of the baby's body coordinates in .json format. The idea was to be able to use this data with annotation tools such as labelMe (https://github.com/wkentaro/labelme).

A few days ago, I had to return to this data to prepare a publication. Bad surprise: PixelLib and LableMe no longer work with recent versions of macOS and Apple's new Silicon processors.

Fortunately, with Red (or Rebol3) I was able to solve the problem with a few lines of code. 

Red [
Needs: 'View
]
;--we use func: all words are global
isFile?: none
loadImage: does [
canvas/image: none
clear f/text
clear info/text
isFile?: no
tmpf: request-file/filter ["jpeg Files" "*.jpg"]
unless none? tmpf [
jpgFile:  tmpf
jsonFile: copy jpgFile
replace jsonFile ".jpg" ".json"
canvas/image: load tmpf
f/text: form tmpf
isFile?: yes
]
]
getCoordinates: func [
f [file!]
][
f: read f ;--Red read json file as string
replace f  ",." ",0." ;--in case of missing 0 values
js: load-json f ;--json string -> redbol map object
keys: keys-of js ;--a block of keys
version: select js keys/1 ;--labelMe version
flags: select js keys/2 ;--none
shapes: select js keys/3 ;--coordinates are here as a block of length 1
imagePath: select js keys/4 ;--jpeg file
imageData: select js keys/5 ;--none
imageHeight: select js keys/6 ;--imageHeight
imageWidth: select js keys/7 ;--imageWidth

bPoints: copy [] ;--block for coordinates
;--Thanks to Oldes for s/points
foreach s shapes [
infos: rejoin ["Label: " s/label " ID: " s/group_id " Shape Type: " s/shape_type]
foreach p s/points [
if all [p/1 > 0.0 p/2 > 0.0] [append bPoints to pair! p]
]
]
bPoints ;--returned coordinates
]
showCoordinates: func [f [file!] b [block!]
][
code: compose [
fill-pen 255.0.0.120 ;--draw command
pen 0.0.0.100 ;--draw command
line-width 1 ;--draw command
polygon ;--draw command
]
img: load f
bb: make image! reduce [3x3 black]
foreach p b [
change at img p bb ;--draw coordinates in image
append code p ;--append polygons
]
]
view win: layout [
title  "Neonate Labelling"
button "Load a Neonate File (.jpg)" [loadImage]
button "Draw Extracted Body" [
if isFile? [
showCoordinates jpgFile getCoordinates jsonFile
info/text: infos
canvas/image: draw img code
]
]
info: field 250
pad 15x0
button "Quit" [Quit]
return
canvas: base 640x480 white
return
f: field 640
do [f/enabled?: info/enabled?: no]
]

And the result: