Python Pillow ImageChops.difference always None - python-3.x

I'm trying to compare screenshots of 2 interactive maps. The screenshots are taken with Selenium and using Pillow to compare.
...
from selenium.webdriver.common.by import By
from selenium import webdriver
from io import BytesIO
from PIL import ImageChops, Image
...
png_bytes1 = driver.find_element(By.CSS_SELECTOR, "body").screenshot_as_png
png_bytes2 = driver2.find_element(By.CSS_SELECTOR, "body").screenshot_as_png
img1 = Image.open(BytesIO(png_bytes1))
img2 = Image.open(BytesIO(png_bytes2))
diff = ImageChops.difference(img1, img2)
print(diff.getbbox())
But diff is always blank. I manually used img1.show() and img2.show() to obtain the images below. diff.show() is always blank and diff.getbbox() prints None. What am I doing wrong and is there a better way of doing it?
Update: It works if I first save these images as jpg. Anyone have ideas why?

It seems ImageChops.difference() will only work if the image parameters are Image objects. PNG files are PngImageFile objects, with an RGBA mode for an extra alpha layer and need to be converted using converted_img1 = img1.convert('RGB').

Related

Scrape Product Image with BeautifulSoup (Error)

I need your help. I'm working on a telegram bot which sends me all the sales from amazon.
It works well but this function doesn't work properly. I have always the same error that, however, blocks the script
imgs_str = img_div.img.get('data-a-dynamic-image') # a string in Json format
AttributeError: 'NoneType' object has no attribute 'img'
def take_image(soup):
img_div = soup.find(id="imgTagWrapperId")
imgs_str = img_div.img.get('data-a-dynamic-image') # a string in Json format
# convert to a dictionary
imgs_dict = json.loads(imgs_str)
#each key in the dictionary is a link of an image, and the value shows the size (print all the dictionay to inspect)
num_element = 0
first_link = list(imgs_dict.keys())[num_element]
return first_link
I still don't understand how to solve this issue.
Thanks for All!
From the looks of the error, soup.find didn't work.
Have you tried using images = soup.findAll("img",{"id":"imgTagWrapperId"})
This will return a list
Images are not inserted in HTML Page they are linked to it so you need wait until uploaded. Here i will give you two options;
1-) (not recommend cause there may be a margin of error) simply; you can wait until the image is loaded(for this you can use "time.sleep()"
2-)(recommend) I would rather use Selenium Web Driver. You also have to wait when you use selenium, but the good thing is that selenium has a unique function for this job.
I will show how make it with selenium;
from selenium import webdriver
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
from selenium.common.exceptions import TimeoutException
browser = webdriver.Chrome()
browser.get("url")
delay = 3 # seconds
try:
myElem = WebDriverWait(browser, delay).until(EC.presence_of_element_located((By.ID, 'imgTagWrapperId')))# I used what do you want find
print ("Page is ready!")
except TimeoutException:
print ("Loading took too much time!")
More Documention
Code example for way 1
Q/A for way 2

Why saving Image object is not the same for thumbnail() and resize()?

from PIL import Image, ImageEnhance, ImageFilter
image = "Ash and Pikachu.png"
image = Image.open(image)
images = image.thumbnail((400, 320)) # thumbnail() works by changing the var name
# image = image.thumbnail((400, 320)) # gives error by keeping same var name
image.save("NewImage.png")
The above code will convert the image to thumbnail as expected. But by replacing thumbnail() with resize(), it just copies and saves the source image with new name.
from PIL import Image, ImageEnhance, ImageFilter
image = "Ash and Pikachu.png"
image = Image.open(image)
# images = image.thumbnail((400, 320)) # doesn't throw errors but doesn't resize the image
image = image.resize((400, 320)) # resize() works by keeping same var name
image.save("NewImage.png")
I'm not using both at the same time, but just wanted to point out where I'm facing issues. Anyhow, can I use the same code to save images in both thumbnail() and resize()?
Let's see the documentation on Image.thumbnail:
Note that this function modifies the Image object in place.
And, the documentation on Image.resize states:
Returns a resized copy of this image.
So, the correct usage of both methods including saving looks like this:
from PIL import Image
image = Image.open('path/to/your/image.png')
image.thumbnail((200, 200))
image.save('thumbnail.png')
image = Image.open('path/to/your/image.png')
image = image.resize((200, 200))
image.save('resize.png')
Both save a (200, 200) version of the input image. Notice: There's no reassignment of image in the first case, but in the second. That's in line with your codes. Inspect images in your first case, it's no proper Image object (it's None actually), but saving image still works, because Image.thumbnail worked in place. In your second case, you explicitly reassign image, which is correct here.
----------------------------------------
System information
----------------------------------------
Platform: Windows-10-10.0.16299-SP0
Python: 3.8.5
Pillow: 8.0.1
----------------------------------------

Opening Image from Website

