Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Скачиваний:
0
Добавлен:
13.05.2026
Размер:
10.53 Кб
Скачать
from os import listdir, system
from os.path import isfile
from time import perf_counter_ns
import typing as typ
from math import pi
import numpy as np
import numpy.typing as npt
import matplotlib.pyplot as plt #type: ignore
from skimage.io import imread #type: ignore

#Функция считывания названий файлов
def f_name(s: str) -> typ.List[str]:
    lt = listdir(s)
    return lt

#Функция создания списков изображений
def img_read(ls: typ.List[str],path: str) -> typ.List[npt.NDArray[np.float32]]:
	array: typ.List[npt.NDArray[np.float32]] = []
	for i in ls:
		if i.startswith("Forest"):
			array.append(\
				np.array(imread(f'{path}/{i}'),dtype=np.float32))
	return array

#Функция «спрямления» изображений
def img_flatten(ls:typ.List[npt.NDArray[np.float32]]) -> None:
	for i in ls:
		i.shape = (len(i)*len(i[0])*3) #type: ignore
		i = i.T

#Функция нормирования параметров
def norm(arr: npt.NDArray[np.float32],\
    mn: npt.NDArray[np.float32],\
    st: npt.NDArray[np.float32]) -> None:
    for i in range(len(arr)):
        arr[i] = (arr[i] - mn[i])/st[i]

#Функция создания аргументов для построения двумерного графика
def plot2d_params(f: typ.Callable[...,typ.Any],\
	x_lim: typ.Tuple[typ.Any, typ.Any],\
	y_lim: typ.Tuple[typ.Any, typ.Any],\
	m: typ.List[np.float32],\
	s: typ.List[np.float32],
	grid_n: int = 100)\
	-> typ.Tuple[typ.Any, typ.Any, typ.Any]:
    x1_p: npt.NDArray[np.float32] = np.linspace(x_lim[0], x_lim[1],\
   	grid_n, dtype=np.float32)
    x2_p: npt.NDArray[np.float32] = np.linspace(y_lim[0], y_lim[1],\
    grid_n, dtype=np.float32)
    x1_p, x2_p = np.meshgrid(x1_p, x2_p)
    s: typ.Tuple[int, ...] = x1_p.shape
    r: npt.NDArray[np.float32] = f(x1_p.flatten(), x2_p.flatten(), m, s)
    r.shape = s
    return (x1_p, x2_p, r)

#Функция нормального распределения
def gauss_2d(x: npt.NDArray[np.float32],\
	y: npt.NDArray[np.float32],\
	m: typ.List[np.float32] = [np.float32(0.0),np.float32(0.0)],\
	st: typ.List[np.float32] = [np.float32(1.0),np.float32(1.0)])\
	-> npt.NDArray[np.float32]:
	##Определение параметров двумерного нормального распределения
	z1: npt.NDArray[np.float32] = (-1)*(x-m[0])**2/(2*st[0]**2)
	z2: npt.NDArray[np.float32] = (-1)*(y-m[1])**2/(2*st[1]**2)
	return 1/(2*pi*st[0]*st[1])*np.exp(z1+z2)

#Функция определения аномалий
def anom_search(P: npt.NDArray[np.float32],\
	brd: float) ->  npt.NDArray[np.float32]:
	H: npt.NDArray[np.float32] =\
	np.zeros(len(P), dtype=np.float32)
	c: int = 0
	for i in P:
		if i < brd:
			H[c] = 1
		c += 1
	return H

#Функция расчёта методики F-score
def F_score(y_orig: npt.NDArray[np.float32],\
	y_anom: npt.NDArray[np.float32]) -> np.float32:
	#Истинноположительные
    TP: np.float32 = np.sum(np.logical_and(y_anom,y_orig))
    #Ложноположительные
    FP: np.float32 = np.sum(np.logical_not(np.logical_or(np.logical_not(y_anom),y_orig)))
    #Ложноотрицательные
    FN: np.float32 = np.sum(np.logical_and(np.logical_not(y_anom),y_orig))
    #Метрика Persision (точность)
    P: np.float32
    if FP+TP != 0:
    	P = TP/(FP+TP)
    else:
    	P = np.float32(0.0)
    #Метрика Recall (полнота)
    R: np.float32
    if FN+TP != 0:
    	R = TP/(FN+TP)
    else:
    	R =np.float32(0.0)
    #Метрика F1
    if P+R != 0:
    	return (2*P*R)/(P+R)
    else:
    	return np.float32(0.0)

#Очистка окна консоли
system('cls')

#Запуск таймера
t1: float = perf_counter_ns()

#Создание списка названий файлов в папке
trees_list: typ.List[str] = f_name("Trees")

#Создание списка изображений в папке
trees_img_list: typ.List[npt.NDArray[np.float32]] =\
	img_read(trees_list, "Trees")
del trees_list
##Изображение для дальнейшего включения в выборку аномалий
img: npt.NDArray[np.float32] = np.copy(trees_img_list[6])

#Создание массива изображений
##«Спрямление» массива изображений
img_flatten(trees_img_list)
##Создание массива из списка
X: npt.NDArray[np.float32] = np.array(trees_img_list, dtype=np.float32)
del trees_img_list

#Метод главных координат
##Максимальное количество параметров
max_par: int = 2
##Среднее нормирование
mean: npt.NDArray[np.float32] = np.mean(X.T, axis=1, dtype=np.float32)
std: npt.NDArray[np.float32] = np.std(X.T, axis=1, dtype=np.float32)
norm(X.T,mean,std)
##Создание матрицы собственных векторов для ковариационной матрицы
U: npt.NDArray[np.float32]
p: bool = isfile('U_matrix.npy')
if p == True:
    ##Создание матрицы преобразований
    U = np.load('U_matrix.npy')
