Сделать объекты изображения ближе друг к другу - PullRequest
1 голос
/ 30 марта 2020

У меня нет большого опыта работы с PIL, и у меня есть эти изображения, отредактированные из стопки ячеек микроскопических изображений, каждая из которых находится в маске с размером изображения 30x30. Я изо всех сил пытался поместить эти клетки на черном фоне как можно ближе друг к другу, не перекрывая друг друга.

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

def spread_circles(circles, rad, iterations,step):
    radsqr = rad**2
    for i in range(iterations):
        for ix,c in enumerate(circles):
            vecs = c-circles
            dists = np.sum((vecs)**2,axis=1)
            if len(dists)>0:
                push = (vecs[dists<radsqr,:].T*dists[dists<radsqr]).T
                push = np.sum(push,axis=0)
                pushmag = np.sum(push*push)**0.5
                if pushmag>0:
                    push = push/pushmag*step
                    circles[ix]+=push
    return circles



def gen_image(sample,n_iter, height=850, width = 850, max_shape=30, num_circles=150):
    circles = np.random.uniform(low=max_shape,high=height-max_shape,size=(num_circles,2))   
    circles = spread_circles(circles, max_shape, n_iter, 1).astype(int)
    img = Image.new(mode='F',size=(height,width),color=0).convert('RGBA')
    final1 = Image.new("RGBA", size=(height,width))
    final1.paste(img, (0,0), img)
    for n,c in enumerate(circles):
        foreground = sample[n]       
        final1.paste(foreground, (c[0],c[1]), foreground)     
    return final1 

Но трудно избежать наложения, если я сделаю несколько итераций, и если я увеличу их, они будут слишком разбросаны, например:

enter image description here

То, что я хочу, это что-то похожее на нарисованные мною красные круги:

enter image description here

Мне нужно, чтобы они были как можно ближе, почти как плитки. Как я могу это сделать?

1 Ответ

1 голос
/ 31 марта 2020

Я начал думать об этом и реализовал несколько стратегий. Любой, кто хочет повеселиться, может заимствовать, украсть, присвоить или взломать любые фрагменты моего кода, которые они могут использовать! Я, вероятно, сыграю еще немного завтра.

#!/usr/bin/env python3

from PIL import Image, ImageOps
import numpy as np
from glob import glob
import math

def checkCoverage(im):
   """Determines percentage of image that is cells rather than background"""
   N = np.count_nonzero(im)
   return N * 100 / im.size


def loadImages():
   """Load all cell images in current directory into list of trimmed Numpy arrays"""
   images = []
   for filename in glob('*.png'):
      # Open and convert to greyscale
      im = Image.open(filename).convert('L')
      # Trim to bounding box
      im = im.crop(im.getbbox())
      images.append(np.array(im))
   return images

def Strategy1():
   """Get largest image and pad all images to that size - at least it will tesselate perfectly"""
   images = loadImages()
   N = len(images)
   # Find height of tallest image and width of widest image
   maxh = max(im.shape[0] for im in images)
   maxw = max(im.shape[1] for im in images)
   # Determine how many images we will pack across and down the output image - could be improved
   Nx = int(math.sqrt(N))+1
   Ny = int(N/Nx)+1
   print(f'Padding {N} images each to height:{maxh} x width:{maxw}')
   # Create output image
   res = Image.new('L', (Nx*maxw,Ny*maxh), color=0)
   # Pack all images from list onto regular grid
   x, y = 0, 0
   for im in images:
      this = Image.fromarray(im)
      h, w = im.shape
      # Pack this image into top-left of its grid-cell, unless
      # a) in first row, in which case pack to bottom
      # b) in first col, in which case pack to right
      thisx = x*maxw
      thisy = y*maxh
      if y==0:
         thisy += maxh - h
      if x==0:
         thisx += maxw - w
      res.paste(this, (thisx,thisy))
      x += 1
      if x==Nx:
         x  = 0
         y += 1

   # Trim extraneous black edges
   res = res.crop(res.getbbox())
   # Save as JPEG so we don't find it as a PNG in next strategy
   res.save('strategy1.jpg')
   cov = checkCoverage(np.array(res))
   print(f'Strategy1 coverage: {cov}')


def Strategy2():
   """Rotate all images to portrait (tall rather than wide) and order by height so we tend to stack equal height images side-by-side"""
   tmp = loadImages()
   # Recreate list with all images in portrait format, i.e. tall
   portrait = []
   for im in tmp:
      if im.shape[0] >= im.shape[1]:
         # Already portrait, add as-is
         portrait.append(im)
      else:
         # Landscape, so rotate
         portrait.append(np.rot90(im))
   images = sorted(portrait, key=lambda x: x.shape[0], reverse=True)
   N = len(images)
   maxh, maxw = 31, 31
   # Determine how many images we will pack across and down the output image
   Nx = int(math.sqrt(N))+1
   Ny = int(N/Nx)+1
   print(f'Packing images by height')
   # Create output image
   resw, resh = Nx*maxw, Ny*maxh
   res = Image.new('L', (resw,resh), color=0)
   # Pack all from list 
   xpos, ypos = 0, 0
   # Pack first row L->R, second row R->L and alternate
   packToRight = True
   for im in images:
      thisw, thish = im.shape
      this = Image.fromarray(im)
      if packToRight:
         if xpos+thisw < resw:
            # If it fits to the right, pack it there
            res.paste(this,(xpos,ypos))
            xpos += thisw
         else:
            # Else start a new row, pack at right end and continue packing to left
            packToRight = False
            res.paste(this,(resw-thisw,ypos))
            ypos = res.getbbox()[3]
      else:
         if xpos>thisw:
            # If it fits to the left, pack it there
            res.paste(this,(xpos-thisw,ypos))
            xpos -= thisw
         else:
            # Else start a new row, pack at left end and continue packing to right
            ypos = res.getbbox()[3]
            packToRight = True
            res.paste(this,(0,ypos))

   # Trim any black edges
   res = res.crop(res.getbbox())
   # Save as JPEG so we don't find it as a PNG in next strategy
   res.save('strategy2.jpg')
   cov = checkCoverage(np.array(res))
   print(f'Strategy2 coverage: {cov}')

Strategy1()
Strategy2()

Strategy1 дает это при 42% охвате:

enter image description here

Стратегия2 дает это при покрытии 64%:

enter image description here

...