使用django调整图像大小?

问题描述 投票:12回答:11

我是Django(和Python)的新手,在开始使用其他人的应用程序之前,我一直在尝试自己解决一些问题。我无法理解Django(或Python)的工作方式中的“适合”。我正在尝试解决的是如何在上传图像后调整图像大小。我很好地安装了我的模型并插入管理员,图像上传到目录:

from django.db import models

# This is to list all the countries
# For starters though, this will be just United Kingdom (GB)
class Country(models.Model):
    name = models.CharField(max_length=120, help_text="Full name of country")
    code = models.CharField(max_length=2, help_text="This is the ISO 3166 2-letter country code (see: http://www.theodora.com/country_digraphs.html)")
    flag = models.ImageField(upload_to="images/uploaded/country/", max_length=150, help_text="The flag image of the country.", blank=True)

    class Meta:
        verbose_name_plural = "Countries"

    def __unicode__(self):
        return self.name

我现在遇到的问题是获取该文件并将新文件制作成缩略图。就像我说的,我想知道如何在不使用别人的应用程序的情况下(现在)。我从DjangoSnippets获得了这段代码:

from PIL import Image
import os.path
import StringIO

def thumbnail(filename, size=(50, 50), output_filename=None):
    image = Image.open(filename)
    if image.mode not in ('L', 'RGB'):
        image = image.convert('RGB')
    image = image.resize(size, Image.ANTIALIAS)

    # get the thumbnail data in memory.
    if not output_filename:
        output_filename = get_default_thumbnail_filename(filename)
    image.save(output_filename, image.format) 
    return output_filename

def thumbnail_string(buf, size=(50, 50)):
    f = StringIO.StringIO(buf)
    image = Image.open(f)
    if image.mode not in ('L', 'RGB'):
        image = image.convert('RGB')
    image = image.resize(size, Image.ANTIALIAS)
    o = StringIO.StringIO()
    image.save(o, "JPEG")
    return o.getvalue()

def get_default_thumbnail_filename(filename):
    path, ext = os.path.splitext(filename)
    return path + '.thumb.jpg'

...但这最终让我感到困惑......因为我不知道这对我的Django应用程序有什么影响?真的,它是简单地制作已成功上传的图像缩略图的最佳解决方案吗?任何人都可以向我展示一个好的,坚实的,体面的方式,像我这样的初学者可以学会正确地做到这一点吗?就像在,知道在哪里放置那种代码(models.py?forms.py?...)以及它如何在上下文中工作? ......我只是需要一些帮助来理解并解决这个问题。

谢谢!

python django image python-imaging-library
11个回答
7
投票

如果你没问题,那就准备好了一个Django应用程序,正是你想要的:https://github.com/sorl/sorl-thumbnail


0
投票

我也发誓贾斯汀德里斯科尔的django-photologue也非常适合调整大小。它:

  1. 调整大小(可以按比例缩放到高度或宽度)
  2. 作物(智能的一种:从中心,顶部,左侧,底部或右侧)
  3. 可选择升级
  4. 可以添加效果(例如“颜色”,“亮度”,“对比度”和“清晰度”以及“查找边缘”和“浮雕”等滤镜。锐化图像。使缩略图变为黑白。)
  5. 可以添加简单的水印
  6. 缓存结果

基本上它太棒了。


0
投票

一种非常简单的方法是使用django-imagefit调整图像大小和/或裁剪图像。

它将保留原始图像,因此您可以拥有多个版本,稍后重构您的前端,它也适用于非模型图像。


4
投票

这是我在模型中使用的,如果上传的图像已更改,则保存新的缩略图。它是基于另一个DjangoSnippet,但它我不记得谁写了原始 - 如果你知道请添加一个评论,以便我可以信任他们。

from PIL import Image
from django.db import models
from django.contrib.auth.models import User

import os
import settings

class Photo_Ex(models.Model):
    user = models.ForeignKey(User, blank=True, null=True)    
    photo = models.ImageField(upload_to='photos')
    thumbnail = models.ImageField(upload_to='profile_thumb', blank=True,
                              null=True, editable=False)

    def save(self, *args, **kwargs):
        size = (256,256)
        if not self.id and not self.photo:
            return

        try:
            old_obj = Photo_Ex.objects.get(pk=self.pk)
            old_path = old_obj.photo.path
        except:
            pass

        thumb_update = False
        if self.thumbnail:
            try:
                statinfo1 = os.stat(self.photo.path)
                statinfo2 = os.stat(self.thumbnail.path)
                if statinfo1 > statinfo2:
                    thumb_update = True
            except:
                thumb_update = True

        pw = self.photo.width
        ph = self.photo.height
        nw = size[0]
        nh = size[1]

        if self.photo and not self.thumbnail or thumb_update:
            # only do this if the image needs resizing
            if (pw, ph) != (nw, nh):
                filename = str(self.photo.path)
                image = Image.open(filename)
                pr = float(pw) / float(ph)
                nr = float(nw) / float(nh)

                if image.mode not in ('L', 'RGB'):
                    image = image.convert('RGB')

                if pr > nr:
                    # photo aspect is wider than destination ratio
                    tw = int(round(nh * pr))
                    image = image.resize((tw, nh), Image.ANTIALIAS)
                    l = int(round(( tw - nw ) / 2.0))
                    image = image.crop((l, 0, l + nw, nh))
                elif pr < nr:
                    # photo aspect is taller than destination ratio
                    th = int(round(nw / pr))
                    image = image.resize((nw, th), Image.ANTIALIAS)
                    t = int(round(( th - nh ) / 2.0))
                    image = image.crop((0, t, nw, t + nh))
                else:
                    # photo aspect matches the destination ratio
                    image = image.resize(size, Image.ANTIALIAS)

            image.save(self.get_thumbnail_path())
            (a, b) = os.path.split(self.photo.name)
            self.thumbnail = a + '/thumbs/' + b
            super(Photo_Ex, self).save()
            try:
                os.remove(old_path)
                os.remove(self.get_old_thumbnail_path(old_path))
            except:
                pass

    def get_thumbnail_path(self):
        (head, tail) = os.path.split(self.photo.path)
        if not os.path.isdir(head + '/thumbs'):
            os.mkdir(head + '/thumbs')
        return head + '/thumbs/' + tail

    def get_old_thumbnail_path(self, old_photo_path):
        (head, tail) = os.path.split(old_photo_path)
        return head + '/thumbs/' + tail   

