gerichtetes Weichzeichnen senkrecht zur Kurve

mit matplotlib, NumPy, pandas, SciPy, SymPy und weiteren mathematischen Programmbibliotheken.
Antworten
Erhy
User
Beiträge: 64
Registriert: Mittwoch 2. Januar 2019, 21:09

Hallo!
Möchte in einem Bild gerichtet Weichzeichnen,
wobei die Richtung gemäß der Senkrechten zur Kurve variable ist.

Die Bestimmung der Richtungen ect. habe ich schon erledigt,
nur die Performance des tatsächlichen Weichzeichnen lässt Wünsche offen.

Hier mein Code

Code: Alles auswählen

import numpy as np
import skimage.draw as skimDraw

def skimDrawLineFromKoords(koord1, koord2) :
	return np.stack( ( skimDraw.line( \
                    koord1[0], koord1[1], \
                    koord2[0], koord2[1]  \
                        			) \
						), axis=-1 )

degsAround = [-90., 90.]
partsBetween90deg = 6

degsDiff = degsAround[1] / np.round( \
    degsAround[1] / ( 90. / partsBetween90deg ) \
)

blurFactorsCount = 6

def blurFactorsCreate():
    # partMinimal = 1.0 / 24.0
    partMinimal = 1.0 / 48.0
    # partMaximal = 1.0 / 3.45
    partMaximal = 1.0 / 24.0
    n = blurFactorsCount
    lsp = np.linspace(partMaximal, partMinimal, n)
    return lsp / ( np.sum(lsp) * 2.0 ) # sum of factors == 0.5
    # blurFactor, whose sum is 0.5
    # blurFactors = np.array( [0.11111111, 0.1       , 0.08888889, 0.07777778, 0.06666667, 0.05555556] )

degTanVals = [] # list of degrees with their tangents
for deg in np.arange( \
    degsAround[0] + degsDiff, degsAround[1] - 0.01 , degsDiff ) :
    # tangents are corrected to be 1 at 45 degree
    degTanVals.append( [ deg, \
        (np.tan(np.radians(deg))) * ( 1. + np.finfo(np.float64).eps ) \
                            ] )
degVarr = 	np.asarray( [li[0] for li in degTanVals])
tanVarr = 	np.asarray( [li[1] for li in degTanVals])

degMidDiff = degsDiff / 2. # difference of middle degree value to values in degVarr

# routines, which want to use the results of this method get following values:
degsForFilter = np.empty((degVarr.shape[0]+1))
degsForFilter[0] = degMidDiff + degsAround[0]
degsForFilter[1:] = degVarr + degMidDiff # each element in degVarr added by ...

# for return also vector components according the degees
# vector components assume a hypothenuse with length of 1000 pixels
blurFactors = blurFactorsCreate()

vektorKomponentsForBlurHypotLen = blurFactors.shape[0]
vektorKomponentsToDrawHypotLen = 10

def vektorKomponentsCreate():

    countOf = blurFactorsCount

    vektorKomponentsForBlurAll = np.empty( ( countOf, 2 ), dtype = np.int32 )
    
    for d in range( countOf ) :
        rad = np.radians(degsForFilter[d])
        sin = np.sin(rad)
        cos = np.cos(rad)
        vektorKomponentsForBlurAll[d,0] = \
            np.int32( np.round( vektorKomponentsForBlurHypotLen * sin ) ) 
        vektorKomponentsForBlurAll[d,1] = \
            np.int32( np.round( vektorKomponentsForBlurHypotLen * cos ) )

    return 	vektorKomponentsForBlurAll
'''
for example:
vektorKomponentsForBlurAll = np.array( [
    [-6,  1], [-6,  2], [-5,  4], [-4,  5], [-2,  6],  [-1,  6],
    [ 1,  6], [ 2,  6], [ 4,  5], [ 5,  4], [ 6,  2],  [ 6,  1]
                                        ], dtype=np.int32 )
'''

