Closed donbing closed 2 years ago
Thanks for pointing this out, you're right there is definitely an issue there.
I think I have it figured out but if you could please run a test on your system that would be great. I don't have your specific device but using the omni_epd.mock
device with a custom palette I was able to to determine two different things were happening during the color filtering functions:
RGB
mode need to be converted to that mode before filteringThe result of both of these was that white was essentially being interpreted as black on the image. On the EPD this meant that anything black would be drawn as white when in the 3 color mode. BW only images were unaffected since they aren't using color filtering, simply converted to 2 color.
The fix should be in this commit. If you could update your local install and rebuild the library it should work now - I hope!
I'm happy to test on my 7.5b_V2 if needed. I also have a 4.2c screen on it's way. Hopefully this wasn't caused by the testing we did earlier.
I'm going to test on my 2.7 waveshare and 4.2 inky once I can get numpy to download correctly, the setup seems to be getting stuck there for me atm.
ok, got it to install correctly on my Pi4, but I get the following error from the test util
pi@raspberrypi:~/omni-epd $ omni-epd-test -e inky.what_red
Loaded inky.what_red with width 400 and height 300
Drawing rectangle of width 300.0 and height 225.0
Traceback (most recent call last):
File "/usr/local/bin/omni-epd-test", line 10, in <module>
sys.exit(main())
File "/usr/local/lib/python3.7/dist-packages/omni_epd/test_utility.py", line 127, in main
test.draw()
File "/usr/local/lib/python3.7/dist-packages/omni_epd/test_utility.py", line 83, in draw
draw = self.__draw_rectangle(draw, self.epd.width, self.epd.height, 0, 0, .75, .25)
File "/usr/local/lib/python3.7/dist-packages/omni_epd/test_utility.py", line 58, in __draw_rectangle
imgObj.rectangle((rX, rY, rWidth + rX, rHeight + rY), outline=ImageColor.getrgb("black"), width=2)
TypeError: rectangle() got an unexpected keyword argument 'width'
both my pi zero2s are getting stuck at the numpy install
pi@comitup-624:~/omni-epd $ sudo pip3 install .
Looking in indexes: https://pypi.org/simple, https://www.piwheels.org/simple
Processing /home/pi/omni-epd
Installing build dependencies ... done
Collecting Pillow (from omni-epd==0.2.7b1)
Using cached https://www.piwheels.org/simple/pillow/Pillow-9.0.1-cp37-cp37m-linux_armv7l.whl
Collecting waveshare-epd@ git+https://github.com/waveshare/e-Paper.git#subdirectory=RaspberryPi_JetsonNano/python&egg=waveshare-epd from git+https://github.com/waveshare/e-Paper.git#subdirectory=RaspberryPi_JetsonNano/python&egg=waveshare-epd (from omni-epd==0.2.7b1)
Cloning https://github.com/waveshare/e-Paper.git to /tmp/pip-install-lsb5upt7/waveshare-epd
Collecting inky[rpi] (from omni-epd==0.2.7b1)
Using cached https://files.pythonhosted.org/packages/9e/db/ab689bad7935ce13cf0356bf2ad6e13ee2a115f3a33de7596676ad464921/inky-1.3.1-py3-none-any.whl
Collecting hitherdither@ git+https://github.com/hbldh/hitherdither from git+https://github.com/hbldh/hitherdither (from omni-epd==0.2.7b1)
Cloning https://github.com/hbldh/hitherdither to /tmp/pip-install-lsb5upt7/hitherdither
Requirement already satisfied: RPi.GPIO in /usr/lib/python3/dist-packages (from waveshare-epd@ git+https://github.com/waveshare/e-Paper.git#subdirectory=RaspberryPi_JetsonNano/python&egg=waveshare-epd->omni-epd==0.2.7b1) (0.7.0)
Collecting numpy (from waveshare-epd@ git+https://github.com/waveshare/e-Paper.git#subdirectory=RaspberryPi_JetsonNano/python&egg=waveshare-epd->omni-epd==0.2.7b1)
Using cached https://files.pythonhosted.org/packages/c2/a8/a924a09492bdfee8c2ec3094d0a13f2799800b4fdc9c890738aeeb12c72e/numpy-1.21.5.zip
Installing build dependencies ... done
Requirement already satisfied: spidev in /usr/lib/python3/dist-packages (from waveshare-epd@ git+https://github.com/waveshare/e-Paper.git#subdirectory=RaspberryPi_JetsonNano/python&egg=waveshare-epd->omni-epd==0.2.7b1) (3.5)
Collecting smbus2 (from inky[rpi]->omni-epd==0.2.7b1)
Using cached https://files.pythonhosted.org/packages/c8/bf/62ef029fb7077fc87c3539f7365859bccc6cedb2bb20796b737b788c8d09/smbus2-0.4.1-py2.py3-none-any.whl
Building wheels for collected packages: omni-epd, waveshare-epd, hitherdither, numpy
Running setup.py bdist_wheel for omni-epd ... done
Stored in directory: /root/.cache/pip/wheels/45/58/69/3b9460b88cc0aee0ed20ed6e27c300b100ddac04e4d43d261f
Running setup.py bdist_wheel for waveshare-epd ... done
Stored in directory: /tmp/pip-ephem-wheel-cache-d_a3y7oj/wheels/4b/4d/5a/fc28bbb9a5787d248880f44b98931eab72d5d884de27a4845b
Running setup.py bdist_wheel for hitherdither ... done
Stored in directory: /tmp/pip-ephem-wheel-cache-d_a3y7oj/wheels/2d/7f/db/ba7bd2317fae19a700c11482577b6115f907ff836b2221c184
Running setup.py bdist_wheel for numpy ... /
hm, the pi4 has PIL 4.2.1
installed. that's why :(
pi@raspberrypi:~/omni-epd $ python3
Python 3.7.3 (default, Jan 22 2021, 20:04:44)
[GCC 8.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> PIL.version.__version__
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'PIL' is not defined
>>> import PIL
>>> PIL.version.__version__
'4.2.1'
but setup indicates that it found 5.4.1
pi@raspberrypi:~/omni-epd $ sudo pip3 install .
Looking in indexes: https://pypi.org/simple, https://www.piwheels.org/simple
Processing /home/pi/omni-epd
Installing build dependencies ... done
Requirement already satisfied: Pillow in /usr/lib/python3/dist-packages (from omni-epd==0.2.7b1) (5.4.1)
more /usr/lib/python3/dist-packages/PIL/_version.py
::::::::::::::
# Master version for Pillow
__version__ = '5.4.1'
I think setup.cfg needs to pin it's dependencies.. PR to fix that here https://github.com/robweber/omni-epd/pull/59
so after fixing that, and running the draw_image example, I only get black pixels on the red inkywhat
pi@raspberrypi:~/omni-epd/examples/basic_example $ python3 draw_image.py
Loading display
Loading image
Writing to display
i added the following ini file to the example
[EPD]
type=inky.what_red
mode=red
[Display]
rotate=180
[Image Enhancements]
contrast=1.5
sharpness=2
and now i get bits of red in the pic!
i'll try with the waveshare a bit later, to verify that it works there too.
looks right on the waveshare_epd.epd2in7b_V2
too!
altho.. why does one have a black background and one white :)
Looks like waveshare forgot to invert the black layer.
Interesting....it's easily fixed by flipping how the image is generated but wonder how many other displays require the inverted image?
Is this an oversight or intentional in the Waveshare driver do you think? If it's intentional we'll have to fix here.
@robweber Well I think I have an answer to that question and why I was seeing weird behavior on my 7.5b in the other issues. I went through every driver file in the WS lib and wow is it inconsistent (as you know). Anyway, I added to my already existing google sheet some additional info on if each driver does inverting in the buffer and then possibly again in the display methods.
For reference, here are the ways that they invert (or it seems). I really didn't dig into the code a ton. I just marked it if it appeared to be inverting the bits:
v1.1 - buf[int((x + y * self.width) / 8)] &= ~(0x80 >> (x % 8))
- invert
v1.2 - buf[int(x / 8) + y * linewidth] &= ~(0x80 >> (x % 8))
- invert
v2 - buf[int((x + y * self.width) / 4)] &= ~(0xC0 >> (x % 4 * 2))
- invert and grey to red
v3 - buf[i] ^= 0xFF
v4 - data_t = buf[Add]&(~(0xF0 >> ((newx % 2)*4)))
- 4" color
The above "versions" of inverting are what I reference in my sheet here (drivers tab) - https://docs.google.com/spreadsheets/d/1r4EKPGZvz_xQZA58WPyuTVPhXGKCWd7-Z-btqgaFfKw/edit#gid=1378358670
I tried to highlight differences from the norm, so outliers were more easily identified.
Sorry it took me so long to respond, but I wanted to have some concrete info before I speculated
I think it's probably an oversight. Usually, with e-paper 1 is black and 0 is white.
@aaronr8684 does waveshare's example display correctly?
On my 7.5b? Yea. I ran a bunch of tests with Omni with it as well. I know that a black image pixel is on for the black, red, or yellow ePaper pixels, but it seems like every driver handles it in a different way. I'm not really sure at this point what the best approach is for Omni, but I figured the first step was documenting the differences.
No, the 2.7b. If the waveshare example displays incorrectly as well (the background should be white), then the line I linked to above should be changed to self.send_data(~imageblack[i])
Waveshare's drivers could really use some work.
@missionfloyd I don't have the 2.7b. Yes, a little consistency would be nice. They really should have one method of display per screen type. BW, R/Y, 4-Grey, Color
@donbing have you tried the v1 waveshare_epd.epd2in7b
driver?
@aaronr8684 the non-V2 version? if so, no. but I will
pi@raspberrypi:~/omni-epd/examples $ omni-epd-test -e waveshare_epd.epd2in7b -i PIA03519_small.jpg
Loaded waveshare_epd.epd2in7b with width 176 and height 264
... the command never terminates, and the screen is not refreshed (my device is a v2)
I did a bit more testing..
Given this omni-epg.ini
[EPD]
type=waveshare_epd.epd2in7b_V2
mode=red
When I execute
omni-epd-test -e waveshare_epd.epd2in7b_V2 -i PIA03519_small.jpg
Then I get this image
Given this omni-epg.ini
[EPD]
type=waveshare_epd.epd2in7b_V2
When I execute
omni-epd-test -e waveshare_epd.epd2in7b_V2 -i PIA03519_small.jpg
Then I get this image
Testing on a 4.2" red inkywhat gives back backgrounds for both red and BW.
Testing on a 5.6" inky impression gives black backgrounds for both color and BW modes too
I also have a waveshare pico-epaper-4.2. But that's a faff to wire up, so I'll leave it unless anyone specifically wants it testing..
@donbing Can you modify the Omni-EPD library code in /usr/local/lib/python<ver>/dist-packages/omni_epd/displays/waveshare_display.py
on line 214 to img_black.putpalette((255, 255, 255, 0, 0, 0))
You'll need to change <ver>
to your version, either 3 or 3.X
Does that fix the background in "red" mode?
In summary, the issue lies in how the individual libraries handle the image to buffer conversion. The correct result (that you see in some waveshare libraries and all tested inky libraries) is to convert the black in an image (0x00) to ePaper black (0xFF). The Omni library is correctly generating the black background PIL image (see attached), but some of the libraries are not inverting it correctly before sending it to the screen. The correct fix is to create a PR in the official repo for waveshare, but the faster fix might be to figure out which screens are incorrect and break them out into a separate class implementation.
img_black.show()
output:
I also have a waveshare pico-epaper-4.2. But that's a faff to wire up, so I'll leave it unless anyone specifically wants it testing..
Yea my 5.65" color is a pico hat, that I have to wire up to work with my pi4. After I did it once, I just snapped a photo so that I didn't have to work with the pin table and could just visually see the pinout. I've attached it if it helps. Ignore the 5V/Ground fan wires 😄
Visual Pinout (uses 3.3V):
@aaronr8684 I changed the code you suggested. And yep, I now get black backgrounds for both black and red modes
@donbing I assumed that would work. It shouldn't have any effect on the bw mode, but it's helpful to test. Thanks!
The epd5in83c
also behaves like your 2.7b_V2 so that's at least 2 screens that have been confirmed to have a different implementation. I have someone testing the 2.7b_V1 to see if it's the same.
I think at this point, we'll have to wait to see how @robweber would like to approach it. Once a direction is given, I'm happy to do the coding if no one has the time at the moment. I'm working on adding Omni to another project, so I'm glad that we are working through these bugs first to make testing downstream a little smoother.
@aaronr8684 I changed the code you suggested. And yep, I now get black backgrounds for both black and red modes
If you're curious, this is what the img_black.show()
output looks like with the code you changed:
Glad I could help 👍, I'd been looking at adding omni to my crypto-ticker project.
I really appreciate the pic btw, I've got the 4.2" pico red up and running with the waveshare_epd.epd4in2b_V2
driver 🤩.
You'll be happy to know that waveshare_epd.epd4in2b_V2 also suffers from the black-conversion issue 🤕
Glad I could help 👍, I'd been looking at adding omni to my crypto-ticker project.
Yea I was taking a look at your project and really like the look of it. I was thinking it would be cool to bring in that functionality to the epd_display / PaperPi project as a plugin #59. It would be a great addition and offers a lot more features over the current crypto option. Maybe someday when all the other work is done 🤷♂️
The correct fix is to create a PR in the official repo for waveshare, but the faster fix might be to figure out which screens are incorrect and break them out into a separate class implementation.
Really appreciate all the testing everyone is putting into this. I never would have thought a simple "red isn't showing up" issue would have ballooned so much. I think the best approach at this point might be to simply handle the screens that are incorrect. In the device mapping another key could be added to specify which ones and then use the correct palette filter to get the right image for the buffer. To be fair I hate this type of solution since as soon as Waveshare changes it will be out of date but it's a compromise on usability.
@aaronr8684 - that spreadsheet is nice, must have taken some time to dig into all the displays. Is that something I could link to on the Wiki of this project? More for informational than anything.
You'll be happy to know that waveshare_epd.epd4in2b_V2 also suffers from the black-conversion issue 🤕
I added another column to track which screens display correctly and which do not. More info the better, right? 😅
@aaronr8684 - that spreadsheet is nice, must have taken some time to dig into all the displays. Is that something I could link to on the Wiki of this project? More for informational than anything.
@robweber Absolutely! It's not at all "cleaned up" and although I tried to be as accurate as possible, I'm sure some of the code interpretation is probably wrong from my "at a glance" approach. I've change the read-only access to comment access so it can be more of a collaborative document. I'll also add the versions of inversion to the doc for easier reference.
@aaronr8684 Sorry, I responded to the wrong person.
@donbing Have you checked if waveshare's examples work right? It would help to know if it's our problem or theirs.
@donbing Sorry for the double ping. A photo would be nice if you run the waveshare test file on the 3rd test
np, here's a few snaps from the waveshare example.
@donbing Will you try one more thing with the Omni test file after making the following changes?
In /usr/local/lib/python<ver>/dist-packages/omni_epd/displays/waveshare_display.py
:
Replace this line (should be 214):
img_black.putpalette((0, 0, 0, 255, 255, 255))
With these:
img_black = img_black.convert('L')
threshold = 32
img_black = img_black.point( lambda p: 255 if p > threshold else 0)
img_black = img_black.convert('1')
And then share the results.
Thanks in advance!
I ran some tests with different threshold values...the higher the number the more black that will result. In the filtered image, the only difference seems to be whether the red is converted to black or white, which makes sense. I think the red in the original filtered image must be represented somewhere between 64 and 128, probably around 85 if I had to guess.
224 and 128:
64 and 32:
For bw images, 128 is probably good, but with a third color, I think the best results are going to be in the lower range.
Looks like the driver is correct. Are we sure 827321789983b9368f5e0f8d68421eec13bc6f40 didn't break something?
@donbing can you try changing lines 214 and 217 in waveshare_display.py
back to
img_black.putpalette((255, 255, 255, 0, 0, 0, 255, 255, 255) + (255, 255, 255)*253)
and
img_color.putpalette((255, 255, 255, 255, 255, 255, 0, 0, 0) + (255, 255, 255)*253)
respectively?
Alternatively, what if we convert back to RGB, then let the driver do it's thing?
Change line 219 to
self._device.display(self._device.getbuffer(img_black.convert("RGB")), self._device.getbuffer(img_color.convert("RGB")))
img_color.putpalette((255, 255, 255, 255, 255, 255, 0, 0, 0) + (255, 255, 255)*253)
line 217 is already identical to this?
I changed just line 214, to read
img_black.putpalette((255, 255, 255, 0, 0, 0, 255, 255, 255) + (255, 255, 255)*253)
however.. i then change it back to
img_black.putpalette((255, 255, 255, 0, 0, 0))
and I still get a black background, this is using the test_utility code
using
img_black.putpalette((0, 0, 0, 255, 255, 255))
results in a white background
@donbing were you able to make the changes I suggested?
@aaronr8684 will try now, 2 mins
Alrighty, so Given:
img_black = img_black.convert('L')
threshold = 32
img_black = img_black.point( lambda p: 255 if p > threshold else 0)
img_black = img_black.convert('1')
Then:
Looks like the driver is correct. Are we sure 8273217 didn't break something?
It's possible (probable even) but at this point it doesn't matter, because regardless of which way the putpalette is setup, it break some screens. I'm trying to test some solutions that will work across all screen, and based on donbing's last reply, that last test might have done that. @missionfloyd do you have any red or yellow screens that you can test with the fix of replacing the putpalette?
That's great @donbing, that means that this solution works on yours and mine which have had the opposite results with the putpalette options we've tested so far.
For completeness of the solution (and to incorporate some changes I've tested since), can you replace the entire else
block starting around line 208 with the following code:
# apply the color filter to get a 3 color image
image = self._filterImage(image)
image = image.convert('L')
# create copies to send separately to the display method
img_black = image.copy()
img_color = image.copy()
# switch grey colors to black or white based on threshold
threshold = 16
img_black = img_black.point( lambda p: 255 if p > threshold else 0 )
# switch grey colors to black or white based on hardcoded thresholds. Should work with red or yellow screens
img_color = img_color.point( lambda p: 0 if p > 18 and p < 235 else 255 )
self._device.display(self._device.getbuffer(img_black), self._device.getbuffer(img_color))
Hi, I've got a
waveshare_epd.epd2in7b_V2
that i'm trying to show a picture on.when i configure the device for
red
mode using an ini file all I get is red pixels on the display, should it also display black?e.g: source image: resized and converted to RBG: displayed: