Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Transparent screenshot with headless ChromeDriver via Selenium

To my understanding the ChromeDriver itself doesn't set the background, the CSS does. Therefore if the background is transparent, why am I not getting transparent screenshots?

This is the screenshot of the supposedly transparent website: Transparent background Same screenshot but with a red div in the background to show where the transparency should lie: Red background

Here is my code:

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 datetime import datetime

options = webdriver.ChromeOptions()
options.add_argument('headless')
driver = webdriver.Chrome(chrome_options=options)

driver.set_window_size(2560, 1600)
driver.get('https://twitter.com/realDonaldTrump/status/516382177798680576')
# driver.execute_script("$('body').append(`<div style='background: red; width: 100%; height: 100%;'></div>`);")
driver.save_screenshot('screenshots/' + str(datetime.now()) + '.png')

driver.quit()

How would I be able to create the transparent version of that screenshot?

*** EDIT *** I made a gist of how I accomplished this. The accepted answer helped me get to the point where I could figure it out and that is what I wanted. This gist, however, is the correct solution to my problem: https://gist.github.com/colecrtr/f58834ff09ab07e3c1164667b753e77a

like image 469
Cole Avatar asked Oct 09 '17 23:10

Cole


People also ask

Can we take screenshot in headless mode Selenium?

Screenshots are beneficial, specifically in headless test execution, where you cannot see the GUI of the application. Still, Selenium will capture it by a screenshot and store it in a file so that you can verify the application later.

Which is the method of Selenium WebDriver to capture the screenshots?

In order to capture a screenshot in Selenium, one has to utilize the method TakesScreenshot. This notifies WebDrive that it should take a screenshot in Selenium and store it. In the above snippet, OutputType defines the output type for the required screenshot.

What argument should be passed to method getScreenshotAs () to capture the screenshot?

TakesScreenshot ts = (TakesScreenshot)driver; The method getScreenshotAs() in the interface TakesScreenshot helps in capturing the screenshots and storing it in a specified path. We have another interface OutputType, which is used to specify the output format of the screenshot such as FILE, BYTES, etc.


2 Answers

One way would be to convert each white pixel to a transparent pixel from the screenshot by setting the alpha byte to 0:

from selenium import webdriver
from PIL import Image
from io import BytesIO # python 3
import numpy as np

def remove_color(img, rgba):
  data = np.array(img.convert('RGBA'))        # rgba array from image
  pixels = data.view(dtype=np.uint32)[...,0]  # pixels as rgba uint32
  data[...,3] = np.where(pixels == np.uint32(rgba), np.uint8(0), np.uint8(255))  # set alpha channel
  return Image.fromarray(data)

driver = webdriver.Chrome()
driver.get("http://www.bbc.co.uk/news")

# take screenshot with a transparent background
with Image.open(BytesIO(driver.get_screenshot_as_png())) as img :
  with remove_color(img, 0xffffffff) as img2:
    img2.save(r"C:\temp\screenshot.png")

However you may end up with some unexpected transparent pixels if the page content has some white pixels and the antialiassing will probably be visible.

Another solution is to use the DevTool API with Chrome to exclude the background from the screenshot:

from selenium import webdriver
import json

def send(cmd, params={}):
  resource = "/session/%s/chromium/send_command_and_get_result" % driver.session_id
  url = driver.command_executor._url + resource
  body = json.dumps({'cmd':cmd, 'params': params})
  response = driver.command_executor._request('POST', url, body)
  if response['status']: raise Exception(response.get('value'))
  return response.get('value')

options = webdriver.ChromeOptions()
options.add_argument("disable-gpu")
options.add_argument("disable-infobars")

driver = webdriver.Chrome(chrome_options=options)
driver.get("http://www.bbc.co.uk/news")

# take screenshot with a transparent background
send("Emulation.setDefaultBackgroundColorOverride", {'color': {'r': 0, 'g': 0, 'b': 0, 'a': 0}})
driver.get_screenshot_as_file(r"C:\temp\screenshot.png")
send("Emulation.setDefaultBackgroundColorOverride")  # restore
like image 63
Florent B. Avatar answered Sep 20 '22 06:09

Florent B.


A png can have transparent pixels, but a screenshot cannot. Whenever you render something you mix the rendering looking at transparency levels of layers, but the combine layer will always have a background.

A screenshot is of what has been rendered on screen and it can never be transparent. How do you display a true transparent image on a desktop? You can't because the background of desktop or something else will always have to be there.

So what you are asking has nothing to do with Chrome, ChromeDriver. Any screenshot taking tool cannot take a transparent screenshot, without you telling it what to mask.

Even tools like Photoshop use special way (grey color boxes with small changes) to show transparent background, but if you use a screenshot tool to capture that image the result would be a image with actual pixel and no transparency like below

Transparent image

like image 44
Tarun Lalwani Avatar answered Sep 21 '22 06:09

Tarun Lalwani