def vektorLinesCreate() :
    koord0 = np.array( [0,0], dtype=np.int32 )
    lines = [ skimDrawLineFromKoords( koord0, vektorKomponentsForBlurAll[i] ) \
                for i in range( vektorKomponentsForBlurAll.shape[0] ) ]
    lengthToShorten = min([ lines[i].shape[0] for i in range( lines.__len__() ) ])
    
    linesWithConter = np.empty( ( lines.__len__() * 2, lengthToShorten, 2), dtype=np.int32 )
    # indices here : direction * 2 , number of point in line, koordinate

    for i in range( lines.__len__() ) :
        linesWithConter[ i * 2 ] = \
            np.resize(lines[i],(lengthToShorten, lines[i].shape[1])) \
                if lines[i].shape[0] > lengthToShorten \
                else lines[i]

        # lines in the opposite directions:
        # 0, 2, 4 -> 1,3,5
        linesWithConter[ i * 2 + 1] = linesWithConter[ i * 2 ] * -1
    
    return linesWithConter

vektorKomponentsForBlurAll = vektorKomponentsCreate()

# all line coordinates involved to blur
linesWithConter = vektorLinesCreate()

# normally img_height and img_width are more then 100 times bigger
# then border_width
img_height = 23; img_width = 18
border_width = 7
rgb = np.arange(img_height * img_width * 3, dtype=np.uint8).reshape(img_height, img_width, 3)
rgbFlForAverage = np.stack( ( rgb.astype( np.float64 ), rgb.astype( np.float64 ) ) )
rgbFloat = rgbFlForAverage[0] 

mask = np.zeros_like( rgb[:,:,0] )
mask_inner_selection = slice( border_width, -border_width )
mask_inner = mask[mask_inner_selection, mask_inner_selection]
mask_inner[:,:] = np.arange( mask_inner.shape[0] * mask_inner.shape[1], dtype=np.uint8 ). \
            reshape( mask_inner.shape[0], mask_inner.shape[1] )

pxsKoordToFilter = np.asarray( np.where(mask > 0) )

# orientations_at_all has the calculated direction to filter with
orientations_at_all = np.zeros_like(mask)
orientations_at_all[:,:] = np.arange( img_height * img_width, dtype=np.uint8). \
    reshape( img_height, img_width ) % partsBetween90deg 
orientations_at_all[mask == 0] = 255 # 255 means not in mask
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
#  is it possible that the blur filter procedure performs better ?  # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
for j in range( pxsKoordToFilter.shape[1] ) :
    koord = pxsKoordToFilter[:,j]
    koordTup = tuple(koord)
    direction = orientations_at_all[koordTup]

    rgbFloat[ koordTup ] = sum( [ 
		rgb[ tuple( koord + linesWithConter[ direction * 2, 	i ] ) ] * blurFactors[i] + \
		rgb[ tuple( koord + linesWithConter[ direction * 2 + 1, i ] ) ] * blurFactors[i] \
		for i in range( blurFactors.shape[0] ) ] )
    
    # weighted average in dependence of value in mask
    rgbFloat[ koordTup ] *= mask[koordTup]
    rgbFloat[ koordTup ] += rgbFlForAverage[1][koordTup] * \
            ( 255 - mask[koordTup] )
    
    rgbFloat[ koordTup ] /= 255.0

rgb_blured = ( rgbFloat + 0.499 ).astype(np.uint8)
Erhy
User
Beiträge: 64
Registriert: Mittwoch 2. Januar 2019, 21:09

nun um Faktor 100 schneller:

Code: Alles auswählen

import time
import numpy as np
import skimage.draw as skimDraw

class time_duration:
	def run( self, function_to_test, *args, **kwargs) :
		start = time.perf_counter_ns()
		results = function_to_test( *args, **kwargs )
		end   = time.perf_counter_ns()
		self.duration = end  // 1000000 - start // 1000000
		return results

	def __init__( self ):
		self.duration = 0

duration = 0

