I'm trying to convert a colored example.svg file into a grayscaled example_gs.svg in python 3.6 in Anaconda 4.8.3 on a Windows 10 device.
First I tried to apply a regex to convert the 'rgb(xxx,yyy,zzz)' to black, but that created a black rectangle losing the image in the process. Next I installed inkscape and ran a grayscale command which appeared to be working but did not modify the example.svg. The third attempt with pillow Image did not load the .svg.
# conda install -c conda-forge inkscape
# https://www.commandlinefu.com/commands/view/2009/convert-a-svg-file-to-grayscale
# inkscape -f file.svg --verb=org.inkscape.color.grayscale --verb=FileSave --verb=FileClose
import re
import os
import fileinput
from PIL import Image
import cv2
# Doesn't work, creates a black square. Perhaps set a threshold to not convert rgb white/bright colors
def convert_svg_to_grayscale(filepath):
# Read in the file
with open(filepath, 'r') as file :
filedata = file.read()
# Replace the target string
filedata = re.sub(r'rgb\(.*\)', 'black', filedata)
# Write the file out again
with open(filepath, 'w') as file:
file.write(filedata)
# opens inkscape, converts to grayscale but does not actually export to the output file again
def convert_svg_to_grayscale_inkscape(filepath):
command = f'inkscape -f {filepath} --verb=org.inkscape.color.grayscale --verb=FileSave --verb=FileClose'
os.system(f'cmd /k {command}')
# Pillow Image is not able to import .svg files
def grayscale(filepath):
image = Image.open(filepath)
cv2.imwrite(f'{filepath}', image.convert('L'))
# walks through png files and calls function to convert the png file to .svg
def main():
filepath = 'example.svg'
convert_svg_to_grayscale_inkscape(filepath)
convert_svg_to_grayscale(filepath)
grayscale(filepath)
if __name__ == '__main__':
main()
How could I change a colored .svg file into a grayscaled image in python 3.6 on a windows device?
You have chosen the right tool for converting to grayscale. Your last attempt is good but you need to import cairosvg that provides the svg2png function. Then, load the png file with Pillow and convert it to an np.array and then you can easily load it with openCV and convert it to grayscale as you did. At last you can use svglib and reportlab to export the images in svg. Use this snippet as an example: https://stackoverflow.com/a/62345450/13605264
The accepted solution does rasterize the svg. So basically it converts the vector graphics to a pixel image. I propose a version that maintains all the desirable traints of an svg.
You can try the following solution. The code also serves as template to do other svg related changes by scripting in python.
What the code does is parse the svg with an XML parser. For this XPath is used andlooks for all elements with the style attribute. As a reference: this is how an example line in svg looks:
style="fill:#ffe6cc;fill-opacity:1;fill-rule:nonzero;stroke:none"
What we need to to is string editing to change the rgb #ffe6cc to a grayscale value. The bulk of the code does just this.
#!/usr/bin/env -S python3
import xml.etree.ElementTree as ET
from xml.etree.ElementTree import ElementTree
tree = ET.parse('example.svg')
root = tree.getroot()
# using XPath addressing
for elem in root.findall(".//{http://www.w3.org/2000/svg}path[@style]"):
style = elem.attrib['style']
print(style)
# do string editing
d = {}
for i in style.split(';'):
att,value = i.split(':')
d[att] = value
for atatt in ['fill','stroke']:
# convert fill or stroke to grayscale
if atatt in d and d[atatt] != 'none':
s = d[atatt]
r = int(s[1:3],16)
g = int(s[3:5],16)
b = int(s[5:],16)
gray = int(0.3*r + 0.59*g + 0.11*b + 0.5)
snew = '#%02x%02x%02x' % (gray, gray, gray)
#print(s,r,g,b,gray,snew)
d[atatt] = snew
# write back dict
s = ';'.join(map(':'.join, d.items()))
print(s)
# write back edited string
elem.attrib['style'] = s
with open('example_gs.svg', 'wb') as f:
ElementTree(root).write(f)
Improvements are welcome.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With