Rick's b.log - 2023/10/01 |
|
It is the 21st of November 2024 You are 3.144.42.174, pleased to meet you! |
|
mailto:
blog -at- heyrick -dot- eu
Let's begin the tenth month with release zero-ten of SimpleSeq.
In this version, the following things have been done:
Here's the download:
The dumb thumbnail picture is a nod to how all the mainstream videos have pictures of the people pulling dumb looking "reaction faces". It's not just MrBeast or a dozen influencers I've never heard of, Dr. Becky and Sabine Hossenfelder are getting in on the crazy-face action.
And, yeah, 26°C isn't that hot, it's just unusual for this time of year. But a "meh" face is kind of boring.
It was a composite of a frame from the start of the video proper, and a photo of my holding an asian parasol that I quickly ran outside and took. I used the Background Eraser app to remove the background of the photo of me, and then spent a ten minutes putting the parasol back into the image. I then used the Polish photo tweaker app to merge the picture of me into the through-the-window shot, and paste in an emoji just because.
The secret is to look to see how big the JPEG is, and create a sprite that is a sixteenth of that size.
SpriteOp 9 creates the sprite area. As we know we're going to be using full RGB, we can work out the size by simply multiplying the pixels by four, then adding in 512 bytes to cover headers and such. More than we need, but better that than not enough.
The next task is to switch to redirecting output to the sprite.
Here, the save area size is read and then the sprite is redirected.
Next, if the scaling ratio is set to
That final SpriteOp undoes the VDU redirection, so everything is normal now, and we have an image like this in memory:
It might seem small, but it's all we need.
Now, for speed, we're going to call
Now, the original image is 3390×2714, or 9.2 million pixels. Our rescaled image is only 211×169, or 35,659 pixels. It's much smaller but it's enough to determine what we need.
Here's the scanner:
The first line gets us an address for the sprite, and the SYS &2E is the numerical way of calling OS_SpriteOp.
What we're doing is simply whizzing through the image to see what colour every pixel is, and this (in the form &00BBGGRR) is split into red, green, and blue parts, which are cumulative. There's no need to worry about overflow as all pixels returning 255 adds up to only 9,093,045. A 32 bit value lets us count up to 4,294,967,295. Performing this calculation on the full size image would only add up to 2,346,117,300 so we don't have to worry about overshooting.
For that image, our final results are 5,244,322 red, 5,809,786 green, and 6,906,236 blue.
I've split this line C-like as it's long. What we're doing is dividing each of the red, green, and blue by the number of pixels to give us an average for each colour.
Essentially we've converted the image to a value of 243 that represents the intensity of the image. It isn't a grey dot because we ORed the values, we didn't add them and divide by three for an average (that would be something like 168 off the top of my head).
Then, if the intensity is over 140 we'll go with white on black, otherwise it's black on white.
You could shave off a few centiseconds (to 2.6s) by rewriting the loop like this:
And you could shave off nearly a second (2.1s) by ditching OS_SpriteOp completely and poking around the sprite memory. As we created a 16M colour sprite, we know the arrangement of each word.
However, this is one of the larger images. The rest are smaller so take less time, and using legal OS calls is better (in karmic sense) than directly poking around memory regions.
SimpleSeq v0.10
SimpleSeq playing music.
^I
to insert a bar before the current bar, and ^D
to delete the current bar. You can choose to apply this to the current channel only (leaving the others untouched), or to all channels.
Just in case. ;)
(*)
" to the filename if there's a backup.
While it's not made explicitly clear, you can, therefore, enter the filename
and then add "/bak
" to the end to load the backup copy, should you wish to revert to the previous version.
Note that this happens every save, so saving twice would effectively render the backup file the same as the main file.
For RISC OS 5 machines with MIDIScorchio!
It got up to 26°C today. The average is ten degrees lower. It was also plenty warm yesterday, so I tackled the brambles again.
It was a bit of a rush job as I'd already uploaded the video. So the edges of me are a little fuzzy. But, then, Background Eraser does tend to make fuzzy images. It's not pixel perfect, it does weird things with resizing the source image. But it's a hell of a lot less bother than firing up the PC and cutting out the wanted parts in a photo editor.
ChooseBD colour determination
Talking of how things are made, I thought I'd explain how ChooseBD works out what colour to use for the pinboard text.
SYS "XJPEG_FileInfo", 1, f$ TO ,, owid%, ohei%
wid% = owid% / 16
hei% = ohei% / 16
sz% = (wid% * hei% * 4) + 512
DIM spr% sz%
spr%!0 = sz%
spr%!8 = 16
SYS "OS_SpriteOp", ( 9 + 256), spr%
SYS "OS_SpriteOp", (15 + 256), spr%, "wrkspc", 0, wid%, hei%, 1 + (6 << 27) + (90 << 14) + (90 << 1)
SpriteOp 15 creates the sprite "wrkspc" within the sprite area. We use a sprite mode word to specify type 6 (16m colours) with 90 dpi X/Y. The dpi isn't important, but the OS will sulk if you don't specify something.
SYS "OS_SpriteOp", (62 + 256), spr%, "wrkspc" TO ,,,sasz%
DIM save% sasz%
SYS "OS_SpriteOp", (60 + 256), spr%, "wrkspc", save% TO , r1%, r2%, r3%
original:smaller
we can get SpriteExtend to render directly from the JPEG file into the sprite (using sprite redirection).
DIM scl% 16
scl%!0 = wid%
scl%!4 = hei%
scl%!8 = owid%
scl%!12= ohei%
SYS "JPEG_PlotFileScaled", f$, 0, 0, scl%, 0
SYS "OS_SpriteOp", (60 + 256), r1%, r2%, r3%
A little version of a big image.OS_SpriteOp
by number to save having BASIC keep on looking up the SWI name, and we're also going to refer to the sprite by address rather than name to save having the OS keep on looking up the sprite by name.
It does make a difference. On my ARMv7 Pi 2, it takes 4.1 seconds by name or 2.9 seconds by reference. That's quite a difference.
SYS "OS_SpriteOp", (24 + 256), spr%, "wrkspc" TO ,,addr%
red% = 0
green% = 0
blue% = 0
FOR yl% = 0 TO (hei% - 1)
FOR xl% = 0 TO (wid% - 1)
SYS &2E, (41 + 512), spr%, addr%, xl%, yl% TO ,,,,,col%
red% += ( col% AND 255)
green% += ( (col% >> 8) AND 255)
blue% += ( (col% >> 16) AND 255)
NEXT
NEXT
val% = (red% / (wid% * hei%) ) OR
(green% / (wid% * hei%) ) OR
(blue% / (wid% * hei%) )
This would be 147 for red, 163 for green, and 194 for blue (because there's lots of blue).
This is then merged together (using OR) to arrive at a value of 243. It's not quite correct as we're not applying any weighting to the colours, but it'll do for what we need it for.
R% = 0 : G% = 0 : B% = 0
H% = hei% - 1 : W% = wid% - 1
S% = spr% : A% = addr%
FOR Y% = 0 TO H% : FOR X% = 0 TO W%
SYS &2E,553,S%,A%,X%,Y% TO ,,,,,C%
R%+=(C% AND 255)
G%+=((C%>>8)AND255)
B%+=((C%>>16)AND255)
NEXT : NEXT
R% = 0 : G% = 0 : B% = 0
E% = hei% * wid% * 4
O% = addr% + 40
FOR L% = 0 TO E% STEP 4
C% = O%!L%
R%+=(C% AND 255)
G%+=((C%>>8)AND255)
B%+=((C%>>16)AND255)
NEXT
rob, 1st October 2023, 23:24 If you're going to be iterating over every pixel anyway, you could calculate the intensity separately for each possible icon location, assuming they are positioned on a grid, to cater for images with large bright and dark areas.Rick, 2nd October 2023, 06:53 Possible, but it's an all-or-nothing setting, you can't set the text colour per icon.
Accordingly, images with large light and dark areas may get things wrong.
I can't balance it as while I put my icons on the lower left, that's just me. Others may prefer the lower right...Rob, 2nd October 2023, 12:37 Ah, I wasn't sure if that were possible or not. Fair enough.David Pilling, 2nd October 2023, 12:57 If resources were limited you could access the JPEG data directly - it is in 8x8 blocks anyway.
If you wanted to make RISC OS do more of the work - is there a SWI for optimised palette conversion - Col Trans maybe or Change FSI. Alas scaling will be simple scaling just omitting rows and columns - otherwise you could set the destination as a 1 x 1 pixel.
Another thought is pick 20 pixels at random, how representative would they be of thousands of pixels. The scaling is this kind of thing, because there will be pathological images where the pixels chucked away are not typical.
Out on t'internet they compete to consume resources for this task - lets fire up a browser...
The solution above is fine, just that it begs the question 'can you over think this'.
Zerosquare, 2nd October 2023, 15:19 I don't understand what you're doing with that ORing of color values. Bitwise OR can be very sensitive to small variations, which is something you don't want in that case.
Example: 126 OR 127 = 127, and 128 OR 129 = 129, but 127 OR 128 = 255.Rick, 2nd October 2023, 19:48 Zerosquare: It's to bias it towards light.
Obviously there are pathological cases that will upset a normal averaging routine, for example, (R+G+B)รท3 would say an image that is full intensity green is only 33% grey. Perhaps using colour weighting, but as David suggests, it's easy to overthink things. ;)
I'm not looking for a "grey pixel to represent this image", I'm looking for a value to tell me if it's light or dark (to use the opposite for the text colour).
David: resources aren't limited, the reason I don't support sprite backdrops isn't because of a lack of memory, it's because of a lack of patience. I wanted something that could run quickly. Pasting a 1/16th JPEG into a sprite for examination met that criteria.
Why 1/16th? Because SpriteExtend is lame and does zero dithering, it simply loops plotting a pixel and then skipping a few. If you zoom up the little image, you'll see the jaggies are quite evident.
I'm not sure quite what would happen if I told it to render to a 1 pixel sized image. Prolly crash. ;)
Thing is, as I said to Rob earlier, there are plenty of cases where no algorithm would be ideal. Consider, for instance, a black and white checkerboard.
© 2023 Rick Murray |
This web page is licenced for your personal, private, non-commercial use only. No automated processing by advertising systems is permitted. RIPA notice: No consent is given for interception of page transmission. |