def skimDrawLineFromKoords(koord1, koord2) :
	return np.stack( ( skimDraw.line( \
                    koord1[0], koord1[1], \
                    koord2[0], koord2[1]  \
                        			) \
						), axis=-1 )

degsAround = [-90., 90.]
partsBetween90deg = 6

degsDiff = degsAround[1] / np.round( \
    degsAround[1] / ( 90. / partsBetween90deg ) \
)

blurFactorsCount = 5

def blurFactorsCreate():
    # partMinimal = 1.0 / 24.0
    partMinimal = 1.0 / 48.0
    # partMaximal = 1.0 / 3.45
    partMaximal = 1.0 / 24.0
    n = blurFactorsCount
    lsp = np.linspace(partMaximal, partMinimal, n)
    return lsp / ( np.sum(lsp) * 2.0 ) # sum of factors == 0.5
    # blurFactor, whose sum is 0.5
    # blurFactors = np.array( [0.11111111, 0.1       , 0.08888889, 0.07777778, 0.06666667, 0.05555556] )

degTanVals = [] # list of degrees with their tangents
for deg in np.arange( \
    degsAround[0] + degsDiff, degsAround[1] - 0.01 , degsDiff ) :
    # tangents are corrected to be 1 at 45 degree
    degTanVals.append( [ deg, \
        (np.tan(np.radians(deg))) * ( 1. + np.finfo(np.float64).eps ) \
                            ] )
degVarr = 	np.asarray( [li[0] for li in degTanVals])
tanVarr = 	np.asarray( [li[1] for li in degTanVals])

degMidDiff = degsDiff / 2. # difference of middle degree value to values in degVarr

# values for further routines:
degsForFilter = np.empty((degVarr.shape[0]+1))
degsForFilter[0] = degMidDiff + degsAround[0]
degsForFilter[1:] = degVarr + degMidDiff # each element in degVarr added by ...

blurFactors = blurFactorsCreate()

vektorKomponentsForBlurHypotLen = blurFactors.shape[0]

def vektorKomponentsCreate():

    countOf = degsForFilter.shape[0]  # 6 ... was blurFactorsCount

    vektorKomponentsForBlurAll = np.empty( ( countOf, 2 ), dtype = np.int32 )
    
    for d in range( countOf ) :
        rad = np.radians(degsForFilter[d])
        sin = np.sin(rad)
        cos = np.cos(rad)
        vektorKomponentsForBlurAll[d,0] = \
            np.int32( np.round( vektorKomponentsForBlurHypotLen * sin ) ) 
        vektorKomponentsForBlurAll[d,1] = \
            np.int32( np.round( vektorKomponentsForBlurHypotLen * cos ) )

    return 	vektorKomponentsForBlurAll
'''
for example:
vektorKomponentsForBlurAll = np.array( [
    [-6,  1], [-6,  2], [-5,  4], [-4,  5], [-2,  6],  [-1,  6],
    [ 1,  6], [ 2,  6], [ 4,  5], [ 5,  4], [ 6,  2],  [ 6,  1]
                                        ], dtype=np.int32 )
'''

def vektorLinesCreate() :
    koord0 = np.array( [0,0], dtype=np.int32 )
    lines = [ skimDrawLineFromKoords( koord0, vektorKomponentsForBlurAll[i] ) \
                for i in range( vektorKomponentsForBlurAll.shape[0] ) ]
    lengthToShorten = min([ lines[i].shape[0] for i in range( lines.__len__() ) ])
    
    # indices here : direction * 2 , number of point in line, koordinate
    linesWithConter = np.empty( ( lines.__len__() * 2, lengthToShorten, 2), dtype=np.int32 )

    for i in range( lines.__len__() ) :
        linesWithConter[ i * 2 ] = \
            np.resize(lines[i],(lengthToShorten, lines[i].shape[1])) \
                if lines[i].shape[0] > lengthToShorten \
                else lines[i]

        # lines in the opposite directions:
        # 0, 2, 4 -> 1,3,5
        linesWithConter[ i * 2 + 1] = linesWithConter[ i * 2 ] * -1
    
    return linesWithConter

