@@ -242,25 +242,21 @@ def compute_haralick_features(im_label, im_intensity, offsets=None,
242
242
243
243
# check for consistent shapes between 'I' and 'Label'
244
244
if im_intensity .shape != im_label .shape :
245
- msg = " Inputs 'I' and ' Label' must have same shape"
246
- raise ValueError (msg )
245
+ err_str = ' Inputs I and Label must have same shape'
246
+ raise ValueError (err_str )
247
247
248
248
num_dims = len (im_intensity .shape )
249
249
250
250
# offsets
251
251
if offsets is None :
252
-
253
252
# set default offset value
254
253
offsets = _default_offsets (im_intensity )
255
254
256
255
else :
257
-
258
256
# check sanity
259
257
if offsets .shape [1 ] != num_dims :
260
- msg = 'Dimension mismatch between input image and offsets'
261
- raise ValueError (
262
- msg ,
263
- )
258
+ err_str = 'Dimension mismatch between input image and offsets'
259
+ raise ValueError (err_str )
264
260
265
261
num_offsets = offsets .shape [0 ]
266
262
@@ -270,18 +266,25 @@ def compute_haralick_features(im_label, im_intensity, offsets=None,
270
266
271
267
# create pandas data frame containing the features for each object
272
268
numLabels = len (rprops )
273
- fdata = pd .DataFrame (np .zeros ((numLabels , len (agg_feature_list ))),
274
- columns = agg_feature_list )
269
+ fdata = pd .DataFrame (
270
+ np .zeros ((numLabels , len (agg_feature_list ))), columns = agg_feature_list ,
271
+ )
275
272
276
273
n_Minus = np .arange (num_levels )
277
274
n_Plus = np .arange (2 * num_levels - 1 )
278
275
279
276
x , y = np .mgrid [0 :num_levels , 0 :num_levels ]
280
277
xy = x * y
281
- xy_IDM = 1. / (1 + np .square (x - y ))
278
+ xy_IDM = 1.0 / (1 + np .square (x - y ))
282
279
283
280
e = 0.00001 # small positive constant to avoid log 0
284
- eps = np .finfo (float ).eps # small constant to avoid div / 0
281
+
282
+ num_features = len (feature_list )
283
+
284
+ # Initialize the array for aggregated features
285
+ aggregated_features = np .zeros (
286
+ (numLabels , 2 * num_features ),
287
+ ) # Alternating mean and range
285
288
286
289
for i in range (numLabels ):
287
290
if rprops [i ] is None :
@@ -291,92 +294,103 @@ def compute_haralick_features(im_label, im_intensity, offsets=None,
291
294
minr , minc , maxr , maxc = rprops [i ].bbox
292
295
293
296
# grab nucleus mask
294
- subImage = im_intensity [minr :maxr + 1 , minc :maxc + 1 ]
297
+ subImage = im_intensity [minr : maxr + 1 , minc : maxc + 1 ]. astype ( np . uint8 )
295
298
296
299
# gets GLCM or gray-tone spatial dependence matrix
297
- arrayGLCM = graycomatrixext (subImage , offsets = offsets ,
298
- num_levels = num_levels ,
299
- gray_limits = gray_limits ,
300
- symmetric = True , normed = True )
300
+ arrayGLCM = graycomatrixext (
301
+ subImage ,
302
+ offsets = offsets ,
303
+ num_levels = num_levels ,
304
+ gray_limits = gray_limits ,
305
+ symmetric = True ,
306
+ normed = True ,
307
+ )
301
308
302
- # Compute haralick features for each offset
303
- ldata = pd .DataFrame (np .zeros ((num_offsets , len (feature_list ))),
304
- columns = feature_list )
309
+ features_per_offset = np .zeros ((num_offsets , num_features ))
305
310
306
311
for r in range (num_offsets ):
307
-
308
312
nGLCM = arrayGLCM [:, :, r ]
309
313
310
314
# get marginal-probabilities
311
- px , py , pxPlusy , pxMinusy = _compute_marginal_glcm_probs_cython (
312
- nGLCM )
315
+ px , py , pxPlusy , pxMinusy = _compute_marginal_glcm_probs_cython (nGLCM )
313
316
314
317
# computes angular second moment
315
- ldata . at [ r , 'Haralick. ASM' ] = np .sum (np .square (nGLCM ))
318
+ ASM = np .sum (np .square (nGLCM ))
316
319
317
320
# computes contrast
318
- ldata .at [r , 'Haralick.Contrast' ] = \
319
- np .dot (np .square (n_Minus ), pxMinusy )
321
+ Contrast = np .dot (np .square (n_Minus ), pxMinusy )
320
322
321
323
# computes correlation
322
324
# gets weighted mean and standard deviation of px and py
323
325
meanx = np .dot (n_Minus , px )
324
326
variance = np .dot (px , np .square (n_Minus )) - np .square (meanx )
325
327
nGLCMr = np .ravel (nGLCM )
326
-
327
- har_corr = (np .dot (np .ravel (xy ), nGLCMr ) - np .square (meanx )) / \
328
- max (eps , variance )
329
- ldata .at [r , 'Haralick.Correlation' ] = np .clip (har_corr ,
330
- a_min = - 1 , a_max = 1 )
328
+ Correlation = (np .dot (np .ravel (xy ), nGLCMr ) - np .square (meanx )) / variance
331
329
332
330
# computes sum of squares : variance
333
- ldata . at [ r , 'Haralick. SumOfSquares' ] = variance
331
+ SumOfSquares = variance
334
332
335
333
# computes inverse difference moment
336
- ldata .at [r , 'Haralick.IDM' ] = \
337
- np .dot (np .ravel (xy_IDM ), nGLCMr )
334
+ IDM = np .dot (np .ravel (xy_IDM ), nGLCMr )
338
335
339
336
# computes sum average
340
- ldata .at [r , 'Haralick.SumAverage' ] = \
341
- np .dot (n_Plus , pxPlusy )
337
+ SumAverage = np .dot (n_Plus , pxPlusy )
342
338
343
339
# computes sum variance
344
340
# [1] uses sum entropy, but we use sum average
345
- ldata .at [r , 'Haralick.SumVariance' ] = \
346
- np .dot (np .square (n_Plus ), pxPlusy ) - \
347
- np .square (ldata .at [r , 'Haralick.SumAverage' ])
341
+ SumVariance = np .dot (np .square (n_Plus ), pxPlusy ) - np .square (SumAverage )
348
342
349
343
# computes sum entropy
350
- ldata .at [r , 'Haralick.SumEntropy' ] = \
351
- - np .dot (pxPlusy , np .log2 (pxPlusy + e ))
344
+ SumEntropy = - np .dot (pxPlusy , np .log2 (pxPlusy + e ))
352
345
353
346
# computes entropy
354
- ldata .at [r , 'Haralick.Entropy' ] = \
355
- - np .dot (nGLCMr , np .log2 (nGLCMr + e ))
347
+ Entropy = - np .dot (nGLCMr , np .log2 (nGLCMr + e ))
356
348
357
349
# computes variance px-y
358
- ldata . at [ r , 'Haralick. DifferenceVariance' ] = np .var (pxMinusy )
350
+ DifferenceVariance = np .var (pxMinusy )
359
351
360
352
# computes difference entropy px-y
361
- ldata .at [r , 'Haralick.DifferenceEntropy' ] = \
362
- - np .dot (pxMinusy , np .log2 (pxMinusy + e ))
353
+ DifferenceEntropy = - np .dot (pxMinusy , np .log2 (pxMinusy + e ))
363
354
364
355
# computes information measures of correlation
365
356
# gets entropies of px and py
366
357
HX = - np .dot (px , np .log2 (px + e ))
367
358
HY = - np .dot (py , np .log2 (py + e ))
368
- HXY = ldata . at [ r , 'Haralick. Entropy' ]
359
+ HXY = Entropy
369
360
pxy_ij = np .outer (px , py )
370
361
pxy_ijr = np .ravel (pxy_ij )
371
362
HXY1 = - np .dot (nGLCMr , np .log2 (pxy_ijr + e ))
372
363
HXY2 = - np .dot (pxy_ijr , np .log2 (pxy_ijr + e ))
373
- ldata . at [ r , 'Haralick. IMC1' ] = (( HXY - HXY1 ) / max (HX , HY )) if max ( HX , HY ) else 0
364
+ IMC1 = (HXY - HXY1 ) / max (HX , HY )
374
365
375
366
# computes information measures of correlation
376
- ldata .at [r , 'Haralick.IMC2' ] = \
377
- np .sqrt (max (0 , 1 - np .exp (- 2.0 * (HXY2 - HXY ))))
378
-
379
- fdata .values [i , ::2 ] = np .mean (ldata .values , axis = 0 )
380
- fdata .values [i , 1 ::2 ] = np .ptp (ldata .values , axis = 0 )
367
+ IMC2 = np .sqrt (1 - np .exp (- 2.0 * (HXY2 - HXY )))
368
+
369
+ features_per_offset [r ] = [
370
+ ASM ,
371
+ Contrast ,
372
+ Correlation ,
373
+ SumOfSquares ,
374
+ IDM ,
375
+ SumAverage ,
376
+ SumVariance ,
377
+ SumEntropy ,
378
+ Entropy ,
379
+ DifferenceVariance ,
380
+ DifferenceEntropy ,
381
+ IMC1 ,
382
+ IMC2 ,
383
+ ]
384
+
385
+ # Calculate means and ranges across all features in a vectorized manner
386
+ means = np .mean (features_per_offset , axis = 0 )
387
+ ranges = np .ptp (features_per_offset , axis = 0 )
388
+
389
+ # Assign means and ranges to the aggregated_features array in alternating columns
390
+ aggregated_features [i , ::2 ] = means
391
+ aggregated_features [i , 1 ::2 ] = ranges
392
+
393
+ # Preparing DataFrame columns with alternating mean and range suffixes
394
+ fdata = pd .DataFrame (aggregated_features , columns = agg_feature_list )
381
395
382
396
return fdata
0 commit comments