Project: s41467-024-46399-1 | Function: register (python)

system: You are an expert Python programmer implementing algorithms from scientific papers.
Complete the masked function based on the algorithm description.
Follow Python best practices and maintain consistent code style.
Return **only** the complete Python function code.

user: Complete this Python function:

**DESCRIPTION:** Registers time-series images using phase cross-correlation to correct for drift in bacterial culture microscopy

**KEY TERMS:** {
  "timeseries": "time-lapse microscopy images",
  "img_shift_thresh": "registration quality threshold",
  "phase_cross_correlation": "cross-correlation algorithm",
  "cum_shift": "cumulative image displacement",
  "registered": "drift-corrected images"
}

**CONTEXT CODE:** 
# Set your condition name and wells here -- each condition will get a plot
conditions = {'Vc_WT_unt' : ["A7_", "A8_", "A9_"],
              'Vc_nspC_unt' : ["B7_", "B8_", "B9_"],
              'Vc_mbaA_unt' : ["C7_", "C8_", "C9_"],
              'Vc_vpsL_unt' : ["D7_", "D8_", "D9_"],
              'Vc_WT_lys' : ["A1_", "A2_", "A3_", "A4_", "A5_", "A6_"],
              'Vc_nspC_lys' : ["B1_", "B2_", "B3_", "B4_", "B5_", "B6_"],
              'Vc_mbaA_lys' : ["C1_", "C2_", "C3_", "C4_", "C5_", "C6_"],
              'Vc_vspL_lys' : ["D1_", "D2_", "D3_", "D4_", "D5_", "D6_"]
              }

# Image parameters
acquisition_frequency = 2 # n times every hour -- change this
bf_sig_output = 2
sig_processing = 7
temporal_median_window = 3
blockRadius = 50
bf_thresh = 1000
bf_thresh2 = 123 # 113 for M9
img_shift_thresh = 100
dilat_ker = np.ones((5,5), np.uint8)
dilat_ker2 = np.ones((3,3), np.uint8)
dilat_ker3 = np.ones((9,9), np.uint8)
size_threshold = 700
duration_threshold = 6

def filter_components(binary_images, min_size, duration_threshold):
    # MASKED: Filters connected components in binary images based on size, duration, and temporal criteria for bacterial biofilm analysis implementation
    raise NotImplementedError("This function is masked for testing purposes.")

# Pythonic implementation of local contrast normalization
def normalize_local_contrast(img, blockRadius):

    block_size = blockRadius * 2 + 1
    block_average = uniform_filter(img, size=(0, block_size, block_size))
    img_normalized = img - block_average + 37488.0

    return img_normalized

# Function for cropping away boundary artifacts after registration
def crop(arr, val=0):
    mask = np.amin(arr, axis=0) != val
    mask_i = mask.any(axis=1)
    mask_j = mask.any(axis=0)
    slices_i = np.where(mask_i)[0][[0, -1]]
    slices_j = np.where(mask_j)[0][[0, -1]]
    return arr[:, slices_i[0]:slices_i[1]+1, slices_j[0]:slices_j[1]+1]

# Function to register images in a timeseries
def register(timeseries, img_shift_thresh):
    # MASKED: Registers time-series images using phase cross-correlation to correct for drift in bacterial culture microscopy implementation
    raise NotImplementedError("This function is masked for testing purposes.")

def temporal_median_filter(images, window_size=temporal_median_window):
    half_window = window_size // 2
    median_filtered_images = []

    for i in range(images.shape[0]):
        if i == 0:
            window = images[:window_size-1]
        elif i == images.shape[0] - 1:
            window = images[-1*(window_size-1):]
        else:
            start = max(0, i - half_window)
            end = min(images.shape[0], i + half_window + 1)
            window = images[start:end]

        median_image = np.median(window, axis=0)
        median_filtered_images.append(median_image)

    return np.asarray(median_filtered_images, dtype=np.uint8)

data = []
num_plots = 0
for j, (_, values) in enumerate(conditions.items()):
    print('Analyzing '+ _ +'...')

    replicates = len(conditions[_])
    extract_lists = replicates

    file_dict = {'Bright': []}
    for well in conditions[_]:

        matching_files = natsorted([f for f in os.listdir() if well in f])

        for file_type in file_dict.keys():
            file_dict[file_type] = [f for f in matching_files if file_type in f]

        BF_images = np.asarray([cv2.imread(f,-1) for f in file_dict['Bright']])
        BF_normalized = normalize_local_contrast(BF_images.astype(np.float32), blockRadius)
        BF_registered, shifts = register(BF_normalized.astype(np.uint16), img_shift_thresh)
        BF_output_images = []
        BF_masks = []
        BF_welldata = [well+'BF']
        img_nobacks = []
        imgs_inverted = []

        for i in range(BF_images.shape[0]):
            # First output images
            img_blurred = cv2.GaussianBlur((BF_registered[i]/256).astype(np.uint8), (0, 0), bf_sig_output)
            BF_output_images.append(img_blurred)

            # Generate noback image
            img = BF_images[i]
            img_inverted = 65535 - img
            imgs_inverted.append(img_inverted)

            img_flattened = img_inverted.flatten()
            img_reshaped = img_flattened.reshape(-1, 1)

            # Fit Bayesian Gaussian mixture model
            gmm = BayesianGaussianMixture(n_components=1, covariance_type='diag')
            gmm.fit(img_reshaped)

            min_mean_idx = np.argmin(gmm.means_)
            means = gmm.means_[min_mean_idx]
            img_noback = img_inverted.astype(np.float32) - means
            img_nobacks.append(img_noback)

        BF_output_images = np.asarray(BF_output_images)
        img_nobacks = np.asarray(img_nobacks)
        img_nobacks = np.asarray([shift(img, shift = cum_shift, mode='constant', cval=-1e6)
                                  for img, cum_shift in zip(img_nobacks, shifts)])
        img_nobacks = crop(img_nobacks, val=-1e6)
        imgs_inverted = np.asarray(imgs_inverted)
        imgs_inverted = np.asarray([shift(img, shift = cum_shift)
                                  for img, cum_shift in zip(imgs_inverted, shifts)])
        imgs_inverted = crop(imgs_inverted)
        if 'Vc' in _ or 'Va' in _:
            img_mask = ((255-BF_output_images) > bf_thresh2).astype(np.uint8)
            img_mask = temporal_median_filter(img_mask)
            img_masks = np.asarray([cv2.dilate(cv2.morphologyEx(cv2.medianBlur(img_mask_slice, 3),cv2.MORPH_CLOSE, dilat_ker), dilat_ker2)
                                    for img_mask_slice in img_mask])
            img_masks = filter_components(img_masks, size_threshold, duration_threshold)

        # Species-specific processing of background subtracted images to generate masks
        unnormalized_masks = []
        normalized_masks = []
        for i in range(img_nobacks.shape[0]):
            if 'Vc' in _ or 'Va' in _:
                # Vibrio cholerae and Vibrio anguillarum
                img_mask = img_masks[i]
                BF_masks.append(img_mask)
            elif 'Vp' in _:
                # Vibrio parahaemolyticus
                normalized_mask = ((255-BF_output_images[i]) > bf_thresh2).astype(np.uint8)
                normalized_mask = cv2.morphologyEx(normalized_mask, cv2.MORPH_OPEN, dilat_ker)
                normalized_mask = cv2.medianBlur(normalized_mask, 3)
                unnormalized_mask = (img_nobacks[i] > bf_thresh).astype(np.uint8)


Complete the function implementation replacing "raise NotImplementedError".