vektorKomponentsForBlurAll = vektorKomponentsCreate()

# all line coordinates involved to blur
linesWithConter = vektorLinesCreate()

# normally img_height and img_width are more then 100 times bigger
# then border_width

# small image for analyzing
# img_height = 23; img_width = 18
# big image to time the filter
img_height = 223; img_width = 218

border_width = 7
rgb = ( np.arange(img_height * img_width * 3, dtype=np.uint32 ).
    reshape(img_height, img_width, 3) % 256 ).astype(np.uint8)

mask = np.zeros_like( rgb[:,:,0] )
mask_inner_selection = slice( border_width, -border_width )
mask_inner = mask[mask_inner_selection, mask_inner_selection]
mask_inner[:,:] = ( np.arange( mask_inner.shape[0] * mask_inner.shape[1], dtype=np.uint32 ). \
            reshape( mask_inner.shape[0], mask_inner.shape[1] ) % 256 ).astype(np.uint8)

# orientations_at_all has the calculated direction to filter with
orientations_at_all = np.zeros_like(mask)
orientations_at_all[:,:] = ( np.arange( img_height * img_width, dtype=np.uint32). \
    reshape( img_height, img_width ) % degsForFilter.shape[0] ).astype(np.uint8)
orientations_at_all[mask == 0] = 255 # 255 means not in mask

#  slow blur filter
def dofilter_slow() :

	pxsKoordToFilter = np.asarray( np.where(mask > 0) )

	rgbFlForAverage = np.stack( ( rgb.astype( np.float64 ), rgb.astype( np.float64 ) ) )
	rgbFloat = rgbFlForAverage[0] 

	# for each pixel, which matters
	for j in range( pxsKoordToFilter.shape[1] ) :
		koord = pxsKoordToFilter[:,j]
		koordTup = tuple(koord)
		direction = orientations_at_all[koordTup]

		rgbFloat[ koordTup ] = sum( [ 
            rgb[ tuple( koord + linesWithConter[ direction * 2, 	i ] ) ] * blurFactors[i] + \
            rgb[ tuple( koord + linesWithConter[ direction * 2 + 1, i ] ) ] * blurFactors[i] \
            for i in range( blurFactors.shape[0] ) ] )
		
        # weighted average of the pixel in dependence of value in mask
		rgbFloat[ koordTup ] *= mask[koordTup]
		rgbFloat[ koordTup ] += rgbFlForAverage[1][koordTup] * \
				( 255 - mask[koordTup] )

		rgbFloat[ koordTup ] /= 255.0

	rgbToShow = ( rgbFloat 	+ 0.499 ).astype(np.uint8)
		
	return 	rgbToShow # return of dofilter_slow()

