В модуль добавлен объект ImageMagick для получения размеров изображения, изменения размеров изображения для формата backgrounds, с поддержкой запуска через chroot.master
Родитель
19f92a0fe4
Сommit
933ab35b1c
@ -0,0 +1,92 @@
|
||||
import os
|
||||
import hashlib
|
||||
from calculate.utils.files import Process, write_file, read_file
|
||||
|
||||
class ImageMagickError(Exception):
|
||||
pass
|
||||
|
||||
class ImageMagick:
|
||||
def __init__(self, prefix='/'):
|
||||
self.prefix = prefix
|
||||
self.init_commands(prefix)
|
||||
self.default_opts = []
|
||||
|
||||
@property
|
||||
def available(self):
|
||||
return self.convert_cmd and self.identify_cmd
|
||||
|
||||
@property
|
||||
def chroot(self):
|
||||
return self.prefix != '/'
|
||||
|
||||
def init_commands(self, prefix):
|
||||
self.convert_cmd = "/usr/bin/convert"
|
||||
self.identify_cmd = "/usr/bin/identify"
|
||||
self.chroot_cmd = "/bin/chroot"
|
||||
self.bash_cmd = "/bin/bash"
|
||||
if not os.path.exists(os.path.join(prefix, self.convert_cmd[1:])):
|
||||
self.convert_cmd = None
|
||||
if not os.path.exists(os.path.join(prefix, self.identify_cmd[1:])):
|
||||
self.identify_cmd = None
|
||||
|
||||
def trim_prefix_path(self, filename):
|
||||
retpath = "/%s" % os.path.relpath(filename, self.prefix)
|
||||
if retpath.startswith("/.."):
|
||||
return None
|
||||
return retpath
|
||||
|
||||
def get_image_resolution(self, source):
|
||||
if self.chroot:
|
||||
identify = Process(self.chroot_cmd, self.prefix,
|
||||
self.bash_cmd, "-c",
|
||||
" ".join([self.identify_cmd,
|
||||
"-format '%w %h'", source]))
|
||||
else:
|
||||
identify = Process(self.identify_cmd, "-format", "%w %h", source)
|
||||
if identify.success():
|
||||
swidth, _sep, sheight = identify.read().strip().partition(" ")
|
||||
if swidth.isdigit() and sheight.isdigit():
|
||||
return int(swidth), int(sheight)
|
||||
return None
|
||||
|
||||
def convert(self, source, target, *opts):
|
||||
command = [self.convert_cmd, "-quality", "95",
|
||||
source]
|
||||
command.extend(self.default_opts)
|
||||
command.extend(opts)
|
||||
command.append(target)
|
||||
if self.chroot:
|
||||
convert = Process(self.chroot_cmd, self.prefix,
|
||||
self.bash_cmd, "-c",
|
||||
" ".join(command))
|
||||
else:
|
||||
convert = Process(*command)
|
||||
if convert.success():
|
||||
return True
|
||||
else:
|
||||
print(convert.read_error())
|
||||
return False
|
||||
|
||||
def convert_resize_crop_center(self, source, target, height, width):
|
||||
#if ((width == self.source_width and height == self.source_height) and
|
||||
# (source.rpartition('.')[2] == target.rpartition('.')[2])):
|
||||
# with write_file(target) as sf:
|
||||
# sf.write(read_file(source))
|
||||
# return True
|
||||
res = "%dx%d" % (width, height)
|
||||
|
||||
return self.convert(source, target, "-quality", "95",
|
||||
"-resize", "%s^" % res,
|
||||
"-strip", "-gravity", "center",
|
||||
"-crop", "%s+0+0" % res)
|
||||
|
||||
def convert_resize_gfxboot(self, source, target, height, width):
|
||||
res = "%dx%d" % (width, height)
|
||||
|
||||
return self.convert(source, target, "-quality", "95",
|
||||
"-resize", "%s^" % res,
|
||||
"-strip", "-gravity", "center",
|
||||
"-crop", "%s+0+0" % res,
|
||||
"-sampling-factor", "2x2",
|
||||
"-interlace", "none",
|
||||
"-set", "units", "PixelsPerSecond")
|
@ -0,0 +1,215 @@
|
||||
import pytest
|
||||
from calculate.utils.images import ImageMagick, ImageMagickError
|
||||
import os
|
||||
from calculate.utils.files import Process
|
||||
|
||||
@pytest.mark.images_utils
|
||||
def test_imagemagick_initialization():
|
||||
im = ImageMagick()
|
||||
assert im.available
|
||||
assert not im.chroot
|
||||
|
||||
im = ImageMagick(prefix='/mnt/somepath')
|
||||
assert not im.available
|
||||
assert im.chroot
|
||||
|
||||
|
||||
@pytest.mark.images_utils
|
||||
@pytest.mark.parametrize('case',
|
||||
[
|
||||
{
|
||||
"name": "simple path",
|
||||
"chroot": "/mnt/install",
|
||||
"source": "/mnt/install/usr/share/pixmap/image.jpg",
|
||||
"result": "/usr/share/pixmap/image.jpg",
|
||||
},
|
||||
{
|
||||
"name": "relative",
|
||||
"chroot": "/mnt/install",
|
||||
"source": "/mnt/install/usr/share/../pixmap/image.jpg",
|
||||
"result": "/usr/pixmap/image.jpg",
|
||||
},
|
||||
{
|
||||
"name": "first level",
|
||||
"chroot": "/mnt/install",
|
||||
"source": "/mnt/install/image.jpg",
|
||||
"result": "/image.jpg",
|
||||
},
|
||||
{
|
||||
"name": "Wrong",
|
||||
"chroot": "/mnt/install",
|
||||
"source": "/mnt/image.jpg",
|
||||
"result": None,
|
||||
},
|
||||
],
|
||||
ids=lambda x:x["name"])
|
||||
def test_imagemagick_trim_prefix_path(case):
|
||||
im = ImageMagick(case['chroot'])
|
||||
assert im.trim_prefix_path(case["source"]) == case["result"]
|
||||
|
||||
@pytest.mark.images_utils
|
||||
@pytest.mark.parametrize('case',
|
||||
[
|
||||
{
|
||||
"name": "PNG file",
|
||||
"image": "tests/utils/testfiles/file.png",
|
||||
"result": (48,48),
|
||||
},
|
||||
{
|
||||
"name": "JPEG file",
|
||||
"image": "tests/utils/testfiles/file.jpg",
|
||||
"result": (320,180),
|
||||
},
|
||||
{
|
||||
"name": "No file",
|
||||
"image": "tests/utils/testfiles/file2.jpg",
|
||||
"result": None,
|
||||
},
|
||||
{
|
||||
"name": "Wrong file",
|
||||
"image": "tests/utils/testfiles/wrong.jpg",
|
||||
"result": None,
|
||||
},
|
||||
],
|
||||
ids=lambda x:x["name"])
|
||||
def test_imagemagick_get_resolutions(case):
|
||||
im = ImageMagick()
|
||||
assert im.get_image_resolution(case["image"]) == case["result"]
|
||||
|
||||
@pytest.fixture
|
||||
def chroot_test():
|
||||
chrootpath = "/mnt/testchroot"
|
||||
assert os.getuid() == 0, "Need superuser privileges"
|
||||
if os.path.exists(chrootpath):
|
||||
os.unlink(chrootpath)
|
||||
os.symlink("/", chrootpath)
|
||||
try:
|
||||
yield chrootpath
|
||||
finally:
|
||||
os.unlink(chrootpath)
|
||||
|
||||
@pytest.mark.chroot
|
||||
@pytest.mark.images_utils
|
||||
def test_chroot_imagemagick_get_resolution(chroot_test):
|
||||
im = ImageMagick(chroot_test)
|
||||
curpath = os.getcwd()
|
||||
image_path = os.path.join(curpath, "tests/utils/testfiles/file.png")
|
||||
assert im.get_image_resolution(image_path) == (48,48)
|
||||
|
||||
def get_histogram(image, remap_image):
|
||||
p = Process("/usr/bin/convert", image, "-remap", remap_image, "-format", "%c", "histogram:info:-")
|
||||
return p.read()
|
||||
|
||||
def get_verbose_image_info(image):
|
||||
p = Process("/usr/bin/identify", "-verbose", image)
|
||||
return p.read()
|
||||
|
||||
@pytest.mark.images_utils
|
||||
@pytest.mark.parametrize('case',
|
||||
[
|
||||
{
|
||||
# проверка, пропорционального уменьшения
|
||||
"name": "Origin test",
|
||||
"resize": (16,32),
|
||||
"result": """192: (0,0,0) #000000 black
|
||||
64: (0,255,0) #00FF00 lime
|
||||
256: (255,255,255) #FFFFFF white"""
|
||||
},
|
||||
{
|
||||
# проверка, что при изменении размера только по горизонтали
|
||||
# удаляются только части изображения справа и слева
|
||||
# в исходном изображении на этих частях находится белый фон
|
||||
"name": "Shrink horizontal",
|
||||
"resize": (16,16),
|
||||
"result": """192: (0,0,0) #000000 black
|
||||
64: (0,255,0) #00FF00 lime"""
|
||||
},
|
||||
{
|
||||
# проверка, что при уменьшении изображения первоначально оно сдавливается
|
||||
# по вертикали а затем обрезаются части слева и справа
|
||||
"name": "Shrink all",
|
||||
"resize": (8,8),
|
||||
"result": """48: (0,0,0) #000000 black
|
||||
16: (0,255,0) #00FF00 lime"""
|
||||
},
|
||||
{
|
||||
# проверка, пропорционального уменьшения
|
||||
"name": "Shrink proportionately",
|
||||
"resize": (8,16),
|
||||
"result": """48: (0,0,0) #000000 black
|
||||
16: (0,255,0) #00FF00 lime
|
||||
64: (255,255,255) #FFFFFF white"""
|
||||
},
|
||||
{
|
||||
# проверка, пропорционального увеличения
|
||||
"name": "Increase size proportionately",
|
||||
"resize": (32,64),
|
||||
"result": """768: (0,0,0) #000000 black
|
||||
256: (0,255,0) #00FF00 lime
|
||||
1024: (255,255,255) #FFFFFF white"""
|
||||
},
|
||||
{
|
||||
# проверка увеличения и обрезки по горизонтали
|
||||
"name": "Increase size and cut",
|
||||
"resize": (32,32),
|
||||
"result": """768: (0,0,0) #000000 black
|
||||
256: (0,255,0) #00FF00 lime"""
|
||||
},
|
||||
{
|
||||
# проверка увеличения по горизонтали
|
||||
# в этом случае будет отрезан верх и низ
|
||||
# поэтому на выходе нет зелёного цвета
|
||||
"name": "Increase horizontal size",
|
||||
"resize": (16,48),
|
||||
"result": """384: (0,0,0) #000000 gray(0)
|
||||
384: (255,255,255) #FFFFFF gray(255)"""
|
||||
},
|
||||
],
|
||||
ids=lambda x:x["name"])
|
||||
def test_imagemagick_convert(case):
|
||||
image_path = "tests/utils/testfiles/origin.png"
|
||||
output_file = "tests/utils/testfiles/test_output5.png"
|
||||
if os.path.exists(output_file):
|
||||
os.unlink(output_file)
|
||||
im = ImageMagick()
|
||||
im.default_opts = ["-filter","box"]
|
||||
assert im.convert_resize_crop_center(image_path, output_file, *case["resize"])
|
||||
histogram = get_histogram(output_file, image_path)
|
||||
discard_space = lambda x: x.replace(" ","").replace("\n","")
|
||||
assert discard_space(histogram) == discard_space(case["result"])
|
||||
|
||||
@pytest.mark.chroot
|
||||
@pytest.mark.images_utils
|
||||
def test_chroot_imagemagick_convert_center(chroot_test):
|
||||
curpath = os.getcwd()
|
||||
image_path = "tests/utils/testfiles/origin.png"
|
||||
output_file = "tests/utils/testfiles/test_output.png"
|
||||
image_path = os.path.join(curpath, image_path)
|
||||
output_file = os.path.join(curpath, output_file)
|
||||
result = """48: (0,0,0) #000000 black
|
||||
16: (0,255,0) #00FF00 lime"""
|
||||
if os.path.exists(output_file):
|
||||
os.unlink(output_file)
|
||||
im = ImageMagick(chroot_test)
|
||||
im.default_opts = ["-filter","box"]
|
||||
assert im.convert_resize_crop_center(image_path, output_file, 8, 8)
|
||||
histogram = get_histogram(output_file, image_path)
|
||||
discard_space = lambda x: x.replace(" ","").replace("\n","")
|
||||
assert discard_space(histogram) == discard_space(result)
|
||||
|
||||
@pytest.mark.images_utils
|
||||
def test_imagemagick_convert_gfxboot():
|
||||
output_file = "tests/utils/testfiles/test_output.jpg"
|
||||
image_path = "tests/utils/testfiles/origin.png"
|
||||
im = ImageMagick()
|
||||
im.convert_resize_gfxboot(image_path, output_file, 32, 32)
|
||||
assert "sampling-factor: 2x2" in get_verbose_image_info(output_file)
|
||||
|
||||
@pytest.mark.images_utils
|
||||
def test_clear_imagemagick_convert():
|
||||
for output_file in (
|
||||
"tests/utils/testfiles/test_output.png",
|
||||
"tests/utils/testfiles/test_output.jpg"
|
||||
):
|
||||
if os.path.exists(output_file):
|
||||
os.unlink(output_file)
|
После Ширина: | Высота: | Размер: 13 KiB |
После Ширина: | Высота: | Размер: 4.5 KiB |
После Ширина: | Высота: | Размер: 5.4 KiB |
@ -0,0 +1 @@
|
||||
NOFILE
|
Загрузка…
Ссылка в новой задаче