Warm tip: This article is reproduced from stackoverflow.com, please click
django python-3.x python-imaging-library

draw multiple text on image with PIL django python

发布于 2020-03-29 21:03:13

currently its drawing one text on thumbnail and i want to draw multiple watermark on image thumbnail

models.py:

class Image(models.Model):
      license_type = (
            ('Royalty-Free','Royalty-Free'),
            ('Rights-Managed','Rights-Managed')
          )
      image_number = models.CharField(default=random_image_number,max_length=12,unique=True)
      title = models.CharField(default=random_image_number,max_length = 100)
      image = models.ImageField(upload_to = 'image' , default = 'demo/demo.png')
      thumbnail = models.ImageField(upload_to='thumbs', blank=True, null=True)
      category = models.ForeignKey('Category', null=True, blank=True, on_delete=models.CASCADE)
      shoot = models.ForeignKey(ImageShoot, on_delete=models.CASCADE, related_name='Image', null=True,blank=True)
      image_keyword = models.CharField(max_length=500)
      description = models.TextField(max_length=3000,null=True,blank=True)
      credit = models.CharField(max_length=150, null=True,blank=True)
      location = models.CharField(max_length=100, null=True,blank=True)
      license_type = models.CharField(max_length=20,choices=license_type, default='')
      uploaded_at = models.TimeField(auto_now_add=True)

      def __str__(self):
          return self.title

      def save(self, *args, **kwargs):    
         super(Image, self).save(*args, **kwargs)
         if not self.make_thumbnail():
            raise Exception('Could not create thumbnail - is the file type valid?')

     def make_thumbnail(self):
         TXT_BOX = (1300, 1300)  # Depends on the font size you choose
         REPEAT_X = 3  # How close together horizontally
         REPEAT_Y = 4  # How close together vertically
         fh = storage.open(self.image.path)
         base = PILImage.open(fh).convert('RGBA')
         base.load()
         width, height = base.size
# Draw one text box with the rotated text, fixed size, independent of base.size
         txt = PILImage.new('RGBA', TXT_BOX, (255, 255, 255, 0))
         fnt = ImageFont.truetype('arial.ttf', 100)  # change font and size
         d = ImageDraw.Draw(txt)
         d.text((5, 5), "liveimages.in", font=fnt, fill=(255, 255, 255, 128))
         txt = txt.rotate(45, expand=True)  # use expand to make sure the box still fits

   # draw step_x * step_y boxes
   # if REPEAT_X and REPEAT_Y are 1 the boxes are too far apart
        step_x = int(base.size[0] / txt.size[0] * REPEAT_X)
        step_y = int(base.size[1] / txt.size[1] * REPEAT_Y)

        for x_ratio in range(0, step_x):
            x = int(width * x_ratio / step_x)
            for y_ratio in range(0, step_y):
                y = int(height * y_ratio / step_y)
                base.alpha_composite(txt, dest=(x, y))  # in-place adding of txt
       base.thumbnail((1000, 1000), PILImage.ANTIALIAS)
       fh.close()

       thumb_name, thumb_extension = os.path.splitext(self.image.name)
       thumb_extension = thumb_extension.lower()

       thumb_filename = thumb_name + '_thumb' + thumb_extension
# Save the new image    
       i_out = base.convert('RGB')
       temp_thumb = BytesIO()        
       i_out.save(temp_thumb, 'JPEG')
       temp_thumb.seek(0)
       if not self.thumbnail:
    # Load a ContentFile into the thumbnail field so it gets saved
          self.thumbnail.save(thumb_filename, ContentFile(temp_thumb.read()), save=True)
          temp_thumb.close()
       return True

............................................................................................................... i want a thumbnail like this from uploaded image:

enter image description here

generated image:

enter image description here

Questioner
afk
Viewed
14
dirkgroten 2020-01-31 18:57

I've tried a few settings, you might want to play with them until you get the optimal result. But the main idea is:

  • Create a square image with the rotated text in it. Only one text, not multiple texts. So only "liveimages.in" rotated 45°.
  • Then place that square image multiple times on top of your base image using alpha_composite(txt, (x, y)).

This is what I get:

TXT_BOX = (160, 160)  # Depends on the font size you choose
FONT_SIZE = 25
REPEAT_X = 3  # How close together horizontally
REPEAT_Y = 2  # How close together vertically

def make_thumbnail(self):
    fh = default_storage.open(self.image.path)
    base = PILImage.open(fh).convert('RGBA')
    base.load()
    width, height = base.size

    # Draw one text box with the rotated text, fixed size, independent of base.size
    txt = PILImage.new('RGBA', TXT_BOX, (255, 255, 255, 0))
    fnt = ImageFont.truetype('Avenir.ttc', FONT_SIZE)  # change font and size
    d = ImageDraw.Draw(txt)
    d.text((5, 5), "liveimages.in", font=fnt, fill=(255, 255, 255, 128))
    txt = txt.rotate(45, expand=True)  # use expand to make sure the box still fits

    # draw step_x * step_y boxes
    # if REPEAT_X and REPEAT_Y are 1 the boxes are too far apart
    step_x = int(base.size[0] / txt.size[0] * REPEAT_X)
    step_y = int(base.size[1] / txt.size[1] * REPEAT_Y)
    print(f"Steps: ({step_x}, {step_y})")
    for x_ratio in range(0, step_x):
        x = int(width * x_ratio / step_x)
        for y_ratio in range(0, step_y):
            y = int(height * y_ratio / step_y)
            base.alpha_composite(txt, dest=(x, y))  # in-place adding of txt
    base.thumbnail((1000, 1000), PILImage.ANTIALIAS)
    fh.close()

    # Save the new image    
    i_out = base.convert('RGB')
    temp_thumb = BytesIO()        
    i_out.save(temp_thumb, FTYPE)
    temp_thumb.seek(0)
    if not self.thumbnail:
        # Load a ContentFile into the thumbnail field so it gets saved
        self.thumbnail.save(thumb_filename, ContentFile(temp_thumb.read()), save=True)
        temp_thumb.close()
    return True