2
投票

一个关键问题是:什么时候应该生成缩略图?

  1. 当用户请求缩略图时动态地?
  2. 或者,您是否希望在数据库中的国家/地区被INSERTed / UPDATEd时创建物理磁盘文件。

如果(1)我建议你创建一个映射到url /flagthumbnail/countryid的视图。然后视图方法必须:

  1. 从数据库中获取国家/地区实例
  2. 将标志图像读入PIL图像并调整其大小。
  3. 创建(并返回)具有正确内容类型的HTTPResponse,并将PIL图像写入响应。

每当您需要显示缩略图标志时,只需使用<a href="/flagthumbnail/countryid">

如果(2),您可以连接到Django的django.db.models.signals.post_save信号,并在信号处理程序中创建并保存缩略图文件。


2
投票

我想这取决于你使用缩略图的方式和时间。

如果您想在每次保存国家/地区时创建一些缩略图,您可以这样做:

from django.db import models

# This is to list all the countries
# For starters though, this will be just United Kingdom (GB)
class Country(models.Model):
    name = models.CharField(max_length=120, help_text="Full name of country")
    code = models.CharField(max_length=2, help_text="This is the ISO 3166 2-letter country code (see: http://www.theodora.com/country_digraphs.html)")
    flag = models.ImageField(upload_to="images/uploaded/country/", max_length=150, help_text="The flag image of the country.", blank=True)

    class Meta:
        verbose_name_plural = "Countries"

    def __unicode__(self):
        return self.name

    def save(self, force_insert=False, force_update=False):
        resize_image(self.flag)
        super(Country, self).save(force_insert, force_update)

如果你不是100%确定你需要你的图像大小,你可以在最后一分钟调整大小。我已经看到这有效地使用了templatetag(我相信Pinax上的一个版本)。您可以创建一个获取图像和大小的模板标签,然后根据需要创建并保存适当大小的图像,或者显示之前创建的图像(如果有)。它工作得很好。


2
投票

覆盖保存方法是一个不错的选择,但在这种情况下我更倾向于使用signal。 Django信号允许您“监听”给定模型类型的各种事件;在这种情况下,你会对post_save事件感兴趣。

我通常在我的models.py文件中订阅这样的信号。您的代码看起来像这样:

from django.db.models.signals import post_save
from models import Country

def resize_image(sender, **kwargs):
    country = kwargs["instance"]
    resize_image(country.flag) # where resize_image generates a thumbnail given a Country instance

post_save.connect(resize_image, sender=Country)

2
投票

Ryan是正确的信号是一种更好的方法,但是覆盖保存的优点是我们可以获得新的和新的图像路径,看看图像是否已经改变(如果它已经创建了一个新的缩略图),保存模型实例然后删除旧图像和缩略图。

记住django不会为你清理旧图像,所以除非你有脚本来检查图像/缩略图是否仍在使用中并清理掉任何不存在的图像/缩略图,否则会泄漏磁盘空间。 (根据图像大小和更新频率,这可能会或可能不是您的问题)

我不知道你怎么能用post_save信号做到这一点,而且我对信号(今晚研究的信息)知之甚少!)知道是否有合适的pre_save信号。如果我找到一个,那么我将重新编写上面的代码,将信号用作通用的预存储列表。


2
投票

你可以尝试:

image headcut

特征:

  • 允许您设置图像的关注中心......头部不再被切割。
  • 视频缩略图
  • 防止跨站点图像链接
  • 简单的设置和使用

2
投票

不确定你发送的代码,因为我从不使用这样的模型,但还有另一种方法。

您可以实现自己的FileUploadHandler来处理图像文件上传。例子是here。在第37行(dest.close())之后使用thumbnail(upload_dir + upload.name)函数(您发送的函数)。

希望它能帮到你。


1
投票

另一种方法是直接使用Imagemagick,如果你想避免使用Pillow和Python 3的几个问题,比如this one

from subprocess import call
call(['convert', img_path_file_name, '-thumbnail', target_size_str, '-antialias', style_path_file_name])

您可以在模型保存或模板标记上调用此方法,以文件缓存方式生成原始文件的一次性操作副本,甚至是芹菜任务。

您可以在我的一个项目中获得如何使用它的示例:

© www.soinside.com 2019 - 2024. All rights reserved.