Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Subtle adjustments to dark pixels brightness #881

Open
wants to merge 4 commits into
base: main
Choose a base branch
from

Conversation

nlw0
Copy link

@nlw0 nlw0 commented Dec 10, 2023

This is some code I wrote after our very productive conversation in #880 . I'm not sure you'll be interested in it, just thought I might share the code because I'm happy with the results. If there's any interest to merge some part of it, please let me know how I can modify it. Otherwise just feel free to close the request.

I tried to make it generic at first, because in the end there's only a few variables I wanted to change. For example, it might be possible to have the PicoUnicorn class templated, and then you can simply select different values for BCD_FRAMES etc. But eventually that looked like too much work.

Some minor modifications are:

  • Having an explicit DISCHARGE_FRAMES, because increasing that is a simple way to make everything a little darker.
  • Separate set_pixel_ method lets the user input 16-bit linear values, skipping the LUT, and set_pixel simply uses the LUT and calls that.

The main modification is that we now have a FRAME_DELAY, which is the estimated extra time per bit-plane frame. In my experiments, 4 ticks was what I was most pleased with. This time is subtracted from the count of each frame, and in the first frame we have a count of zero. This is also the base time to compute everything, this delay defines our least significant bit in the binary code modulation.

Because I increased the minimum time, I found it natural to reduce the total number of bits to 12 instead of 14. This approximately preserves the time for a whole frame refresh, I think.

To go along with this alternative bit-depth, I also computed a new 12-bit LUT, and made it so there's a linear tapering at the beginning, so there are no levels "lost" to a same output value in the darkest range, which was the other thing I was looking forward to.

I'm happy to be able to do these customizations! Perhaps not many people are as concerned as me looking at almost imperceptible dark pixels, but if there's any interest, here is what I did.

Final note: here's some Julia code I used to compute the LUT: https://gist.github.com/nlw0/d020b67d954b9e4a98865aa3ceb9a343

@Gadgetoid
Copy link
Member

Thank you! This is some awesome work and sleuthing.

I think I'd be inclined- when I have some time to tinker- to A/B test our existing code versus this code across a range of patterns, colours and brightnesses and pick the better of the two. There will be an element of subjectivity involved, but it would be interesting to see what - if any - practical, visible difference there is.

IIRC We picked 14-bit gamma correction mostly so that 8-bit brightnesses could be gamma corrected without any loss of dynamic range. At a glance it looks like 12-bit also satisfies this requirement and I'm going to guess that it makes very little visual difference.

It doesn't help that the size of the display only lets us look at a subset of brightness levels simultaneously.

@nlw0
Copy link
Author

nlw0 commented Dec 13, 2023

I'm glad you find it interesting! I believe this patch may cause it to be slightly less bright, I'm not sure why. It would be easier to judge having two displays side by side to compare, or try it in the larger panels. My focus was in low brightness, though. Not sure a single implementation will be the best for all applications.

I think ideally we could have this class templated over a few parameters, basically the number of frames, time of each frame and the LUT. The original version is perfectly reproduced by one set of parameters, this implementation is another, and we also may enable eg a high frame rate binary mode, or a classic 8-bit table with 24-bit colors. If you think it's a good idea I may try to implement something like that, but it would be nice to figure out more details first.

@nlw0
Copy link
Author

nlw0 commented Dec 15, 2023

I refactored the code so that the class is templated, and takes a bunch of parameters. Doing that with a separate .cpp file is annoying because you need to add template everywhere in the method declarations, so I moved most of the code to the .hpp file. I'm doing this mostly to illustrate what it might look like, maybe there's a better way to do it. The main point is just that the whole class can stay almost the same, but then we can play around with those few parameters related to the bitplanes/frames timing, and the gamma/brightness LUT. Theoretically, the two examples should behave exactly like the original code, but we can now easily instantiate the class with the different timings, like the 12 bit one I think is nicer for my application.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants