Skip to content

Commit de513c3

Browse files
committed
Fixed the bmp loading warning issue of Mario.rte using pyvips + np + PIL, instead of redirecting the warning away from the terminal. Closes #41.
1 parent 739ccda commit de513c3

File tree

4 files changed

+95
-55
lines changed

4 files changed

+95
-55
lines changed

Python/palette.py

Lines changed: 54 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,27 @@
1-
import os, sys, io, cv2
1+
import os, sys, io
22

3+
import numpy as np
34
from PIL import Image
45

56

6-
palette = Image.open(os.path.join("Media", "palette.bmp")).getpalette()
7+
def load_vips_env():
8+
# pyvips from pip doesn't seem to work, unzip the latest vips-dev-w64-all.zip instead from https://github.com/libvips/libvips/releases.
9+
vipshome = 'c:\\vips-dev-8.10\\bin'
10+
os.environ['PATH'] = vipshome + ';' + os.environ['PATH']
11+
12+
load_vips_env()
13+
14+
import pyvips
15+
16+
17+
# TODO: Move to shared_globals.py
18+
def resource_path(relative_path):
19+
if hasattr(sys, '_MEIPASS'):
20+
return os.path.join(sys._MEIPASS, relative_path)
21+
return os.path.join(os.path.abspath("."), relative_path)
22+
23+
24+
palette = Image.open(resource_path(os.path.join("Media", "palette.bmp"))).getpalette()
725

826

927
def is_input_image(full_filename):
@@ -15,61 +33,43 @@ def process_image(full_filename, input_image_path, output_image_path):
1533

1634
old_img = get_old_img(input_image_path)
1735

18-
# scale = 1
19-
# old_img = old_img.resize((int(old_img.width * scale), int(old_img.height * scale)))
36+
scale = 1
37+
old_img = old_img.resize((int(old_img.width * scale), int(old_img.height * scale)))
2038

21-
# # putpalette() always expects 256 * 3 ints.
22-
# for k in range(256 - int(len(palette) / 3)):
23-
# for j in range(3):
24-
# palette.append(palette[j])
39+
# putpalette() always expects 256 * 3 ints.
40+
for k in range(256 - int(len(palette) / 3)):
41+
for j in range(3):
42+
palette.append(palette[j])
2543

26-
# palette_img = Image.new('P', (1, 1))
27-
# palette_img.putpalette(palette)
28-
# new_img = old_img.convert(mode="RGB").quantize(palette=palette_img, dither=False)
44+
palette_img = Image.new('P', (1, 1))
45+
palette_img.putpalette(palette)
46+
new_img = old_img.convert(mode="RGB").quantize(palette=palette_img, dither=False)
2947

30-
# new_img.save(os.path.splitext(output_image_path)[0] + ".png")
48+
new_img.save(os.path.splitext(output_image_path)[0] + ".png")
3149

3250

3351
def get_old_img(input_image_path):
34-
# This prevents cv2 from printing warnings to the terminal.
35-
# TODO: Fix the cv2 and PIL bmp reading issue instead of redirecting a warning away from the terminal.
36-
with suppress_stdout_stderr():
37-
# A bmp threw an "Unsupported BMP compression (1)" with Image.open(), so we open it in cv2 first.
38-
# cv2 solution: https://stackoverflow.com/a/52416250/13279557
39-
# cv2 to PIL: https://stackoverflow.com/a/43234001/13279557
40-
# old_img = Image.fromarray(cv2.imread(input_image_path)) # TODO: This may work just as well as the line below for all cases.
41-
old_img = Image.fromarray(cv2.cvtColor(cv2.imread(input_image_path), cv2.COLOR_BGR2RGB))
42-
43-
return old_img
44-
45-
46-
# This stackoverflow's question answers itself in the question with the solution to getting rid of "libpng warning: iCCP: known incorrect sRGB profile".
47-
# https://stackoverflow.com/q/11130156/13279557
48-
class suppress_stdout_stderr(object):
49-
'''
50-
A context manager for doing a "deep suppression" of stdout and stderr in
51-
Python, i.e. will suppress all print, even if the print originates in a
52-
compiled C/Fortran sub-function.
53-
This will not suppress raised exceptions, since exceptions are printed
54-
to stderr just before a script exits, and after the context manager has
55-
exited (at least, I think that is why it lets exceptions through).
56-
57-
'''
58-
def __init__(self):
59-
# Open a pair of null files
60-
self.null_fds = [os.open(os.devnull,os.O_RDWR) for x in range(2)]
61-
# Save the actual stdout (1) and stderr (2) file descriptors.
62-
self.save_fds = [os.dup(1), os.dup(2)]
63-
64-
def __enter__(self):
65-
# Assign the null pointers to stdout and stderr.
66-
os.dup2(self.null_fds[0],1)
67-
os.dup2(self.null_fds[1],2)
68-
69-
def __exit__(self, *_):
70-
# Re-assign the real stdout/stderr back to (1) and (2)
71-
os.dup2(self.save_fds[0],1)
72-
os.dup2(self.save_fds[1],2)
73-
# Close all file descriptors
74-
for fd in self.null_fds + self.save_fds:
75-
os.close(fd)
52+
# pyvips acts as a substitute for PIL and cv2, because both of those can throw a warning in the terminal with RLE bmps.
53+
pyvips_img = pyvips.Image.new_from_file(input_image_path, access='sequential')
54+
np_img = vips2numpy(pyvips_img)
55+
return Image.fromarray(np.uint8(np_img))
56+
57+
58+
format_to_np_dtype = {
59+
'uchar': np.uint8,
60+
'char': np.int8,
61+
'ushort': np.uint16,
62+
'short': np.int16,
63+
'uint': np.uint32,
64+
'int': np.int32,
65+
'float': np.float32,
66+
'double': np.float64,
67+
'complex': np.complex64,
68+
'dpcomplex': np.complex128,
69+
}
70+
71+
72+
def vips2numpy(vi):
73+
return np.ndarray(buffer=vi.write_to_memory(),
74+
dtype=format_to_np_dtype[vi.format],
75+
shape=[vi.height, vi.width, vi.bands])

foo.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import os
2+
3+
import numpy as np
4+
from PIL import Image
5+
6+
def load_vips_env():
7+
vipshome = 'c:\\vips-dev-8.10\\bin'
8+
os.environ['PATH'] = vipshome + ';' + os.environ['PATH']
9+
10+
load_vips_env()
11+
12+
import pyvips
13+
14+
def get_old_img(input_image_path):
15+
# pyvips acts as a substitute for PIL and cv2, because both of those can throw a warning in the terminal with RLE bmps.
16+
pyvips_img = pyvips.Image.new_from_file(input_image_path, access='sequential')
17+
np_img = vips2numpy(pyvips_img)
18+
return Image.fromarray(np.uint8(np_img))
19+
20+
format_to_np_dtype = {
21+
'uchar': np.uint8,
22+
'char': np.int8,
23+
'ushort': np.uint16,
24+
'short': np.int16,
25+
'uint': np.uint32,
26+
'int': np.int32,
27+
'float': np.float32,
28+
'double': np.float64,
29+
'complex': np.complex64,
30+
'dpcomplex': np.complex128,
31+
}
32+
33+
def vips2numpy(vi):
34+
return np.ndarray(buffer=vi.write_to_memory(),
35+
dtype=format_to_np_dtype[vi.format],
36+
shape=[vi.height, vi.width, vi.bands])
37+
38+
get_old_img("input.bmp").show()

main.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# Run manually: python main.py
2-
# Build EXE: pyinstaller --noconsole --onefile --icon="Media/legacy-mod-converter.ico" --add-data="Media/github-icon.png;Media" --add-data="Media/discord-icon.png;Media" --add-data="Media/finish.wav;Media" --name="Legacy Mod Converter" main.py
2+
# Build EXE: pyinstaller --noconsole --onefile --icon="Media/legacy-mod-converter.ico" --add-data="Media/github-icon.png;Media" --add-data="Media/discord-icon.png;Media" --add-data="Media/finish.wav;Media" --add-data="Media/palette.bmp;Media" --name="Legacy Mod Converter" main.py
33

44
from Python import gui
55

requirements.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,6 @@ pyinstaller
44
jsoncomment
55
Pillow
66
opencv-python
7+
numpy
8+
pyvips # You also need to unzip the latest vips-dev-w64-all.zip from https://github.com/libvips/libvips/releases and use the path in palette.py's load_vips_env().
79
# python3 needs sudo apt-get install python3-tk

0 commit comments

Comments
 (0)