elif p == False:
    ##Создание ковариационной матрицы
    sigma: npt.NDArray[np.float32] = np.cov(X.T,bias=True)
    ##Создание матрицы преобразований
    U, S, Vh = np.linalg.svd(sigma)
    np.save('U_matrix.npy', U[::, :max_par:])
    del sigma, S, Vh
X_cmprs: npt.NDArray[np.float32] = X@U #Компрессия изображений
del X, p, mean, std, max_par

#Двумерное нормальное распределение
##Определение параметров двумерного нормального распределения
m_x: np.float32 = np.mean(X_cmprs.T[0], dtype=np.float32) #среднее для икса
st_x: np.float32 = np.std(X_cmprs.T[0], dtype=np.float32) #отклонение для икса
m_y: np.float32 = np.mean(X_cmprs.T[1], dtype=np.float32) #среднее для игрека
st_y: np.float32 = np.std(X_cmprs.T[1], dtype=np.float32) #отклонение для игрека

#Построение изображений в двух главных координатах
##График исходных данных
plt.figure(1)
plt.scatter(X_cmprs.T[0],X_cmprs.T[1],alpha=0.3, label="Изображения") #Построение графика
plt.title("Изображения в двух главных координатах до выделения аномалий")
plt.legend()
##График после определения аномалий
plt.figure(2)
plt.scatter(X_cmprs.T[0],X_cmprs.T[1],alpha=0.3, label="Изображения") #Построение графика
plt.title("Изображения в двух главных координатах после выделения аномалий")
plt.legend()

#Построение контурного графика нормального распределения
x, y, z = plot2d_params(gauss_2d,\
	(np.min(X_cmprs.T[0]), np.max(X_cmprs.T[0])),\
	(np.min(X_cmprs.T[1]), np.max(X_cmprs.T[1])),\
	[m_x,m_y],\
	[st_x,st_y]) #Создание аргументов
##График исходных данных
plt.figure(1)
plt.contour(x, y, z, levels=5, linestyles="dashed") #Построение графика
##График после определения аномалий
plt.figure(2)
plt.contour(x, y, z, levels=5, linestyles="dashed") #Построение графика
del x, y, z

#Формирование аномалий
img_a1: npt.NDArray[np.float32] = img/5 #Слишком тёмное 
img_a2: npt.NDArray[np.float32] = img*5*(img<52) + 255*(img>51) #Пересвеченное
img_a3: npt.NDArray[np.float32] = np.copy(img) #С чёрными и белыми точками
img_a3[::2,::2,:] = 0
img_a3[1::2, 1::2, :] = 255
img_a4: npt.NDArray[np.float32] = np.copy(img) #C перепутанными 0 и 2 каналами
img_a4[:,:,2], img_a4[:,:,0] = img_a4[:,:,0], img_a4[:,:,2]
anomalies: typ.List[npt.NDArray[np.float32]] =\
[img_a1,img_a2,img_a3,img_a4] #Список аномалий

img_flatten(anomalies) #«Спрямление» аномалий
anomalies_arr: npt.NDArray[np.float32] =\
np.array(anomalies, dtype=np.float32) #Массив аномалий
##Нормирование массива аномалий
mean_a: npt.NDArray[np.float32] =\
np.mean(anomalies_arr.T, axis=1, dtype=np.float32)
std_a: npt.NDArray[np.float32] =\
np.std(anomalies_arr.T, axis=1, dtype=np.float32)
norm(anomalies_arr.T,mean_a,std_a)
anomalies_arr = anomalies_arr@U #Компрессия аномалий
X_cmprs = np.vstack((X_cmprs, anomalies_arr),dtype=np.float32) #Добавление аномалий
Y_cmprs: npt.NDArray[np.float32] #Исходный массив наличия аномалий
Y_cmprs = np.hstack((np.zeros(len(X_cmprs)-4),np.ones(4)),dtype=np.float32)
plt.figure(1)
plt.scatter(anomalies_arr.T[0],anomalies_arr.T[1],alpha=1,c='r',label="Аномалии") #Построение графика
plt.legend()
del img_a1, img_a2, img_a3, img_a4, anomalies, anomalies_arr, mean_a, std_a, U

#Определение оптимального порога
P: npt.NDArray[np.float32] =\
	gauss_2d(X_cmprs.T[0],X_cmprs.T[1],[m_x,m_y],[st_x,st_y])
metric: typ.List[typ.List[typ.Any]] =\
	[[i/(10**6) for i in range(11)],[]]
for i in metric[0]:
	anom: npt.NDArray[np.float32] = anom_search(P,i)
	metric[1].append(F_score(Y_cmprs,anom))
max_Fscore = max(metric[1]) #Максимальное значение метрики
optimal_brd = metric[0][metric[1].index(max_Fscore)] #Оптимальный порог 
print(f'Optimal decision border: {optimal_brd:.1e}.')
Y_cmprs = anom_search(P,optimal_brd)

##График после определения аномалий
plt.figure(2)
plt.scatter(X_cmprs.T[0,Y_cmprs==1],X_cmprs.T[1,Y_cmprs==1],alpha=1,c='r',label="Аномалии") #Построение графика
plt.legend()
del P, metric, max_Fscore, optimal_brd, X_cmprs, Y_cmprs, m_x, m_y, st_x, st_y

#Вывод времени выполнения программы
print(f'\nTotal program execution time: \
{(perf_counter_ns()-t1)/10**9:.5f} seconds.')
del t1

plt.show()
Соседние файлы в папке лабы