Libvips: проблемы с производительностью веб-API

Созданный на 19 янв. 2018  ·  5Комментарии  ·  Источник: libvips/libvips

Привет,

Я хочу использовать веб-API для pyvips.
Но действительно плохая производительность. Где я ошибаюсь?

Мой код следующий;

hello.py

from flask import Flask
from Image import ImageTransform
from flask import request
from flask import send_file
import io

app = Flask(__name__)

@app.route("/")
def hello():
    return "Hello World!"

@app.route("/image")
def image():
    image_value = request.args.get('image')
    i = ImageTransform(image_value)
    i.set_transform()
    a = i.image.write_to_buffer('.jpeg')
    return send_file(io.BytesIO(a), mimetype='image/jpg')

Image.py

import pyvips
import math

class ImageTransform(object):
    image = None

    def __init__(self, file_name):
        self.image = pyvips.Image.new_from_file(file_name, access='sequential')

    def set_resize_transform(self, width, height):
        im = self.image
        mem = im.write_to_buffer('.jpeg[Q=80]')
        im = pyvips.Image.thumbnail_buffer(mem, width, height=height)

        im = im.bandjoin(255)
        im = im.gravity("centre", width, height,
                            extend="background",
                            background=[244, 244, 244, 255])
        self.image = im                            
        pass
    def set_ratio_and_watermark_transform(self):
        im = self.image
        # ##### ######
        # Trigonometry: Tangent = Opposite / Adjacent
        tangent = im.height/im.width
        # convert arctangent to degrees
        angle = math.atan(tangent)*(180/math.pi);

        water_mark_x = 140
        water_mark_y = 140
        water_mark_len = 16
        # a^2 = b^2 + c^2 ; a = sqrt(b^2 + c^2)
        half_hypotenuse = math.sqrt(im.height*im.height + im.width*im.width)/2;
        font_size = int(math.hypot(im.height, im.width)/math.hypot(water_mark_x, water_mark_y)*(4 + int(water_mark_len/2)))

        text = pyvips.Image.text("text text text", font = "Futura Medium", align='centre', width = 500, dpi = (font_size*4))
        text = text.linear(0.3, 0)

        text = text.similarity(angle=angle)

        x = (im.width-text.width)/2
        y = (im.height-text.height)/2

        text = text.embed(x, y, text.width + x, text.height + y, extend="copy")

        w = im.width if text.width > im.width else text.width
        h = im.height if text.height > im.height else text.height

        im = text.ifthenelse(255, im, blend=True)      
        self.image = im

    def set_transform(self, type=int):
        self.set_ratio_and_watermark_transform()
        self.set_resize_transform(600, 450)

    def write_image(self,dest_name):
        self.image.write_to_file(dest_name)

Баш
FLASK_APP = запуск фляги hello.py

```bash
ab -c 10 -n 10 http://54.229.246.74/image\?image\=foto.jpeg
question

Самый полезный комментарий

Думаю, я бы добавил водяной знак вот так:

# in libvips 8.6, you can get text to automatically size to fill a rectangle
text = pyvips.Image.text("text text text", 
                         font="Futura Medium", align="centre", 
                         width=300, height=300)  

# scale down brightness, then back to 8 bit ... this will avoid float arithmetic
# for the rotate/expand/ifthenelse
text = (text * 0.3).cast("uchar")

tangent = float(im.width) / im.height
angle = math.atan(tangent) * (180 / math.pi)
text = text.similarity(angle=angle)
text = text.gravity("centre", target_width, target_height)

im = text.ifthenelse(255, im, blend=True) 

Итак, мы использовали новую функцию «размер текста по размеру поля» в 8.6, используя x * 0.3 вместо linear (они делают то же самое, просто перегрузку оператора легче читать), сохраняя текст как 8-битный, чтобы избежать смещения позже, и использование gravity чтобы сделать embed немного проще.

Все 5 Комментарий

Привет @volkan ,

Я бы реорганизовал ваш конвейер примерно так:

  1. Используйте thumbnail чтобы открыть изображение и уменьшить его размер за одну операцию.
  2. Добавьте наложение водяного знака
  3. Наконец, используйте gravity чтобы добавить фон

Я сделаю тебе пример.

Вот тестовая программа, которая запускает ваш код и возможный новый конвейер. Я изменил касательную в вашей версии на:

        tangent = float(im.width) / im.height

Но в остальном без изменений.

import time
import sys
import math
import pyvips

from Image import ImageTransform

start = time.time()
i = ImageTransform(sys.argv[1])
i.set_transform()
i.write_image(sys.argv[2])
end = time.time()
print "ImageTransform() took ", end - start

start = time.time()

target_width = 600
target_height = 450         

# efficiently load and downsize in one operation
im = pyvips.Image.thumbnail(sys.argv[1], target_width, height=target_height)

# watermark
tangent = float(im.width) / im.height
angle = math.atan(tangent) * (180 / math.pi)
water_mark_x = 140
water_mark_y = 140
water_mark_len = 16
half_hypotenuse = math.sqrt(im.height*im.height + im.width*im.width)/2;
font_size = int(math.hypot(im.height, im.width)/math.hypot(water_mark_x, water_mark_y)*(4 + int(water_mark_len/2)))
text = pyvips.Image.text("text text text", font = "Futura Medium", align='centre', width = 500, dpi = (font_size*4))
text = text.linear(0.3, 0)
text = text.similarity(angle=angle)
x = (im.width - text.width) / 2
y = (im.height - text.height) / 2
text = text.embed(x, y, text.width + x, text.height + y, extend="copy")
w = im.width if text.width > im.width else text.width
h = im.height if text.height > im.height else text.height
im = text.ifthenelse(255, im, blend=True)  

# add alpha and margins
im = im.bandjoin(255)
im = im.gravity("centre", target_width, target_height,
                    extend="background",
                    background=[244, 244, 244, 255])

im.write_to_file(sys.argv[2])

end = time.time()

print "after revision, took ", end - start

На этом ноутбуке с изображением 6k x 4k я вижу:

$ python volkan.py ~/pics/theo.jpg x.jpg
ImageTransform() took  0.490525960922
after revision, took  0.0774891376495

Вы можете сделать код водяного знака немного проще, я попробую.

Думаю, я бы добавил водяной знак вот так:

# in libvips 8.6, you can get text to automatically size to fill a rectangle
text = pyvips.Image.text("text text text", 
                         font="Futura Medium", align="centre", 
                         width=300, height=300)  

# scale down brightness, then back to 8 bit ... this will avoid float arithmetic
# for the rotate/expand/ifthenelse
text = (text * 0.3).cast("uchar")

tangent = float(im.width) / im.height
angle = math.atan(tangent) * (180 / math.pi)
text = text.similarity(angle=angle)
text = text.gravity("centre", target_width, target_height)

im = text.ifthenelse(255, im, blend=True) 

Итак, мы использовали новую функцию «размер текста по размеру поля» в 8.6, используя x * 0.3 вместо linear (они делают то же самое, просто перегрузку оператора легче читать), сохраняя текст как 8-битный, чтобы избежать смещения позже, и использование gravity чтобы сделать embed немного проще.

@jcupitt ваши примеры очень полезны. Спасибо.

ОК, закрою!

Была ли эта страница полезной?
0 / 5 - 0 рейтинги