ver 2
This commit is contained in:
@@ -60,4 +60,161 @@ def save_wafer_map(wafer_map, bin_to_char_mapping, file_path):
|
||||
"""
|
||||
with open(file_path, 'w', encoding='utf-8') as file:
|
||||
for row in wafer_map:
|
||||
file.write(''.join([bin_to_char_mapping.get(int(bin_code), ' ') for bin_code in row]) + '\n')
|
||||
file.write(''.join([bin_to_char_mapping.get(int(bin_code), ' ') for bin_code in row]) + '\n')
|
||||
|
||||
def get_bin_counts(wafer_map):
|
||||
"""
|
||||
Calculates the count of each bin type in the wafer map.
|
||||
"""
|
||||
unique, counts = np.unique(wafer_map, return_counts=True)
|
||||
return dict(zip(unique, counts))
|
||||
|
||||
def inset_wafer_map(wafer_map, layers, from_bin_value, to_bin_value):
|
||||
"""
|
||||
Finds the outer N layers of the main die area, defined by proximity to
|
||||
'ignore' bins, and changes them from 'from_bin_value' to 'to_bin_value'.
|
||||
"""
|
||||
if layers <= 0:
|
||||
return wafer_map
|
||||
|
||||
rows, cols = wafer_map.shape
|
||||
|
||||
# Mask for all dies that are candidates for changing.
|
||||
target_mask = (wafer_map == from_bin_value)
|
||||
|
||||
# Mask for dies that are NOT part of the wafer (e.g., ignore bins)
|
||||
background_mask = (wafer_map == -1)
|
||||
|
||||
# This will hold all dies that have been identified for changing.
|
||||
final_change_mask = np.zeros_like(wafer_map, dtype=bool)
|
||||
|
||||
# This mask represents the "peeling front". Initially, it's the background.
|
||||
peel_front_mask = np.copy(background_mask)
|
||||
|
||||
for _ in range(layers):
|
||||
# Find dies in our target_mask that are adjacent to the current peel_front_mask
|
||||
newly_found_edge_mask = np.zeros_like(wafer_map, dtype=bool)
|
||||
|
||||
# Iterate only over the candidates to be more efficient
|
||||
for r, c in np.argwhere(target_mask):
|
||||
# Check neighbors
|
||||
for dr, dc in [(0, 1), (0, -1), (1, 0), (-1, 0)]:
|
||||
nr, nc = r + dr, c + dc
|
||||
|
||||
# If a neighbor is part of the current "peel front"
|
||||
if (0 <= nr < rows and 0 <= nc < cols and peel_front_mask[nr, nc]):
|
||||
newly_found_edge_mask[r, c] = True
|
||||
break # Found it's an edge, no need to check other neighbors
|
||||
|
||||
# If we didn't find any new edge dies, we're done.
|
||||
if not np.any(newly_found_edge_mask):
|
||||
break
|
||||
|
||||
# Add the newly found layer to our final mask of dies to change.
|
||||
final_change_mask |= newly_found_edge_mask
|
||||
|
||||
# The layer we just found becomes the new "peel front" for the next iteration.
|
||||
peel_front_mask |= newly_found_edge_mask
|
||||
|
||||
# Remove the dies we just processed from the pool of candidates.
|
||||
target_mask &= ~newly_found_edge_mask
|
||||
|
||||
# Apply the change to a copy of the original map
|
||||
wafer_map_copy = np.copy(wafer_map)
|
||||
wafer_map_copy[final_change_mask] = to_bin_value
|
||||
|
||||
return wafer_map_copy
|
||||
|
||||
def compare_wafer_maps(maps, references):
|
||||
"""
|
||||
Compares multiple wafer maps by aligning them based on reference points.
|
||||
"""
|
||||
if not maps or len(maps) != len(references):
|
||||
return np.array([]), {}
|
||||
|
||||
# Determine the bounds of the combined map
|
||||
max_up = 0
|
||||
max_down = 0
|
||||
max_left = 0
|
||||
max_right = 0
|
||||
|
||||
for i, (map_arr, ref) in enumerate(zip(maps, references)):
|
||||
ref_r, ref_c = ref
|
||||
rows, cols = map_arr.shape
|
||||
|
||||
max_up = max(max_up, ref_r)
|
||||
max_down = max(max_down, rows - ref_r - 1)
|
||||
max_left = max(max_left, ref_c)
|
||||
max_right = max(max_right, cols - ref_c - 1)
|
||||
|
||||
# Total dimensions of the comparison grid
|
||||
total_rows = max_up + max_down + 1
|
||||
total_cols = max_left + max_right + 1
|
||||
|
||||
# A grid where each cell holds a list of bin codes from all maps at that position
|
||||
# Initialize with a special value for "no map data"
|
||||
NO_DATA = -10
|
||||
comparison_grid = [[[NO_DATA] * len(maps) for _ in range(total_cols)] for _ in range(total_rows)]
|
||||
|
||||
# Place each map onto the comparison grid
|
||||
for i, (map_arr, ref) in enumerate(zip(maps, references)):
|
||||
ref_r, ref_c = ref
|
||||
rows, cols = map_arr.shape
|
||||
|
||||
offset_r = max_up - ref_r
|
||||
offset_c = max_left - ref_c
|
||||
|
||||
for r in range(rows):
|
||||
for c in range(cols):
|
||||
# Only consider non-ignore bins
|
||||
if map_arr[r, c] != -1:
|
||||
comparison_grid[offset_r + r][offset_c + c][i] = map_arr[r, c]
|
||||
|
||||
# --- Process the grid to create a final result map ---
|
||||
# Result codes:
|
||||
# -1: No data from any map (Ignore)
|
||||
# 0: Match (Good)
|
||||
# 1: Match (NG)
|
||||
# 2: Match (Dummy)
|
||||
# 3: Mismatch (mix of Good, NG, Dummy)
|
||||
# 4: Partial data (some maps have data, others don't)
|
||||
|
||||
result_map = np.full((total_rows, total_cols), -1, dtype=int)
|
||||
|
||||
for r in range(total_rows):
|
||||
for c in range(total_cols):
|
||||
bins = [b for b in comparison_grid[r][c] if b != NO_DATA]
|
||||
|
||||
if not bins: # No map has data here
|
||||
result_map[r, c] = -1
|
||||
continue
|
||||
|
||||
if len(bins) < len(maps): # Some maps have data, others don't
|
||||
result_map[r, c] = 4
|
||||
continue
|
||||
|
||||
# All maps have data, check for match or mismatch
|
||||
first_bin = bins[0]
|
||||
is_match = all(b == first_bin for b in bins)
|
||||
|
||||
if is_match:
|
||||
if first_bin == 2: # Good
|
||||
result_map[r, c] = 0
|
||||
elif first_bin == 1: # NG
|
||||
result_map[r, c] = 1
|
||||
elif first_bin == 0: # Dummy
|
||||
result_map[r, c] = 2
|
||||
else: # Mismatch
|
||||
result_map[r, c] = 3
|
||||
|
||||
legend = {
|
||||
-1: {"label": "No Data", "color": "#3e3e42"},
|
||||
0: {"label": "Match (Good)", "color": "#28a745"},
|
||||
1: {"label": "Match (NG)", "color": "#4a90e2"}, # Different color for match NG
|
||||
2: {"label": "Match (Dummy)", "color": "#7f8c8d"},
|
||||
3: {"label": "Mismatch", "color": "#dc3545"},
|
||||
4: {"label": "Partial Data", "color": "#f39c12"}
|
||||
}
|
||||
|
||||
# Also return the detailed grid for tooltip purposes
|
||||
return result_map, legend, comparison_grid
|
||||
|
Reference in New Issue
Block a user