I solved this by still using Otsu to find the threshold value. I then just filter the the regions with value == 1
in the thresholded signal (i.e. the region_mask
; green line in plot below) by their width.
This is the result (see below for the code):
plt.plot(signal_orig)
plt.plot(signal)
plt.plot(region_mask * np.max(signal))
plt.show()
Code:
signal = savgol_filter(signal_orig, 51, 1) # window size 51, polynomial order 1
threshold = threshold_otsu(signal)
region_mask = signal > threshold
region_list = get_regions_from_mask(region_mask)
region_list = filter_date_regions_by_width(region_list, minimum_region_width=200)
def filter_date_regions_by_width(region_list, minimum_region_width):
"""
Filters removes data regions from region_list with width < minimum_region_width.
:param region_list: list of DataRegion objects, that we want to filter.
:param minimum_region_width: minimum width of a region, so that it is not filtered out.
:return:
"""
filtered_list = []
for region in region_list:
if region.width >= minimum_region_width:
filtered_list.append(region)
return filtered_list
def get_regions_from_mask(region_mask):
"""
Get list of the DataRegion objects that were found inside region_mask.
:param region_mask: the np.array of 0 and 1 in which we want find regions of value == 1.
:return: region_list: a list of DataRegion objects.
"""
region_list = [] # list that will hold all regions that were found
region = DataRegion() # object to hold the start and end values of the region
inside_region = False # indicates, if we are currently inside a mask_region with value=1
for index, value in enumerate(region_mask):
if value == 1 and not inside_region:
# entered a region
inside_region = True
region.start = index
if value == 0 and inside_region:
# left a region
inside_region = False
region.end = index
region.width = region.end - region.start
region_list.append(region)
region = DataRegion()
return region_list
Definition of the DataRegion
class:
from dataclasses import dataclass
@dataclass
class DataRegion:
start: int = None
end: int = None
width: int = None