# -*- coding: utf-8 -*- """ GEPARD - Gepard-Enabled PARticle Detection Copyright (C) 2018 Lars Bittrich and Josef Brandt, Leibniz-Institut für Polymerforschung Dresden e. V. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program, see COPYING. If not, see . """ import numpy as np from scipy import optimize import cv2 from ..cythonModules.getMaxRect import findMaxRect # TODO: UNCOMMENT!!! #################################################### '''Code taken from https://github.com/pogam/ExtractRect/blob/master/extractRect.py and modified for our use''' #################################################### def residual(angle, img): img_rot, rotationMatrix = rotateImageAroundAngle(img, angle) point1, point2, width, height, max_area = findMaxRect(img_rot) return 1/max_area def getFinalRectangle(angle, img, shiftScaleParam): ny = img.shape[1] img_rot, rotationMatrix = rotateImageAroundAngle(img, angle) point1, point2, width, height, max_area = findMaxRect(img_rot) width *= shiftScaleParam.scale_factor height *= shiftScaleParam.scale_factor #invert rectangle M_invert = cv2.invertAffineTransform(rotationMatrix) rect_coord = [point1, [point1[0],point2[1]] , point2, [point2[0],point1[1]] ] rect_coord_ori = [] for coord in rect_coord: rect_coord_ori.append(np.dot(M_invert,[coord[0],(ny-1)-coord[1],1])) #transform to numpy coord of input image coord_out = [] for coord in rect_coord_ori: coord_out.append([shiftScaleParam.scale_factor*round( coord[0],0)-shiftScaleParam.shift_x,\ shiftScaleParam.scale_factor*round((ny-1)-coord[1],0)-shiftScaleParam.shift_y]) #transpose back the original coords: coord_out = transposeRectCoords(coord_out) return coord_out, round(width), round(height) def transposeRectCoords(rectCoords): #mirror x and y: newCoords = [] for i in range(len(rectCoords)): newCoords.append([rectCoords[i][1], rectCoords[i][0]]) return newCoords def addBorderToMakeImageSquare(img): nx, ny = img.shape if nx != ny: n = max([nx,ny]) img_square = np.ones([n,n]) xshift = (n-nx)/2 yshift = (n-ny)/2 if yshift == 0: img_square[int(xshift):int(xshift+nx),:] = img else: img_square[:,int(yshift):int(yshift+ny)] = img else: xshift = 0 yshift = 0 img_square = img return img_square, xshift, yshift def limitImageSize(img, limitSize): if img.shape[0] > limitSize: img_small = cv2.resize(img,(limitSize, limitSize),interpolation=0) scale_factor = 1.*img.shape[0]/img_small.shape[0] else: img_small = img scale_factor = 1 return img_small, scale_factor def makeImageOddSized(img): # set the input data with an odd number of point in each dimension to make rotation easier nx,ny = img.shape nx_extra = -nx ny_extra = -ny if nx%2==0: nx+=1 nx_extra = 1 if ny%2==0: ny+=1 ny_extra = 1 img_odd = np.ones([img.shape[0]+max([0,nx_extra]),img.shape[1]+max([0,ny_extra])], dtype=np.uint8) img_odd[:-nx_extra, :-ny_extra] = img return img_odd def rotateImageAroundAngle(img, angle): nx, ny = img.shape rotationMatrix = cv2.getRotationMatrix2D(((nx-1)/2, (ny-1)/2), float(angle), 1) img_rot = np.array(cv2.warpAffine(img, rotationMatrix, img.shape, flags=cv2.INTER_NEAREST, borderValue=1)) img_rot = img_rot.astype(np.uint8) return img_rot, rotationMatrix def findRotatedMaximalRectangle(img, nbre_angle=4, limit_image_size=300): img_square, shift_x, shift_y = addBorderToMakeImageSquare(img) img_small, scale_factor = limitImageSize(img_square, limit_image_size) img_odd = makeImageOddSized(img_small) nx,ny = nx_odd, ny_odd = img_odd.shape shiftScaleParam = ShiftAndScaleParam(shift_x, shift_y, scale_factor) # angle_range = ([(90.,180.),]) angle_range = ([(0., 90.), ]) coeff1 = optimize.brute(residual, angle_range, args=(img_odd,), Ns=nbre_angle, finish=None) popt = optimize.fmin(residual, coeff1, args=(img_odd,), xtol=5, ftol=1.e-5, disp=False) opt_angle = popt[0] rectangleCoords, width, height = getFinalRectangle(opt_angle, img_odd, shiftScaleParam) return rectangleCoords, opt_angle, width, height class ShiftAndScaleParam(object): def __init__(self, shift_x=0, shift_y=0, scale_factor=1): self.shift_x = shift_x self.shift_y = shift_y self.scale_factor = scale_factor