from google.
colab import drive
drive.mount('/content/drive')
Drive already mounted at /content/drive; to attempt to forcibly
remount, call drive.mount("/content/drive", force_remount=True).
gz_file_path ='/content/drive/MyDrive/UCSD_Anomaly_Dataset.tar.gz'
import tarfile
# Extract the .tar.gz file
with tarfile.open(gz_file_path, 'r:gz') as tar:
tar.extractall('/content/UCSD_Anomaly_Dataset')
!pip install tensorflow
!pip install opencv-python
Requirement already satisfied: tensorflow in
/usr/local/lib/python3.10/dist-packages (2.17.0)
Requirement already satisfied: absl-py>=1.0.0 in
/usr/local/lib/python3.10/dist-packages (from tensorflow) (1.4.0)
Requirement already satisfied: astunparse>=1.6.0 in
/usr/local/lib/python3.10/dist-packages (from tensorflow) (1.6.3)
Requirement already satisfied: flatbuffers>=24.3.25 in
/usr/local/lib/python3.10/dist-packages (from tensorflow) (24.3.25)
Requirement already satisfied: gast!=0.5.0,!=0.5.1,!=0.5.2,>=0.2.1
in /usr/local/lib/python3.10/dist-packages (from tensorflow) (0.6.0)
Requirement already satisfied: google-pasta>=0.1.1 in
/usr/local/lib/python3.10/dist-packages (from tensorflow) (0.2.0)
Requirement already satisfied: h5py>=3.10.0 in
/usr/local/lib/python3.10/dist-packages (from tensorflow) (3.12.1)
Requirement already satisfied: libclang>=13.0.0 in
/usr/local/lib/python3.10/dist-packages (from tensorflow) (18.1.1)
Requirement already satisfied: ml-dtypes<0.5.0,>=0.3.1 in
/usr/local/lib/python3.10/dist-packages (from tensorflow) (0.4.1)
Requirement already satisfied: opt-einsum>=2.3.2 in
/usr/local/lib/python3.10/dist-packages (from tensorflow) (3.4.0)
Requirement already satisfied: packaging in
/usr/local/lib/python3.10/dist-packages (from tensorflow) (24.1)
Requirement already satisfied: protobuf!=4.21.0,!=4.21.1,!=4.21.2,!
=4.21.3,!=4.21.4,!=4.21.5,<5.0.0dev,>=3.20.3 in
/usr/local/lib/python3.10/dist-packages (from tensorflow) (3.20.3)
Requirement already satisfied: requests<3,>=2.21.0 in
/usr/local/lib/python3.10/dist-packages (from tensorflow) (2.32.3)
Requirement already satisfied: setuptools in
/usr/local/lib/python3.10/dist-packages (from tensorflow) (75.1.0)
Requirement already satisfied: six>=1.12.0 in
/usr/local/lib/python3.10/dist-packages (from tensorflow) (1.16.0)
Requirement already satisfied: termcolor>=1.1.0 in
/usr/local/lib/python3.10/dist-packages (from tensorflow) (2.5.0)
Requirement already satisfied: typing-extensions>=3.6.6 in
/usr/local/lib/python3.10/dist-packages (from tensorflow) (4.12.2)
Requirement already satisfied: wrapt>=1.11.0 in
/usr/local/lib/python3.10/dist-packages (from tensorflow) (1.16.0)
Requirement already satisfied: grpcio<2.0,>=1.24.3 in
/usr/local/lib/python3.10/dist-packages (from tensorflow) (1.64.1)
Requirement already satisfied: tensorboard<2.18,>=2.17 in
/usr/local/lib/python3.10/dist-packages (from tensorflow) (2.17.0)
Requirement already satisfied: keras>=3.2.0 in
/usr/local/lib/python3.10/dist-packages (from tensorflow) (3.4.1)
Requirement already satisfied: tensorflow-io-gcs-filesystem>=0.23.1 in
/usr/local/lib/python3.10/dist-packages (from tensorflow) (0.37.1)
Requirement already satisfied: numpy<2.0.0,>=1.23.5 in
/usr/local/lib/python3.10/dist-packages (from tensorflow) (1.26.4)
Requirement already satisfied: wheel<1.0,>=0.23.0 in
/usr/local/lib/python3.10/dist-packages (from astunparse>=1.6.0-
>tensorflow) (0.44.0)
Requirement already satisfied: rich in /usr/local/lib/python3.10/dist-
packages (from keras>=3.2.0->tensorflow) (13.9.3)
Requirement already satisfied: namex in
/usr/local/lib/python3.10/dist-packages (from keras>=3.2.0-
>tensorflow) (0.0.8)
Requirement already satisfied: optree in
/usr/local/lib/python3.10/dist-packages (from keras>=3.2.0-
>tensorflow) (0.13.0)
Requirement already satisfied: charset-normalizer<4,>=2 in
/usr/local/lib/python3.10/dist-packages (from requests<3,>=2.21.0-
>tensorflow) (3.4.0)
Requirement already satisfied: idna<4,>=2.5 in
/usr/local/lib/python3.10/dist-packages (from requests<3,>=2.21.0-
>tensorflow) (3.10)
Requirement already satisfied: urllib3<3,>=1.21.1 in
/usr/local/lib/python3.10/dist-packages (from requests<3,>=2.21.0-
>tensorflow) (2.2.3)
Requirement already satisfied: certifi>=2017.4.17 in
/usr/local/lib/python3.10/dist-packages (from requests<3,>=2.21.0-
>tensorflow) (2024.8.30)
Requirement already satisfied: markdown>=2.6.8 in
/usr/local/lib/python3.10/dist-packages (from tensorboard<2.18,>=2.17-
>tensorflow) (3.7)
Requirement already satisfied: tensorboard-data-server<0.8.0,>=0.7.0
in /usr/local/lib/python3.10/dist-packages (from
tensorboard<2.18,>=2.17->tensorflow) (0.7.2)
Requirement already satisfied: werkzeug>=1.0.1 in
/usr/local/lib/python3.10/dist-packages (from tensorboard<2.18,>=2.17-
>tensorflow) (3.0.6)
Requirement already satisfied: MarkupSafe>=2.1.1 in
/usr/local/lib/python3.10/dist-packages (from werkzeug>=1.0.1-
>tensorboard<2.18,>=2.17->tensorflow) (3.0.2)
Requirement already satisfied: markdown-it-py>=2.2.0 in
/usr/local/lib/python3.10/dist-packages (from rich->keras>=3.2.0-
>tensorflow) (3.0.0)
Requirement already satisfied: pygments<3.0.0,>=2.13.0 in
/usr/local/lib/python3.10/dist-packages (from rich->keras>=3.2.0-
>tensorflow) (2.18.0)
Requirement already satisfied: mdurl~=0.1 in
/usr/local/lib/python3.10/dist-packages (from markdown-it-py>=2.2.0-
>rich->keras>=3.2.0->tensorflow) (0.1.2)
Requirement already satisfied: opencv-python in
/usr/local/lib/python3.10/dist-packages (4.10.0.84)
Requirement already satisfied: numpy>=1.21.2 in
/usr/local/lib/python3.10/dist-packages (from opencv-python) (1.26.4)
#!pip install matplotlib==3.4.3
import cv2
import numpy as np
import os
from tensorflow.keras.models import Model
from tensorflow.keras.layers import Conv2D, MaxPooling2D,
UpSampling2D, Input
from tensorflow.keras.optimizers import Adam
import matplotlib.pyplot as plt
from sklearn.metrics import mean_squared_error
from PIL import Image
def load_and_preprocess_frames(directory_path, frame_height=160,
frame_width=160):
all_videos = []
for video_folder in sorted(os.listdir(directory_path)):
video_path = os.path.join(directory_path, video_folder)
if os.path.isdir(video_path):
frames = []
for filename in sorted(os.listdir(video_path)):
frame_path = os.path.join(video_path, filename)
if filename.lower().endswith('.tif'):
# Open the image, resize, and convert to grayscale
if needed
with Image.open(frame_path) as img:
img = img.resize((frame_width, frame_height))
# Resize to (128, 128)
frame = np.array(img) # Convert to numpy
array
frame = np.expand_dims(frame, axis=-1) # Add
channel dimension for grayscale
frame = frame / 255.0 # Normalize to [0, 1]
frames.append(frame)
if frames:
all_videos.append(np.array(frames))
return all_videos
# Example usage
train_directory_ped1 =
'/content/UCSD_Anomaly_Dataset/UCSD_Anomaly_Dataset.v1p2/UCSDped1/
Train'
train_directory_ped2 =
'/content/UCSD_Anomaly_Dataset/UCSD_Anomaly_Dataset.v1p2/UCSDped2/
Train'
train_videos_ped1 = load_and_preprocess_frames(train_directory_ped1)
train_videos_ped2 = load_and_preprocess_frames(train_directory_ped2)
train_videos = train_videos_ped1 + train_videos_ped2
# Display a sample of frames to verify the loading process
def display_sample_frames(videos, num_frames=5):
for i, video_frames in enumerate(videos[:1]): # Display frames
from the first video only
plt.figure(figsize=(15, 5))
for j in range(min(num_frames, len(video_frames))):
plt.subplot(1, num_frames, j + 1)
plt.imshow(video_frames[j])
plt.axis('off')
plt.title(f"Video {i + 1} - Frame {j + 1}")
plt.show()
display_sample_frames(train_videos)
import numpy as np
def split_into_sequences(video_frames, sequence_length=16):
"""
Splits the frames of a video into sequences of a specified length.
Parameters:
- video_frames: np.array, the frames of a single video (e.g.,
shape (num_frames, height, width, channels))
- sequence_length: int, the number of frames per sequence
Returns:
- sequences: np.array, shape (num_sequences, sequence_length,
height, width, channels)
"""
num_frames = len(video_frames)
sequences = []
# Slide over frames to create sequences
for i in range(0, num_frames - sequence_length + 1,
sequence_length):
sequence = video_frames[i:i + sequence_length]
sequences.append(sequence)
return np.array(sequences)
# Define directories for both training datasets
train_directory_ped1 =
'/content/UCSD_Anomaly_Dataset/UCSD_Anomaly_Dataset.v1p2/UCSDped1/
Train'
train_directory_ped2 =
'/content/UCSD_Anomaly_Dataset/UCSD_Anomaly_Dataset.v1p2/UCSDped2/
Train'
# Load both datasets
train_videos_ped1 = load_and_preprocess_frames(train_directory_ped1)
train_videos_ped2 = load_and_preprocess_frames(train_directory_ped2)
# Combine videos from both datasets
train_videos = train_videos_ped1 + train_videos_ped2
# Concatenate all sequences from all videos in both datasets
train_3d_data = np.concatenate([split_into_sequences(video,
sequence_length=16) for video in train_videos], axis=0)
print("Prepared training data shape:", train_3d_data.shape)
Prepared training data shape: (562, 16, 160, 160, 1)
from tensorflow.keras.models import Model
from tensorflow.keras.layers import Conv3D, MaxPooling3D,
UpSampling3D, Input, Activation
from tensorflow.keras.optimizers import Adam
# 3D CNN Autoencoder Model
def build_3d_cnn_autoencoder(input_shape=(16, 160, 160, 1)):
input_layer = Input(shape=input_shape)
# Encoder
x = Conv3D(32, (3, 3, 3), padding='same')(input_layer)
x = Activation('relu')(x)
x = MaxPooling3D((2, 2, 2), padding='same')(x)
x = Conv3D(32, (3, 3, 3), padding='same')(x)
x = Activation('relu')(x)
x = MaxPooling3D((2, 2, 2), padding='same')(x)
# Decoder
x = Conv3D(32, (3, 3, 3), padding='same')(x)
x = Activation('relu')(x)
x = UpSampling3D((2, 2, 2))(x)
x = Conv3D(32, (3, 3, 3), padding='same')(x)
x = Activation('relu')(x)
x = UpSampling3D((2, 2, 2))(x)
decoded = Conv3D(1, (3, 3, 3), activation='sigmoid',
padding='same')(x) # Final layer with sigmoid for [0, 1] range
# Autoencoder Model
autoencoder = Model(input_layer, decoded)
autoencoder.compile(optimizer=Adam(learning_rate=0.001),
loss='mse')
return autoencoder
# Example usage
input_shape = (16, 160, 160, 1) # 16 consecutive frames, 160x160
resolution, 1 channel (grayscale)
cnn_3d_autoencoder = build_3d_cnn_autoencoder(input_shape=input_shape)
# Encoder
# x = Conv3D(32, (3, 3, 3), activation='relu', padding='same')
(input_layer)
# x = MaxPooling3D((2, 2, 2), padding='same')(x)
# x = Conv3D(64, (3, 3, 3), activation='relu', padding='same')(x)
# x = MaxPooling3D((2, 2, 2), padding='same')(x)
# encoded = Conv3D(277, (3, 3, 3), activation='relu',
padding='same')(x)
# # Decoder
# x = UpSampling3D((2, 2, 2))(encoded)
# x = Conv3D(64, (3, 3, 3), activation='relu', padding='same')(x)
# x = UpSampling3D((2, 2, 2))(x)
# decoded = Conv3D(1, (3, 3, 3), activation='sigmoid',
padding='same')(x) # Single channel for grayscale output
# autoencoder = Model(input_layer, decoded)
# autoencoder.compile(optimizer=Adam(), loss='mse')
# return autoencoder
cnn_3d_autoencoder.compile(optimizer='adam', loss='mse')
# Assuming train_3d_data is your full dataset
train_size = int(0.8 * len(train_3d_data))
train_data = train_3d_data[:train_size]
val_data = train_3d_data[train_size:]
from tensorflow.keras.callbacks import EarlyStopping, ModelCheckpoint
# Set up early stopping and model checkpoint to monitor validation
loss
early_stopping = EarlyStopping(monitor='val_loss', patience=3,
verbose=1)
cnn_3d_autoencoder.fit(
train_data, train_data,
epochs=10,
batch_size=2,
shuffle=True,
validation_data=(val_data, val_data),
callbacks=[early_stopping]
)
Epoch 1/10
225/225 ━━━━━━━━━━━━━━━━━━━━ 27s 84ms/step - loss: 0.0158 - val_loss:
0.0013
Epoch 2/10
225/225 ━━━━━━━━━━━━━━━━━━━━ 29s 58ms/step - loss: 0.0023 - val_loss:
9.8407e-04
Epoch 3/10
225/225 ━━━━━━━━━━━━━━━━━━━━ 13s 58ms/step - loss: 0.0018 - val_loss:
8.5347e-04
Epoch 4/10
225/225 ━━━━━━━━━━━━━━━━━━━━ 21s 59ms/step - loss: 0.0017 - val_loss:
7.8221e-04
Epoch 5/10
225/225 ━━━━━━━━━━━━━━━━━━━━ 14s 61ms/step - loss: 0.0015 - val_loss:
7.4683e-04
Epoch 6/10
225/225 ━━━━━━━━━━━━━━━━━━━━ 20s 60ms/step - loss: 0.0014 - val_loss:
7.0392e-04
Epoch 7/10
225/225 ━━━━━━━━━━━━━━━━━━━━ 21s 61ms/step - loss: 0.0014 - val_loss:
7.2750e-04
Epoch 8/10
225/225 ━━━━━━━━━━━━━━━━━━━━ 20s 61ms/step - loss: 0.0013 - val_loss:
6.5005e-04
Epoch 9/10
225/225 ━━━━━━━━━━━━━━━━━━━━ 21s 61ms/step - loss: 0.0012 - val_loss:
6.2748e-04
Epoch 10/10
225/225 ━━━━━━━━━━━━━━━━━━━━ 14s 62ms/step - loss: 0.0012 - val_loss:
6.3976e-04
<keras.src.callbacks.history.History at 0x79e7ed803910>
import matplotlib.pyplot as plt
from PIL import Image
import numpy as np
import os
def load_and_preprocess_frames(directory_path, frame_height=160,
frame_width=160):
all_videos = []
for video_folder in sorted(os.listdir(directory_path)):
video_path = os.path.join(directory_path, video_folder)
if os.path.isdir(video_path):
frames = []
for filename in sorted(os.listdir(video_path)):
frame_path = os.path.join(video_path, filename)
if filename.lower().endswith('.tif'):
try:
# Attempt to open, resize, and normalize the
image
with Image.open(frame_path) as img:
img = img.resize((frame_width,
frame_height)) # Resize to (128, 128)
frame = np.array(img) # Convert to numpy
array
frame = np.expand_dims(frame, axis=-1) #
Add channel dimension for grayscale
frame = frame / 255.0 # Normalize to [0,
1]
frames.append(frame)
except Exception as e:
# Print an error message for any file that
fails to load
print(f"Error loading file {frame_path}: {e}")
if frames:
all_videos.append(np.array(frames))
return all_videos
# Example usage for loading the test set
test_directory_ped1 =
'/content/UCSD_Anomaly_Dataset/UCSD_Anomaly_Dataset.v1p2/UCSDped1/
Test'
test_directory_ped2 =
'/content/UCSD_Anomaly_Dataset/UCSD_Anomaly_Dataset.v1p2/UCSDped2/
Test'
test_videos_ped1 = load_and_preprocess_frames(test_directory_ped1)
test_videos_ped2 = load_and_preprocess_frames(test_directory_ped2)
test_videos = test_videos_ped1 + test_videos_ped2
Error loading file
/content/UCSD_Anomaly_Dataset/UCSD_Anomaly_Dataset.v1p2/UCSDped1/Test/
Test017/142.tif: -2
test_3d_data = np.concatenate([split_into_sequences(video,
sequence_length=16) for video in test_videos], axis=0)
print("Prepared test data shape:", test_3d_data.shape)
Prepared test data shape: (554, 16, 160, 160, 1)
reconstructed_test_data = cnn_3d_autoencoder.predict(test_3d_data)
18/18 ━━━━━━━━━━━━━━━━━━━━ 11s 326ms/step
from sklearn.metrics import mean_squared_error
# Calculate reconstruction error for each sequence
reconstruction_errors = [
mean_squared_error(original.flatten(), reconstructed.flatten())
for original, reconstructed in zip(test_3d_data,
reconstructed_test_data)
]
import numpy as np
threshold = np.percentile(reconstruction_errors, 30
)
print("Anomaly threshold:", threshold)
Anomaly threshold: 0.0011392208955404764
anomalies = [error > threshold for error in reconstruction_errors]
import matplotlib.pyplot as plt
plt.plot(reconstruction_errors, label='Reconstruction Error')
plt.axhline(y=threshold, color='r', linestyle='--', label='Anomaly
Threshold')
plt.xlabel("Sequence")
plt.ylabel("Reconstruction Error")
plt.title("Reconstruction Error on Test Data")
plt.legend()
plt.show()
# Define ground truth for UCSDped1
ground_truth_frames_ped1 = [
list(range(60, 153)),
list(range(50, 176)),
list(range(91, 201)),
list(range(31, 169)),
list(range(5, 91)) + list(range(140, 201)),
list(range(1, 101)) + list(range(110, 201)),
list(range(1, 176)),
list(range(1, 95)),
list(range(1, 49)),
list(range(1, 141)),
list(range(70, 166)),
list(range(130, 201)),
list(range(1, 157)),
list(range(1, 201)),
list(range(138, 201)),
list(range(123, 201)),
list(range(1, 48)),
list(range(54, 121)),
list(range(64, 139)),
list(range(45, 176)),
list(range(31, 201)),
list(range(16, 108)),
list(range(8, 166)),
list(range(50, 172)),
list(range(40, 136)),
list(range(77, 145)),
list(range(10, 123)),
list(range(105, 201)),
list(range(1, 16)) + list(range(45, 114)),
list(range(175, 201)),
list(range(1, 181)),
list(range(1, 53)) + list(range(65, 116)),
list(range(5, 166)),
list(range(1, 122)),
list(range(86, 201)),
list(range(15, 109))
]
# Define ground truth for UCSDped2
ground_truth_frames_ped2 = [
list(range(61, 180)),
list(range(95, 180)),
list(range(1, 146)),
list(range(31, 180)),
list(range(1, 129)),
list(range(1, 162)),
list(range(46, 180)),
list(range(1, 180)),
list(range(1, 120)),
list(range(1, 150)),
list(range(1, 180)),
list(range(88, 180))
]
# Combine both ground truth annotations
ground_truth_frames = ground_truth_frames_ped1 +
ground_truth_frames_ped2
# Convert reconstruction errors into binary predictions based on the
threshold
binary_predictions = [1 if error > threshold else 0 for error in
reconstruction_errors]
# Group predictions by video
sequence_length = 16
num_sequences_per_video = len(test_videos[0]) // sequence_length #
Assuming each video has the same number of frames
# Organize binary predictions by video
model_predictions = [
binary_predictions[i * num_sequences_per_video : (i + 1) *
num_sequences_per_video]
for i in range(len(test_videos))
]
from sklearn.metrics import precision_score, recall_score, f1_score
# Initialize lists to collect all ground truth labels and model
predictions
all_gt_labels = []
all_model_labels = []
# Iterate over each test video
for i, gt_frames in enumerate(ground_truth_frames):
# Get binary predictions for the current video, expanded to match
frame-level granularity
video_predictions = []
for seq_pred in model_predictions[i]:
video_predictions.extend([seq_pred] * sequence_length) #
Repeat each sequence prediction across its frames
# Generate ground truth labels for each frame in the video
gt_labels = [1 if frame in gt_frames else 0 for frame in
range(len(video_predictions))]
# Collect the ground truth and predictions for overall evaluation
all_gt_labels.extend(gt_labels)
all_model_labels.extend(video_predictions[:len(gt_labels)]) #
Ensure predictions align with gt labels length
# Calculate and display evaluation metrics
precision = precision_score(all_gt_labels, all_model_labels)
recall = recall_score(all_gt_labels, all_model_labels)
f1 = f1_score(all_gt_labels, all_model_labels)
print(f"Precision: {precision:.4f}")
print(f"Recall: {recall:.4f}")
print(f"F1 Score: {f1:.4f}")
Precision: 0.6432
Recall: 0.7471
F1 Score: 0.6912
from sklearn.metrics import confusion_matrix, ConfusionMatrixDisplay
import matplotlib.pyplot as plt
# Assuming `all_gt_labels` are the ground truth labels and
`all_model_labels` are your final binary predictions
# Calculate the confusion matrix
cm = confusion_matrix(all_gt_labels, all_model_labels)
# Plot the confusion matrix
disp = ConfusionMatrixDisplay(confusion_matrix=cm,
display_labels=["Normal", "Anomaly"])
disp.plot(cmap="Blues")
plt.title("Confusion Matrix for Anomaly Detection")
plt.show()
Recall (0.7471): This moderately high recall indicates that the model successfully detects most
of the actual anomalies, although it may miss a few. A recall of 0.7471 means that the model is
generally effective in identifying anomalous sequences but may occasionally let some anomalies
go undetected.
Precision (0.6432): With a precision of 0.6432, the model is reasonably selective in identifying
anomalies. However, it does flag some normal sequences as anomalies, which suggests there
are still false positives. This precision level indicates a good balance where the model avoids
being overly sensitive, but it could still be improved if false alarms are a concern.
F1 Score (0.6912): The F1 score of 0.6912 reflects a solid balance between recall and precision.
This score indicates that the model is fairly good at both capturing actual anomalies and
avoiding false positives, making it a well-rounded choice for general anomaly detection.