#  fast blur filter
def dofilter_fast() : 
	# perform with flatten arrays 
	rgb_shape = rgb.shape
	rgb_flat = rgb.reshape( ( rgb_shape[0]*rgb_shape[1], rgb_shape[2] ) )

	# construct helpers
	 
	rgbFlForAverFlat = np.empty(( 2, rgb_flat.shape[0], rgb_flat.shape[1] ), dtype=np.float64 )
	rgbFlForAverFlat[0] = rgb_flat.astype(np.float64) #  with 3 color components
	rgbFlForAverFlat[1] = rgbFlForAverFlat[0]
	rgbFloat = rgbFlForAverFlat[0]  # normally with 3 color components

	mask_flat = mask.flatten()
	pxsKoordToFilterDup = np.where(mask_flat > 0) # indices to act for
	pxsKoordToFilter = np.asarray( pxsKoordToFilterDup )

	orients_flat = orientations_at_all.flatten() # marked orientations
	pxsPreSelected = orients_flat[pxsKoordToFilterDup] # orient. which matters

	# gather all indices for each possible orientation separately
	pxsKooForDirs = []
	for d in range( linesWithConter.shape[0] // 2 ) :
		dupj = np.where( pxsPreSelected == d ) # use it, to take from pxsKoordToFilter
		pxsKooForDirs.append( dupj )

	# maximal index offset
	iXsDiffMax = np.max(np.abs(linesWithConter))

	# construct slice parameters for further views
	sliceParams = np.empty((iXsDiffMax * 2 + 1, 2), dtype=object)
	for i in range(-iXsDiffMax, iXsDiffMax+1):
		sliceParams[i+iXsDiffMax] = [iXsDiffMax+i, -iXsDiffMax+i]
	sliceParams[ sliceParams == 0 ] = None # to work with arrays with arbitrary sizes
	# slices :
	sls = [ slice( sliceParams[i][0], sliceParams[i][1] ) \
		for i in range( sliceParams.shape[0] ) ]

	# bigger image to have views of them
	imgW = np.full( (rgb.shape[0]+iXsDiffMax*2, rgb.shape[1]+iXsDiffMax*2, rgb.shape[2] ), 
					255, dtype=rgb.dtype)
	imgW[iXsDiffMax:-iXsDiffMax, iXsDiffMax:-iXsDiffMax] = rgb

	l_offsetted = linesWithConter + iXsDiffMax # to have no negative values in it
	dirList_with_F_lists = []
	for d in range( l_offsetted.shape[0] ) : # for each direction
		a_List = [ None ] # first index not used
		for a in range( 1, l_offsetted.shape[1] ) : # for each attenuation
			# gather all needed views in which the rgb array is shifted
			i_to_append = imgW[ sls[ l_offsetted[d,a,0]],
								sls[ l_offsetted[d,a,1]] \
							]
			a_List.append( i_to_append.reshape( 
				( i_to_append.shape[0] * i_to_append.shape[1], i_to_append.shape[2] ) ) )

		dirList_with_F_lists.append( a_List )

	# filter activity
	
	blurFact0 = blurFactors[0] * 2
	for d in range( 0, l_offsetted.shape[0] , 2 ) : # for each orientation
		dHalf = d // 2
		if pxsKooForDirs[dHalf][0].shape[0] > 0 :
			iXsTuple = tuple( np.take(pxsKoordToFilter, pxsKooForDirs[dHalf] ) )
			factArrList1 = dirList_with_F_lists[d]
			factArrList2 = dirList_with_F_lists[d+1] # for the opposite direction
			rgbFloat[ iXsTuple ] = rgb_flat[ iXsTuple ] * blurFact0
			for a in range( 1, l_offsetted.shape[1] ) : # for each attenuation
				rgbFloat[ iXsTuple ] += \
					factArrList1[a][ iXsTuple ] * blurFactors[a] + \
					factArrList2[a][ iXsTuple ] * blurFactors[a]

	# weighted average in dependence of value in mask
	mask_flat_selected = np.expand_dims( mask_flat[ pxsKoordToFilterDup ], axis=1 )
	rgbFloat[ pxsKoordToFilterDup ] *= mask_flat_selected
	rgbFloat[ pxsKoordToFilterDup ] += rgbFlForAverFlat[1][pxsKoordToFilterDup] * \
                ( 255 - mask_flat_selected )
	rgbFloat[ pxsKoordToFilterDup ] /= 255.0

	rgbToShow = ( rgbFloat.reshape( rgb_shape ) + 0.499 ).astype(np.uint8)

	return rgbToShow # return of dofilter_fast()

# test and compare both filters

timerObj  = time_duration()
blur_fast = timerObj.run(dofilter_fast)
result_fast = timerObj.duration
blur_slow = timerObj.run(dofilter_slow)
result_slow = timerObj.duration

if not np.array_equal(blur_slow, blur_fast) :
	print('the arrays differ !!!')
else:
	print('the quotient of durations to blur: ', 
		np.int64( result_slow / result_fast ) )
Antworten