Update to support i18n & logging
- Add --log-file option - Add internationalization support with gettext - Add logging featur - Remove verbosity flag - Update string formatting - Fix Plate::set_border method
This commit is contained in:
parent
d51bd3ec9c
commit
8b604d8ae2
1 changed files with 126 additions and 40 deletions
|
@ -1,7 +1,13 @@
|
||||||
from numpy import ndarray, argmax, max, quantile, arange, where, convolve, ones
|
from numpy import ndarray, gradient, ones, argmax, arange, arctan, tan
|
||||||
from scipy.optimize import curve_fit
|
from scipy.optimize import curve_fit
|
||||||
|
from scipy.signal import convolve
|
||||||
|
from cv2 import getRotationMatrix2D, warpAffine, INTER_NEAREST
|
||||||
from classes.science.border import Border
|
from classes.science.border import Border
|
||||||
from function.utils import find_point, fill
|
from function.utils import find_point, fill
|
||||||
|
from function.fit import linear
|
||||||
|
|
||||||
|
from logging import getLogger
|
||||||
|
from gettext import gettext as _
|
||||||
|
|
||||||
class Plate:
|
class Plate:
|
||||||
"""
|
"""
|
||||||
|
@ -10,15 +16,17 @@ class Plate:
|
||||||
|
|
||||||
def __init__( self , data ):
|
def __init__( self , data ):
|
||||||
if not isinstance( data , ndarray ):
|
if not isinstance( data , ndarray ):
|
||||||
raise TypeError( 'data must be a ndarray' )
|
raise TypeError( _( 'data must be a ndarray' ) )
|
||||||
|
if len( data.shape ) != 2:
|
||||||
|
raise ValueError( _( 'data must be a 2d matrix' ) )
|
||||||
self.data = data
|
self.data = data
|
||||||
self.set_border()
|
self.set_border()
|
||||||
|
|
||||||
def set_border( self , factor = 10 ):
|
def set_border( self ):
|
||||||
"""
|
"""
|
||||||
Set current border (without area outside the plate)
|
Set current border (without area outside the plate)
|
||||||
"""
|
"""
|
||||||
compressed = self.compress( factor )
|
compressed , factor = self.compress()
|
||||||
|
|
||||||
points = self.get_points( compressed )
|
points = self.get_points( compressed )
|
||||||
self.border = Border()
|
self.border = Border()
|
||||||
|
@ -39,16 +47,37 @@ class Plate:
|
||||||
taken_points = fill(
|
taken_points = fill(
|
||||||
compressed,
|
compressed,
|
||||||
point ,
|
point ,
|
||||||
1000 , # intensity threshold
|
2000 , # intensity threshold
|
||||||
)
|
)
|
||||||
|
|
||||||
x = [ taken_point[1] for taken_point in taken_points ]
|
x = [ taken_point[1] for taken_point in taken_points ]
|
||||||
y = [ taken_point[0] for taken_point in taken_points ]
|
y = [ taken_point[0] for taken_point in taken_points ]
|
||||||
|
|
||||||
|
"""
|
||||||
|
matrix = ones( compressed.shape )
|
||||||
|
for taken_point in taken_points:
|
||||||
|
matrix[ taken_point[0] , taken_point[1] ] = 0
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
plt.plot(
|
||||||
|
[ point[1] ] ,
|
||||||
|
[ point[0] ] ,
|
||||||
|
linestyle = '' ,
|
||||||
|
marker = 'x' ,
|
||||||
|
markersize = 15 ,
|
||||||
|
markeredgecolor = 'red',
|
||||||
|
markeredgewidth = 5 ,
|
||||||
|
)
|
||||||
|
plt.imshow( compressed , aspect = 'auto' )
|
||||||
|
plt.imshow( matrix , aspect = 'auto' , alpha = 0.5 )
|
||||||
|
plt.show()
|
||||||
|
"""
|
||||||
|
|
||||||
|
if len( x ) > 5 and len( y ) > 5:
|
||||||
if max( x ) < x_half:
|
if max( x ) < x_half:
|
||||||
if self.border.x.min < max( x ):
|
if self.border.x.min < max( x ):
|
||||||
self.border.x.min = max( x ) # biggest min
|
self.border.x.min = max( x ) # biggest min
|
||||||
elif min( x ) > x_half: # elif to only accept one side
|
elif min( x ) > x_half:
|
||||||
|
# elif to only accept one side
|
||||||
if self.border.x.max > min( x ):
|
if self.border.x.max > min( x ):
|
||||||
self.border.x.max = min( x ) # smallest max
|
self.border.x.max = min( x ) # smallest max
|
||||||
elif max( y ) < y_half:
|
elif max( y ) < y_half:
|
||||||
|
@ -116,36 +145,93 @@ class Plate:
|
||||||
|
|
||||||
return first_column + last_column + first_line + last_line
|
return first_column + last_column + first_line + last_line
|
||||||
|
|
||||||
def compress( self , factor ):
|
def compress( self ):
|
||||||
|
"""
|
||||||
|
Compress the plate data to fit the biggest dimension in a 1000
|
||||||
|
pixels axis and the smallest in a 100 pixels axis at minimum.
|
||||||
|
Return the compressed data and the compression factor used.
|
||||||
|
"""
|
||||||
|
min_factor = max( self.data.shape ) // 1000 # min factor to have a side
|
||||||
|
# with a maximum of 1000 pixels
|
||||||
|
max_factor = min( self.data.shape ) // 100 # max factor to have
|
||||||
|
# a side with a minimum of 100 pixel
|
||||||
|
if min_factor < max_factor:
|
||||||
|
factor = int( mean( ( max_factor , min_factor ) ) )
|
||||||
|
else: # the smallest side will be less than 100 pixels with the
|
||||||
|
# minimum compression factor
|
||||||
|
logger = getLogger( 'naroo reader' )
|
||||||
|
logger.warning(
|
||||||
|
_( (
|
||||||
|
'slow compression: ratio between height and width'
|
||||||
|
' is greater than 10 ({ratio:.2f})'
|
||||||
|
) ).format(
|
||||||
|
ratio = max( self.size() ) / min( self.size() )
|
||||||
|
)
|
||||||
|
)
|
||||||
|
factor = max_factor
|
||||||
return self.data[
|
return self.data[
|
||||||
: : factor,
|
: : factor,
|
||||||
: : factor,
|
: : factor,
|
||||||
]
|
] , factor
|
||||||
|
|
||||||
|
def middle( self ):
|
||||||
|
"""
|
||||||
|
Get coordinate of center
|
||||||
|
"""
|
||||||
|
return (
|
||||||
|
self.size()[0] // 2,
|
||||||
|
self.size()[1] // 2,
|
||||||
|
)
|
||||||
|
|
||||||
def rotate( self ):
|
def rotate( self ):
|
||||||
"""
|
"""
|
||||||
Auto-rotate to be vertically and horizontally aligned
|
Auto-rotate to be vertically and horizontally aligned
|
||||||
"""
|
"""
|
||||||
maxes = max(
|
indexes_max = argmax(
|
||||||
self.data[ self.border.slice() ],
|
convolve(
|
||||||
axis = 0 ,
|
self.data[
|
||||||
)
|
1 * self.border.y.size() // 4:
|
||||||
indexes = where(
|
3 * self.border.y.size() // 4,
|
||||||
maxes > quantile( maxes , 0.5 )
|
1 * self.border.x.size() // 4:
|
||||||
|
3 * self.border.x.size() // 4
|
||||||
|
] ,
|
||||||
|
ones( ( 500 , 1 ) ),
|
||||||
|
'valid' ,
|
||||||
|
) ,
|
||||||
|
axis = 0,
|
||||||
)
|
)
|
||||||
abciss = arange(
|
abciss = arange(
|
||||||
self.border.x.min,
|
1 * self.border.x.size() // 4,
|
||||||
self.border.x.max
|
3 * self.border.x.size() // 4
|
||||||
)[ indexes ]
|
)
|
||||||
indexes_max = argmax(
|
fit_result = curve_fit(
|
||||||
self.data[ self.border.slice() ],
|
linear ,
|
||||||
axis = 0 ,
|
abciss ,
|
||||||
)[ indexes ]
|
indexes_max,
|
||||||
indexes_max = convolve(
|
)[0]
|
||||||
indexes_max ,
|
|
||||||
ones( 100 ) ,
|
angle = arctan( fit_result[0] ) # rad
|
||||||
'same' ,
|
diff = int( # adjust height border
|
||||||
) / 100
|
tan( angle ) * ( self.border.x.size() )
|
||||||
import matplotlib.pyplot as plt
|
)
|
||||||
plt.plot( abciss , indexes_max )
|
|
||||||
plt.show()
|
rotation_matrix = getRotationMatrix2D(
|
||||||
|
self.middle(),
|
||||||
|
angle ,
|
||||||
|
1 ,
|
||||||
|
)
|
||||||
|
self.data = warpAffine(
|
||||||
|
self.data ,
|
||||||
|
rotation_matrix,
|
||||||
|
self.size() ,
|
||||||
|
flags = INTER_NEAREST,
|
||||||
|
)
|
||||||
|
|
||||||
|
self.border.y.min -= diff
|
||||||
|
self.border.y.max -= diff
|
||||||
|
|
||||||
|
def size( self ):
|
||||||
|
"""
|
||||||
|
get plate size
|
||||||
|
"""
|
||||||
|
return self.data.shape
|
||||||
|
|
Loading…
Reference in a new issue