Pillow 缩放像素化图像 - Django/Pillow

5 投票
1 回答
4657 浏览
提问于 2025-04-18 07:52

我正在用Django开发一个图片上传器。在图片上传并保存到磁盘后,我想调整保存的图片大小,同时保持它的宽高比。我使用Pillow这个库来处理和调整图片大小。但是,当我尝试调整图片大小时,图片变得模糊了,尽管调整后的图片宽高比和原始图片是一样的。

原始保存的图片: https://www.dropbox.com/s/80yk6tnwt3xnoun/babu_980604.jpeg

调整大小后模糊的图片: https://www.dropbox.com/s/bznodpk4t4xlyqp/babu_736302.large.jpeg

我尝试在网上搜索这个问题,也查看了StackOverflow上其他相关的链接,

比如

如何使用PIL调整图片大小并保持其宽高比?

在保持宽高比的同时,如何让竖图和横图的大小完全一样?

但问题依然存在。

版本信息:

Django=1.6.4

Pillow=2.4.0

所有的设置都在虚拟环境中完成。请帮帮我!

附注:我对Python/Django的世界还是个新手。

这是我的代码片段:

import json
import os
import hashlib
from datetime import datetime
from operator import itemgetter
import random
from random import randint
from django.views.decorators.csrf import csrf_exempt, csrf_protect
from django.http import (HttpResponse, HttpResponseRedirect)
from django.core.context_processors import csrf
from django.core.files.images import get_image_dimensions
from django.shortcuts import render, redirect
from django.forms.models import model_to_dict
from django.views.decorators.csrf import csrf_exempt
from PIL import Image, ImageOps
from django.views.decorators.csrf import csrf_exempt, csrf_protect
import settings

from hashlib import md5
from django import forms

from beardedavenger.models import *

from django.views.decorators.http import require_POST

import pdb
import requests

def imagehandler(requests):
if requests.method == 'POST':
    filename = requests.FILES['file'].name
    file_extension = filename.split('.')[len(filename.split('.')) - 1].lower()
    errors = []

    username = 'nit'

    global random

    #allowed image types are png, jpg, jpeg, gif
    if file_extension not in settings.IMAGE_FILE_TYPES:
        errors.append('The image file you provided is not valid. Only the following extensions are allowed: %s' % ', '.join(settings.IMAGE_FILE_TYPES))
    else:
        image = requests.FILES['file']
        image_w, image_h = get_image_dimensions(image)
        rand = str(random.randint(100000,999999))
        with open(settings.MEDIA_ROOT + username + '_' + rand + '.jpeg', 'wb+') as destination:
            for chunk in requests.FILES['file'].chunks():
                destination.write(chunk)

        large_size = (1920, 1200)

        infile = settings.MEDIA_ROOT + username + '_' + rand + ".jpeg"

        large_file = settings.MEDIA_ROOT + username + '_' + rand +".large"

        try:
            im = Image.open(infile)

            base_width = large_size[0]

            aspect_ratio = float(image_w / float(image_h))
            new_height = int(base_width / aspect_ratio)

            if new_height < 1200:
                final_width = base_width
                final_height = new_height
            else:
                final_width = int(aspect_ratio * large_size[1])
                final_height = large_size[1]

            final_size = (final_width, final_height)

            imaged = im.resize((final_width, final_height), Image.ANTIALIAS)
            # imaged = ImageOps.fit(im, final_size, Image.ANTIALIAS, centering = (0.5,0.5))
            imaged.save(large_file, "JPEG", quality=90)

        except IOError:
            errors.append('error while resizing image')

    if not errors:
        response = HttpResponse(json.dumps({'status': 'success','filename': filename }),
        mimetype="application/json")
    else:
        response = HttpResponse(json.dumps({'status': 'failure','errors': errors,'message': 'Error uploading Picture. '}),
        mimetype="application/json")
    return response
else:
    return render(requests, 'upload.html')

更新:

我之前使用Pillow来调整和压缩我的图片。虽然保持了宽高比,但在调整大小后,图片变得有些暗淡(与原始图片相比,抗锯齿效果过强)。我把处理库换成了ImageMagick(尽管很多帖子建议不要这样做!),并使用Wand API(docs.wand-py.org/en/0.3.7/index.html)来处理我的图片。这个改变效果很好!

1 个回答

10

使用这段代码,我得到了这张图片(Python 2.7,Pillow 2.4.0),没有出现像素化的问题。

from PIL import Image

large_size = (1920, 1200)

im = Image.open("babu_980604.jpeg")

image_w, image_h = im.size
aspect_ratio = image_w / float(image_h)
new_height = int(large_size[0] / aspect_ratio)

if new_height < 1200:
    final_width = large_size[0]
    final_height = new_height
else:
    final_width = int(aspect_ratio * large_size[1])
    final_height = large_size[1]

imaged = im.resize((final_width, final_height), Image.ANTIALIAS)

imaged.show()
imaged.save("out.jpg", quality=90)

这段代码和你的代码的主要区别在于,它直接从打开的图片中获取了 image_wimage_h,而不是通过 get_image_dimensions(image),后者的具体实现没有展示出来。

输出图片

你代码中的一些小问题:

  • 你可以在 with open(...) 之前设置 infile,这样在里面也可以使用。

  • final_size 没有被使用,可以删掉,或者在 im.resize() 中用到它。

  • base_width 可以用 large_size[0] 替代,因为你在其他地方也用到了 large_size[1]

  • image 被设置为 requests.FILES['file'],但你也直接使用了 requests.FILES['file']。可以重复使用 image

  • global random 可能不需要。

撰写回答