I was trying to make a simple program to pull an image from the website xkcd.com, and I seem to be running into a problem where it returns list object has no attribute show. Anyone know how to fix this?
import requests
from lxml import html
r = requests.get("http://imgs.xkcd.com/comics/self_driving_issues.png")
tree = html.fromstring(r.content)
final = tree.xpath("""//*[#id="comic"]/img""")
final.show()
Your call to requests.get is retrieving the actual image, the byte code for the png. There is no html to parse or search for with xpath.
Note here, the content is bytes:
r = requests.get("http://imgs.xkcd.com/comics/self_driving_issues.png")
print(r.content)
b'\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x02\xe4\x00\x00\x01#\x08\x03\x00\x00\x00M\x7f\xe4\xc6\x00\x00\x00\x04gAMA\x00\x00\xb1\x8f
Here you see that you can save the results directly to disk.
import requests
r = requests.get("http://imgs.xkcd.com/comics/self_driving_issues.png")
with open("myimage.png", "wb") as f:
f.write(r.content)
[Edit] And to Show the image (you will need to install pillow.)
import requests
from PIL import Image
import io
r = requests.get("http://imgs.xkcd.com/comics/self_driving_issues.png")
img = Image.open(io.BytesIO(r.content))
img.show()

Storing matplotlib images in S3 with S3.Object().put() on boto3 1.5.36

Amongst other things I am drawing plots using matplotlib, which I would like to immediately store as S3 objects.
According to the answers provided in this and this other question and the fine manual, I need S3.Object.put() to move my data into AWS and the procedure should be along the lines of
from matplotlib import pyplot as plt
import numpy as np
import boto3
import io
# plot something
fig, ax = plt.subplots()
x = np.linspace(0, 3*np.pi, 500)
a = ax.plot(x, np.sin(x**2))
# get image data, cf. https://stackoverflow.com/a/45099838/1129682
buf = io.BytesIO()
fig.savefig(buf, format="png")
buf.seek(0)
image = buf.read()
# put the image into S3
s3 = boto3.resource('s3', aws_access_key_id=awskey, aws_secret_access_key=awssecret)
s3.Object(mybucket, mykey).put(ACL='public-read', Body=image)
However, I end up with a new S3 object with content-length zero.
The following gives me a new S3 object with content-length 6.
s3.Object(mybucket, mykey).put(ACL='public-read', Body="foobar")
When I put the next line, I end up with content in the S3 object, but its not a usable image:
s3.Object(mybucket, mykey).put(ACL='public-read', Body=str(image))
I can make it work by going through an actual file, like this:
with open("/tmp/iamstupid","wb") as fh:
fig.savefile(fh, format="png")
s3.Bucket(mybucket).upload_file("/tmp/iamstupid", mykey)
So it seems to work. I am just unable to use the interface correctly. What am I doing wrong? How can I achieve my goal using S3.Object.put()
I was able to resolve it. I found the answer in this question. Its a Python3 thing. From what I understand Python3 "usually" works with unicode. If you want single bytes you have to be explicit about it. The correct usage therefore is
s3.Object(mybucket, mykey).put(ACL='public-read', Body=bytes(image))
I find this a bit strange, since buf.read() is already supposed to return an object of type bytes but I did stop wondering, b/c it works now.

STEP file reading issue in Python

I am using Python3.4.2 and pythonOCC-0.16.0-win32-py34.exe to draw components. Every components are rendered properly with one defined color but that is not look like a real world component.
Above image is my Python implementation which generate 3D image from STEP file with one color.
Below image is rendered one of my windows software and there I have used Step file. I want to render component same as look like in below image so its look like a real world component.
Is there any way to get correct colored output in Python by read STEP file? I have searched a lot but didn't get a way to implement it. Please help me to go in forward direction.
from future import print_function
import sys
#from OCC.STEPCAFControl import STEPCAFControl_Reader
from OCC.STEPControl import STEPControl_Reader
from OCC.IFSelect import IFSelect_RetDone, IFSelect_ItemsByEntity
from OCC.Display.SimpleGui import init_display
from OCC.Display.WebGl import threejs_renderer
step_reader = STEPControl_Reader()
status = step_reader.ReadFile('./models/test.STEP')
if status == IFSelect_RetDone: # check status
failsonly = False
step_reader.PrintCheckLoad(failsonly, IFSelect_ItemsByEntity)
step_reader.PrintCheckTransfer(failsonly, IFSelect_ItemsByEntity)
ok = step_reader.TransferRoot(1)
_nbs = step_reader.NbShapes()
aResShape = step_reader.Shape(1)
else:
print("Error: can't read file.")
sys.exit(0)
#display, start_display, add_menu, add_function_to_menu = init_display()
#display.DisplayShape(aResShape, update=True)
#start_display()
my_renderer = threejs_renderer.ThreejsRenderer(background_color="#123345")
my_renderer.DisplayShape(aResShape)
The above code is used for read STEP file using Python.

Resources