I want to have a webpage function as a viewer for the webcam.
In Genie, this is how I can do it without writing anything to disk and very low delay:
using JpegTurbo, Observables, VideoIO
using GenieFramework
@genietools
const baseurl = "/frame"
const refresh = Observable(true)
# refresh the image in the browser every 0.1 seconds
Timer(1; interval = 0.1) do _
refresh[] = !refresh[]
end
cam = opencamera()
img = Ref(read(cam))
const w = size(img[], 1)
# fetch fresh frames from the camera as quickly as possible
Threads.@spawn while isopen(cam)
read!(cam, img[])
sleep(0.01)
end
# avoid writing to disk
route("/frame") do
respond(String(jpeg_encode(img[])), :jpg)
end
@app begin
@out imageurl = baseurl
@onchange refresh begin
# add an (invalid) anchor to the imagepath in order to trigger a reload in the Quasar/Vue backend
imageurl = string(baseurl, "#", Base.time_ns())
end
end
function ui()
[imageview(src=:imageurl, basic=true, style="max-width: $(w)px")]
end
@page("/", ui)
Server.up()
yes Oxygen is perfectly capable to deliver a similar result. However, the scope of Oxygen is smaller than that of Genie, there is no counterpart to Stipple, so we need to write the front end in HTML and JavaScript ourselves. Usually you would serve those files statically, but to have everything in one file, I provide it as a string within my Julia code.
A technical difference to your version is also that the above code is not using websockets, but HTTP requests. So there is a bit more network overhead in the Oxygen version. But it might not relevant compared to the payload size, I didn’t notice any difference.
This is pretty cool. So theoretically if one knew how to program in JavaScript, one could reproduce some of what Stipple gives us “for free”.
One point of improvement (for any future reader), is that in the asynchronous loop where we update the img container, there is no reason to wait 1/30 seconds – that actually introduces a lag in the video (for me at least). It’s much better to either have a sleep(0.001) for instance, or just a yield():
# fetch fresh frames from the webcam
Threads.@spawn while isopen(cam)
read!(cam, img[])
yield()
end
If things get more complex (not “just” a dashboard application), you probably will need to write the frontend in HTML / CSS / JavaScript. Then for the backend Genie, Oxygen or plain HTTP.jl seem to be the best Julia options currently.
The part of reading the images from the webcam, depends on the default of the actual hardware, resp. its default framerate, I think. I have no lag with 1/30 on my desktop, but on my notebook. In the docs, they sleep dependent on the frame rate (Reading Videos · VideoIO.jl).