From 9e1dad8637bf921317c202d7cf9f31a086bce3b8 Mon Sep 17 00:00:00 2001 From: karllzy Date: Thu, 23 Jun 2022 12:33:27 +0800 Subject: [PATCH] add dual_main.py and dual_main_test.py --- 07_pixelwised_detection.ipynb | 370 ++++++++++++++++++++++++++++++++++ dual_main.py | 110 ++++++++++ main.py | 33 ++- models.py | 83 ++++++-- test_files/dual_main_test.py | 61 ++++++ utils.py | 59 ++++-- 6 files changed, 677 insertions(+), 39 deletions(-) create mode 100644 07_pixelwised_detection.ipynb create mode 100755 dual_main.py create mode 100755 test_files/dual_main_test.py diff --git a/07_pixelwised_detection.ipynb b/07_pixelwised_detection.ipynb new file mode 100644 index 0000000..42e3fd0 --- /dev/null +++ b/07_pixelwised_detection.ipynb @@ -0,0 +1,370 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "collapsed": true, + "pycharm": { + "name": "#%% md\n" + } + }, + "source": [ + "# 基于像素的识别" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "outputs": [], + "source": [ + "import pickle\n", + "import cv2\n", + "import numpy as np\n", + "from utils import split_xy" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "code", + "execution_count": 1, + "outputs": [], + "source": [ + "# 一些参数\n", + "blk_sz = 1\n", + "sensitivity = 1\n", + "selected_bands = [127, 201, 202, 294]\n", + "tree_num = 1\n", + "train_size = 140000\n", + "file_name, labeled_image_file = r\"/Volumes/zc/zhouchao/616/calibrated1.raw\", \\\n", + "r\"/Volumes/zc/zhouchao/616/label1.bmp\"\n", + "\n", + "test_dir = \"/Volumes/zc/zhouchao/618(2)/kazhi/\"\n", + "\n", + "dataset_file = f'./dataset/data_{blk_sz}x{blk_sz}_c{len(selected_bands)}_sen{sensitivity}_1.p'\n", + "model_file = f'./models/rf_{blk_sz}x{blk_sz}_c{len(selected_bands)}_{tree_num}_sen{sensitivity}_4.model'" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "markdown", + "source": [ + "## 数据集的构建" + ], + "metadata": { + "collapsed": false + } + }, + { + "cell_type": "code", + "execution_count": 6, + "outputs": [], + "source": [ + "with open(file_name, \"rb\") as f:\n", + " data = np.frombuffer(f.read(), dtype=np.float32).reshape((600, 448, 1024)).transpose(0, 2, 1)\n", + "data = data[..., selected_bands]\n", + "label = cv2.imread(labeled_image_file)\n", + "color_dict = {(0, 0, 255): 1, (255, 255, 255): 0, (0, 255, 0): 2, (255, 0, 0): 1, (0, 255, 255): 4,\n", + " (255, 255, 0): 5, (255, 0, 255): 6}\n", + "x, y = split_xy(data, label, blk_sz, sensitivity=sensitivity, color_dict=color_dict)\n", + "with open(dataset_file, 'wb') as f:\n", + " pickle.dump((x, y), f)" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "markdown", + "source": [ + "## 数据平衡化" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%% md\n" + } + } + }, + { + "cell_type": "code", + "execution_count": 3, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "数据量: 614400\n", + "x train (430080, 1, 1, 4), y train (430080,)\n", + "x test (184320, 1, 1, 4), y test (184320,)\n", + "train (array([589552., 17534., 1208., 0., 1362., 3112., 1632.]), array([0, 1, 2, 3, 4, 5, 6, 7]), ) \n", + "\n" + ] + }, + { + "data": { + "text/plain": "
", + "image/png": "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\n" + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "train (array([412687., 412687., 412687., 0., 412687., 412687., 412687.]), array([0, 1, 2, 3, 4, 5, 6, 7]), ) \n", + "\n" + ] + }, + { + "data": { + "text/plain": "
", + "image/png": "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\n" + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "train (array([23333., 23333., 23334., 0., 23333., 23333., 23334.]), array([0, 1, 2, 3, 4, 5, 6, 7]), ) \n", + "\n" + ] + }, + { + "data": { + "text/plain": "
", + "image/png": "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\n" + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "140000\n" + ] + } + ], + "source": [ + "from matplotlib import pyplot as plt\n", + "from sklearn.model_selection import train_test_split\n", + "from models import train_rf_and_report\n", + "from imblearn.over_sampling import RandomOverSampler\n", + "\n", + "# 读取数据\n", + "with open(dataset_file, 'rb') as f:\n", + " x_list, y_list = pickle.load(f)\n", + "# 确保数据当中x和y数量对得上\n", + "assert len(x_list) == len(y_list)\n", + "print(\"数据量: \", len(x_list))\n", + "x, y = np.asarray(x_list), np.asarray(y_list, dtype=int)\n", + "x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.3, random_state=5,\n", + " shuffle=True, stratify=y)\n", + "print(f\"x train {x_train.shape}, y train {y_train.shape}\\n\"\n", + " f\"x test {x_test.shape}, y test {y_test.shape}\")\n", + "fig, axs = plt.subplots(figsize=(10, 6))\n", + "hist_res_train = axs.hist(y, [0, 1, 2, 3, 4, 5, 6, 7], align='mid')\n", + "print(f'train {hist_res_train} \\n')\n", + "plt.show()\n", + "\n", + "ros = RandomOverSampler(random_state=0)\n", + "x_train_shape = x_train.shape\n", + "x_train = x_train.reshape((x_train.shape[0], -1))\n", + "x_resampled, y_resampled = ros.fit_resample(x_train, y_train)\n", + "# 画图\n", + "fig, axs = plt.subplots(figsize=(10, 6))\n", + "hist_res_train = axs.hist(y_resampled, [0, 1, 2, 3, 4, 5, 6, 7], align='mid')\n", + "print(f'train {hist_res_train} \\n')\n", + "plt.show()\n", + "# 抽样\n", + "x_train, _, y_train, _ = train_test_split(x_resampled, y_resampled, train_size=train_size, random_state=0, shuffle=True, stratify=y_resampled)\n", + "# 画图\n", + "fig, axs = plt.subplots(figsize=(10, 6))\n", + "hist_res_train = axs.hist(y_train, [0, 1, 2, 3, 4, 5, 6, 7], align='mid')\n", + "print(f'train {hist_res_train} \\n')\n", + "plt.show()\n", + "x_train = x_train.reshape(x_train.shape[0], x_train_shape[1], x_train_shape[2], x_train_shape[3])\n", + "print(len(x_train))" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "markdown", + "source": [ + "## 模型训练" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%% md\n" + } + } + }, + { + "cell_type": "code", + "execution_count": 6, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "预测时间: 0.032733917236328125\n", + "训练集acc:1.0\n", + "测试集acc:0.9790999348958334\n", + "--------------------------------------------------\n", + "测试集报告\n", + " precision recall f1-score support\n", + "\n", + " 0 1.00 0.98 0.99 589552\n", + " 1 0.72 0.96 0.82 17534\n", + " 2 0.66 0.89 0.76 1208\n", + " 4 0.71 0.86 0.77 1362\n", + " 5 0.48 0.87 0.62 3112\n", + " 6 0.43 0.90 0.58 1632\n", + "\n", + " accuracy 0.98 614400\n", + " macro avg 0.66 0.91 0.76 614400\n", + "weighted avg 0.99 0.98 0.98 614400\n", + "\n", + "混淆矩阵:\n", + "[[578285 6281 383 204 2593 1806]\n", + " [ 166 16849 69 187 233 30]\n", + " [ 8 36 1075 47 37 5]\n", + " [ 4 104 52 1166 34 2]\n", + " [ 56 147 48 43 2716 102]\n", + " [ 31 21 8 1 103 1468]]\n", + "二分类报告:\n", + " precision recall f1-score support\n", + "\n", + " 0 1.00 0.99 1.00 607086\n", + " 2 0.56 0.94 0.70 7314\n", + "\n", + " accuracy 0.99 614400\n", + " macro avg 0.78 0.97 0.85 614400\n", + "weighted avg 0.99 0.99 0.99 614400\n", + "\n", + "二混淆矩阵:\n", + "[[601581 5505]\n", + " [ 407 6907]]\n" + ] + } + ], + "source": [ + "from models import feature\n", + "from models import train_t_and_report\n", + "\n", + "features_train = feature(x_train)\n", + "features_test = feature(x_test)\n", + "feature_x = feature(x)\n", + "clf = train_t_and_report(features_train, y_train, feature_x, y, save_path=model_file)" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "markdown", + "source": [ + "## 模型评估" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%% md\n" + } + } + }, + { + "cell_type": "code", + "execution_count": 2, + "outputs": [ + { + "data": { + "text/plain": "
", + "image/png": "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\n" + }, + "metadata": { + "needs_background": "light" + }, + "output_type": "display_data" + } + ], + "source": [ + "from models import PixelWisedDetector\n", + "from utils import visualization_evaluation\n", + "\n", + "clf = PixelWisedDetector(model_path=model_file, channel_num=len(selected_bands))\n", + "visualization_evaluation(detector=clf, data_path=test_dir, selected_bands=selected_bands)" + ], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + }, + { + "cell_type": "code", + "execution_count": 9, + "outputs": [], + "source": [], + "metadata": { + "collapsed": false, + "pycharm": { + "name": "#%%\n" + } + } + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 2 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython2", + "version": "2.7.6" + } + }, + "nbformat": 4, + "nbformat_minor": 0 +} \ No newline at end of file diff --git a/dual_main.py b/dual_main.py new file mode 100755 index 0000000..abef7cc --- /dev/null +++ b/dual_main.py @@ -0,0 +1,110 @@ +import os +import numpy as np +from models import SpecDetector, PixelWisedDetector +from root_dir import ROOT_DIR +from multiprocessing import Process, Queue + +nrows, ncols, nbands = 256, 1024, 4 +img_fifo_path = "/tmp/dkimg.fifo" +mask_fifo_path = "/tmp/dkmask.fifo" +cmd_fifo_path = '/tmp/tobacco_cmd.fifo' + +pxl_model_path = "rf_1x1_c4_1_sen1_4.model" +blk_model_path = "rf_8x8_c4_185_sen32_4.model" + + +def main(pxl_model_path=pxl_model_path, blk_model_path=blk_model_path): + # 启动两个模型线程 + blk_cmd_queue, pxl_cmd_queue = Queue(maxsize=100), Queue(maxsize=100) + blk_img_queue, pxl_img_queue = Queue(maxsize=100), Queue(maxsize=100) + blk_msk_queue, pxl_msk_queue = Queue(maxsize=100), Queue(maxsize=100) + blk_process = Process(target=block_model, args=(blk_cmd_queue, blk_img_queue, blk_msk_queue, blk_model_path, )) + pxl_process = Process(target=pixel_model, args=(pxl_cmd_queue, pxl_img_queue, pxl_msk_queue, pxl_model_path, )) + blk_process.start() + pxl_process.start() + total_len = nrows * ncols * nbands * 4 + if not os.access(img_fifo_path, os.F_OK): + os.mkfifo(img_fifo_path, 0o777) + if not os.access(mask_fifo_path, os.F_OK): + os.mkfifo(mask_fifo_path, 0o777) + data = b'' + while True: + fd_img = os.open(img_fifo_path, os.O_RDONLY) + while len(data) < total_len: + data += os.read(fd_img, total_len) + if len(data) > total_len: + data_total = data[:total_len] + data = data[total_len:] + else: + data_total = data + data = b'' + os.close(fd_img) + + img = np.frombuffer(data_total, dtype=np.float32).reshape((nrows, nbands, -1)).transpose(0, 2, 1) + print(f"get img shape {img.shape}") + pxl_img_queue.put(img) + blk_img_queue.put(img) + pxl_msk = pxl_msk_queue.get() + blk_msk = blk_msk_queue.get() + mask = pxl_msk & blk_msk + print(f"predict success get mask shape: {mask.shape}") + # 写出 + fd_mask = os.open(mask_fifo_path, os.O_WRONLY) + os.write(fd_mask, mask.tobytes()) + os.close(fd_mask) + + +def block_model(cmd_queue: Queue, img_queue: Queue, mask_queue: Queue, blk_model_path=blk_model_path): + blk_model = SpecDetector(os.path.join(ROOT_DIR, "models", blk_model_path), blk_sz=8, channel_num=4) + _ = blk_model.predict(np.ones((nrows, ncols, nbands))) + rigor_rate = 70 + while True: + # deal with the cmd if cmd_queue is not empty + if not cmd_queue.empty(): + cmd = cmd_queue.get() + if isinstance(cmd, int): + rigor_rate = cmd + elif isinstance(cmd, str): + if cmd == 'stop': + break + else: + try: + blk_model_path = SpecDetector(os.path.join(ROOT_DIR, "models", blk_model_path), + blk_sz=8, channel_num=4) + except Exception as e: + print(f"Load Model Failed! {e}") + # deal with the img if img_queue is not empty + if not img_queue.empty(): + img = img_queue.get() + mask = blk_model.predict(img, rigor_rate) + mask_queue.put(mask) + + +def pixel_model(cmd_queue: Queue, img_queue: Queue, mask_queue: Queue, pixel_model_path=pxl_model_path): + pixel_model = PixelWisedDetector(os.path.join(ROOT_DIR, "models", pixel_model_path), blk_sz=1, channel_num=4) + _ = pixel_model.predict(np.ones((nrows, ncols, nbands))) + rigor_rate = 70 + while True: + # deal with the cmd if cmd_queue is not empty + if not cmd_queue.empty(): + cmd = cmd_queue.get() + if isinstance(cmd, int): + rigor_rate = cmd + elif isinstance(cmd, str): + if cmd == 'stop': + break + else: + try: + pixel_model = PixelWisedDetector(os.path.join(ROOT_DIR, "models", pixel_model_path), + blk_sz=1, channel_num=4) + except Exception as e: + print(f"Load Model Failed! {e}") + # deal with the img if img_queue is not empty + if not img_queue.empty(): + img = img_queue.get() + mask = pixel_model.predict(img, rigor_rate) + mask_queue.put(mask) + + +if __name__ == '__main__': + main() diff --git a/main.py b/main.py index 15bfc52..5015319 100755 --- a/main.py +++ b/main.py @@ -1,31 +1,44 @@ import os + +import cv2 import numpy as np from models import SpecDetector from root_dir import ROOT_DIR -nrows, ncols, nbands = 600, 1024, 4 +nrows, ncols, nbands = 256, 1024, 4 img_fifo_path = "/tmp/dkimg.fifo" mask_fifo_path = "/tmp/dkmask.fifo" -selected_model = "rf_8x8_c4_400_13.model" +selected_model = "rf_8x8_c4_185_sen32_4.model" + def main(): model_path = os.path.join(ROOT_DIR, "models", selected_model) detector = SpecDetector(model_path, blk_sz=8, channel_num=4) - _ = detector.predict(np.ones((600, 1024, 4))) + _ = detector.predict(np.ones((256, 1024, 4))) total_len = nrows * ncols * nbands * 4 + if not os.access(img_fifo_path, os.F_OK): os.mkfifo(img_fifo_path, 0o777) if not os.access(mask_fifo_path, os.F_OK): os.mkfifo(mask_fifo_path, 0o777) - - fd_img = os.open(img_fifo_path, os.O_RDONLY) - print("connect to fifo") - + data = b'' while True: - data = os.read(fd_img, total_len) - print("get img") - img = np.frombuffer(data, dtype=np.float32).reshape((nrows, nbands, -1)).transpose(0, 2, 1) + # 读取 + fd_img = os.open(img_fifo_path, os.O_RDONLY) + while len(data) < total_len: + data += os.read(fd_img, total_len) + if len(data) > total_len: + data_total = data[:total_len] + data = data[total_len:] + else: + data_total = data + data = b'' + + os.close(fd_img) + # 识别 + img = np.frombuffer(data_total, dtype=np.float32).reshape((nrows, nbands, -1)).transpose(0, 2, 1) mask = detector.predict(img) + # 写出 fd_mask = os.open(mask_fifo_path, os.O_WRONLY) os.write(fd_mask, mask.tobytes()) os.close(fd_mask) diff --git a/models.py b/models.py index ff425f5..3ef77cd 100755 --- a/models.py +++ b/models.py @@ -2,13 +2,15 @@ import os import pickle import time -import cv2 import numpy as np from sklearn.ensemble import RandomForestClassifier +from sklearn.tree import DecisionTreeClassifier from sklearn.decomposition import PCA from sklearn.metrics import accuracy_score, classification_report, confusion_matrix +nrows, ncols, nbands = 256, 1024, 4 + def feature(x): x = x.reshape((x.shape[0], -1)) @@ -42,6 +44,31 @@ def train_rf_and_report(train_x, train_y, test_x, test_y, return rfc +def train_t_and_report(train_x, train_y, test_x, test_y, save_path=None): + rfc = DecisionTreeClassifier(random_state=42, class_weight={0: 10, 1: 10}) + rfc = rfc.fit(train_x, train_y) + t1 = time.time() + y_pred = rfc.predict(test_x) + y_pred_binary = np.ones_like(y_pred) + y_pred_binary[(y_pred == 0) | (y_pred == 1)] = 0 + y_pred_binary[(y_pred > 1)] = 2 + test_y_binary = np.ones_like(test_y) + test_y_binary[(test_y == 0) | (test_y == 1)] = 0 + test_y_binary[(test_y > 1)] = 2 + print("预测时间:", time.time() - t1) + print("训练集acc:" + str(accuracy_score(train_y, rfc.predict(train_x)))) + print("测试集acc:" + str(accuracy_score(test_y, rfc.predict(test_x)))) + print('-'*50) + print('测试集报告\n' + str(classification_report(test_y, y_pred))) # 生成一个小报告呀 + print('混淆矩阵:\n' + str(confusion_matrix(test_y, y_pred))) # 这个也是,生成的矩阵的意思是有多少 + print('二分类报告:\n' + str(classification_report(test_y_binary, y_pred_binary))) # 生成一个小报告呀 + print('二混淆矩阵:\n' + str(confusion_matrix(test_y_binary, y_pred_binary))) # 这个也是,生成的矩阵的意思是有多少 + if save_path is not None: + with open(save_path, 'wb') as f: + pickle.dump(rfc, f) + return rfc + + def evaluation_and_report(model, test_x, test_y): t1 = time.time() y_pred = model.predict(test_x) @@ -96,21 +123,21 @@ def split_x(data: np.ndarray, blk_sz: int) -> list: """ Split the data into slices for classification.将数据划分为多个像素块,便于后续识别. - ;param data: image data, shape (num_rows x 1024 x num_channels) + ;param data: image data, shape (num_rows x ncols x num_channels) ;param blk_sz: block size ;param sensitivity: 最少有多少个杂物点能够被认为是杂物 ;return data_x, data_y: sliced data x (block_num x num_charnnels x blk_sz x blk_sz) """ x_list = [] - for i in range(0, 600 // blk_sz): - for j in range(0, 1024 // blk_sz): + for i in range(0, nrows // blk_sz): + for j in range(0, ncols // blk_sz): block_data = data[i * blk_sz: (i + 1) * blk_sz, j * blk_sz: (j + 1) * blk_sz, ...] x_list.append(block_data) return x_list class SpecDetector(object): - def __init__(self, model_path, blk_sz=8, channel_num=4): + def __init__(self, model_path, blk_sz=8, channel_num=nbands): self.blk_sz, self.channel_num = blk_sz, channel_num if os.path.exists(model_path): with open(model_path, "rb") as model_file: @@ -118,20 +145,22 @@ class SpecDetector(object): else: raise FileNotFoundError("Model File not found") - def predict(self, data): + def predict(self, data, rigor_rate=70): blocks = split_x(data, blk_sz=self.blk_sz) blocks = np.array(blocks) features = feature(np.array(blocks)) - y_pred = self.clf.predict(features) - y_pred_binary = np.ones_like(y_pred) + print("Spec Detector", rigor_rate) + y_pred = self.clf.predict_proba(features) + y_pred, y_prob = np.argmax(y_pred, axis=1), np.max(y_pred, axis=1) + y_pred_binary = np.zeros_like(y_pred) # classes merge - y_pred_binary[(y_pred == 0) | (y_pred == 1) | (y_pred == 3)] = 0 + y_pred_binary[((y_pred == 2) | (y_pred > 3)) & (y_prob > (100 - rigor_rate) / 100.0)] = 1 # transform to mask - mask = self.mask_transform(y_pred_binary, (1024, 600)) + mask = self.mask_transform(y_pred_binary, (ncols, nrows)) return mask def mask_transform(self, result, dst_size): - mask_size = 600 // self.blk_sz, 1024 // self.blk_sz + mask_size = nrows // self.blk_sz, ncols // self.blk_sz mask = np.zeros(mask_size, dtype=np.uint8) for idx, r in enumerate(result): row, col = idx // mask_size[1], idx % mask_size[1] @@ -140,8 +169,34 @@ class SpecDetector(object): return mask +class PixelWisedDetector(object): + def __init__(self, model_path, blk_sz=1, channel_num=nbands): + self.blk_sz, self.channel_num = blk_sz, channel_num + if os.path.exists(model_path): + with open(model_path, "rb") as model_file: + self.clf = pickle.load(model_file) + else: + raise FileNotFoundError("Model File not found") + + def predict(self, data, rigor_rate=70): + features = data.reshape((-1, self.channel_num)) + y_pred = self.clf.predict(features, rigor_rate) + y_pred_binary = np.ones_like(y_pred, dtype=np.uint8) + print("pixel detector", rigor_rate) + # classes merge + y_pred_binary[(y_pred == 0) | (y_pred == 1) | (y_pred == 3)] = 0 + # transform to mask + mask = self.mask_transform(y_pred_binary) + return mask + + def mask_transform(self, result): + mask_size = (nrows, ncols) + mask = result.reshape(mask_size) + return mask + + class PcaSpecDetector(object): - def __init__(self, model_path, pca_path, blk_sz=8, channel_num=4): + def __init__(self, model_path, pca_path, blk_sz=8, channel_num=nbands): self.blk_sz, self.channel_num = blk_sz, channel_num if os.path.exists(model_path): with open(model_path, "rb") as model_file: @@ -163,11 +218,11 @@ class PcaSpecDetector(object): # classes merge y_pred_binary[(y_pred == 0) | (y_pred == 1) | (y_pred == 3)] = 0 # transform to mask - mask = self.mask_transform(y_pred_binary, (1024, 600)) + mask = self.mask_transform(y_pred_binary, (ncols, nrows)) return mask def mask_transform(self, result, dst_size): - mask_size = 600 // self.blk_sz, 1024 // self.blk_sz + mask_size = nrows // self.blk_sz, ncols // self.blk_sz mask = np.zeros(mask_size, dtype=np.uint8) for idx, r in enumerate(result): row, col = idx // mask_size[1], idx % mask_size[1] diff --git a/test_files/dual_main_test.py b/test_files/dual_main_test.py new file mode 100755 index 0000000..b9622a3 --- /dev/null +++ b/test_files/dual_main_test.py @@ -0,0 +1,61 @@ +import glob +import os +import unittest + +import cv2 +import numpy as np + +from utils import read_raw_file + +nrows, ncols = 256, 1024 + + +class DualMainTestCase(unittest.TestCase): + def test_dual_main(self): + test_img_dirs = '/Volumes/LENOVO_USB_HDD/zhouchao/616_cut/*.raw' + selected_bands = None + img_fifo_path = "/tmp/dkimg.fifo" + mask_fifo_path = "/tmp/dkmask.fifo" + + total_len = nrows * ncols + spectral_files = glob.glob(test_img_dirs) + print("reading raw files ...") + raw_files = [read_raw_file(file, selected_bands=selected_bands) for file in spectral_files] + print("reading file success!") + if not os.access(img_fifo_path, os.F_OK): + os.mkfifo(img_fifo_path, 0o777) + if not os.access(mask_fifo_path, os.F_OK): + os.mkfifo(mask_fifo_path, 0o777) + data = b'' + for raw_file in raw_files: + if raw_file.shape[0] > nrows: + raw_file = raw_file[:nrows, ...] + # 写出 + print(f"send {raw_file.shape}") + fd_img = os.open(img_fifo_path, os.O_WRONLY) + os.write(fd_img, raw_file.tobytes()) + os.close(fd_img) + # 等待 + fd_mask = os.open(mask_fifo_path, os.O_RDONLY) + while len(data) < total_len: + data += os.read(fd_mask, total_len) + if len(data) > total_len: + data_total = data[:total_len] + data = data[total_len:] + else: + data_total = data + data = b'' + os.close(fd_mask) + mask = np.frombuffer(data_total, dtype=np.uint8).reshape((-1, ncols)) + + # 显示 + rgb_img = np.asarray(raw_file[..., [0, 2, 3]] * 255, dtype=np.uint8) + mask_color = np.zeros_like(rgb_img) + mask_color[mask > 0] = (0, 0, 255) + combine = cv2.addWeighted(rgb_img, 1, mask_color, 0.5, 0) + cv2.imshow("img", combine) + cv2.waitKey(0) + + +if __name__ == '__main__': + unittest.main() diff --git a/utils.py b/utils.py index 182d2b0..b52cbb6 100755 --- a/utils.py +++ b/utils.py @@ -6,9 +6,12 @@ import os import time import matplotlib.pyplot as plt +import tqdm from models import SpecDetector +nrows, ncols = 256, 1024 + def trans_color(pixel: np.ndarray, color_dict: dict = None) -> int: """ @@ -35,6 +38,8 @@ def determine_class(pixel_blk: np.ndarray, sensitivity=8) -> int: :param sensitivity: 敏感度 :return: """ + if (pixel_blk.shape[0] ==1) and (pixel_blk.shape[1] == 1): + return pixel_blk[0][0] defect_dict = {0: 0, 1: 0, 2: 1, 3: 1, 4: 1, 5: 1, 6: 1} color_numbers = {cls: pixel_blk.shape[0] ** 2 - np.count_nonzero(pixel_blk - cls) for cls in defect_dict.keys()} @@ -55,7 +60,7 @@ def split_xy(data: np.ndarray, labeled_img: np.ndarray, blk_sz: int, sensitivity """ Split the data into slices for classification.将数据划分为多个像素块,便于后续识别. - ;param data: image data, shape (num_rows x 1024 x num_channels) + ;param data: image data, shape (num_rows x ncols x num_channels) ;param labeled_img: RGB labeled img with respect to the image! make sure that the defect is (255, 0, 0) and background is (255, 255, 255) ;param blk_sz: block size @@ -71,8 +76,8 @@ def split_xy(data: np.ndarray, labeled_img: np.ndarray, blk_sz: int, sensitivity truth_map = np.all(labeled_img == color, axis=2) class_img[truth_map] = class_idx x_list, y_list = [], [] - for i in range(0, 600 // blk_sz): - for j in range(0, 1024 // blk_sz): + for i in range(0, nrows // blk_sz): + for j in range(0, ncols // blk_sz): block_data = data[i * blk_sz: (i + 1) * blk_sz, j * blk_sz: (j + 1) * blk_sz, ...] block_label = class_img[i * blk_sz: (i + 1) * blk_sz, j * blk_sz: (j + 1) * blk_sz, ...] block_label = determine_class(block_label, sensitivity=sensitivity) @@ -90,14 +95,14 @@ def split_x(data: np.ndarray, blk_sz: int) -> list: """ Split the data into slices for classification.将数据划分为多个像素块,便于后续识别. - ;param data: image data, shape (num_rows x 1024 x num_channels) + ;param data: image data, shape (num_rows x ncols x num_channels) ;param blk_sz: block size ;param sensitivity: 最少有多少个杂物点能够被认为是杂物 ;return data_x, data_y: sliced data x (block_num x num_charnnels x blk_sz x blk_sz) """ x_list = [] - for i in range(0, 600 // blk_sz): - for j in range(0, 1024 // blk_sz): + for i in range(0, nrows // blk_sz): + for j in range(0, ncols // blk_sz): block_data = data[i * blk_sz: (i + 1) * blk_sz, j * blk_sz: (j + 1) * blk_sz, ...] x_list.append(block_data) return x_list @@ -105,7 +110,6 @@ def split_x(data: np.ndarray, blk_sz: int) -> list: def visualization_evaluation(detector, data_path, selected_bands=None): selected_bands = [76, 146, 216, 367, 383, 406] if selected_bands is None else selected_bands - nrows, ncols = 600, 1024 image_paths = glob.glob(os.path.join(data_path, "calibrated*.raw")) for idx, image_path in enumerate(image_paths): with open(image_path, 'rb') as f: @@ -132,9 +136,9 @@ def visualization_evaluation(detector, data_path, selected_bands=None): def visualization_y(y_list, k_size): - mask = np.zeros((600 // k_size, 1024 // k_size), dtype=np.uint8) + mask = np.zeros((nrows // k_size, ncols // k_size), dtype=np.uint8) for idx, r in enumerate(y_list): - row, col = idx // (1024 // k_size), idx % (1024 // k_size) + row, col = idx // (ncols // k_size), idx % (ncols // k_size) mask[row, col] = r fig, axs = plt.subplots() axs.imshow(mask) @@ -142,16 +146,23 @@ def visualization_y(y_list, k_size): def read_raw_file(file_name, selected_bands=None): + print(f"reading file {file_name}") with open(file_name, "rb") as f: - data = np.frombuffer(f.read(), dtype=np.float32).reshape((600, -1, 1024)).transpose(0, 2, 1) + data = np.frombuffer(f.read(), dtype=np.float32).reshape((600, -1, ncols)).transpose(0, 2, 1) if selected_bands is not None: data = data[..., selected_bands] return data +def write_raw_file(file_name, data: np.ndarray): + data = data.transpose(0, 2, 1).reshape((nrows, -1, ncols)) + with open(file_name, 'wb') as f: + f.write(data.tobytes()) + + def read_black_and_white_file(file_name): with open(file_name, "rb") as f: - data = np.frombuffer(f.read(), dtype=np.float32).reshape((1, 448, 1024)).transpose(0, 2, 1) + data = np.frombuffer(f.read(), dtype=np.float32).reshape((1, 448, ncols)).transpose(0, 2, 1) return data @@ -166,8 +177,8 @@ def generate_tobacco_label(data, model_file, blk_sz, selected_bands): model = SpecDetector(model_path=model_file, blk_sz=blk_sz, channel_num=len(selected_bands)) y_label = model.predict(data) x_list, y_list = [], [] - for i in range(0, 600 // blk_sz): - for j in range(0, 1024 // blk_sz): + for i in range(0, nrows // blk_sz): + for j in range(0, ncols // blk_sz): if np.sum(np.sum(y_label[i * blk_sz: (i + 1) * blk_sz, j * blk_sz: (j + 1) * blk_sz, ...])) \ > 0: block_data = data[i * blk_sz: (i + 1) * blk_sz, j * blk_sz: (j + 1) * blk_sz, ...] @@ -179,8 +190,8 @@ def generate_tobacco_label(data, model_file, blk_sz, selected_bands): def generate_impurity_label(data, light_threshold, color_dict, split_line=0, target_class_right=None, target_class_left=None, ): y_label = np.zeros((data.shape[0], data.shape[1])) - for i in range(0, 600): - for j in range(0, 1024): + for i in range(0, nrows): + for j in range(0, ncols): if np.sum(np.sum(data[i, j])) >= light_threshold: if j > split_line: y_label[i, j] = target_class_right @@ -192,3 +203,21 @@ def generate_impurity_label(data, light_threshold, color_dict, split_line=0, tar axs[1].matshow(data[..., 0]) plt.show() return pic + + +def file_transform(input_dir, output_dir, selected_bands=None): + files = os.listdir(input_dir) + filtered_files = [file for file in files if file.endswith('.raw')] + os.makedirs(output_dir, mode=0o777, exist_ok=True) + for file_path in filtered_files: + input_path = os.path.join(input_dir, file_path) + output_path = os.path.join(output_dir, file_path) + data = read_raw_file(input_path, selected_bands=selected_bands) + write_raw_file(output_path, data) + + +if __name__ == '__main__': + selected_bands = [127, 201, 202, 294] + input_dir, output_dir = r"/Volumes/LENOVO_USB_HDD/zhouchao/616/",\ + r"/Volumes/LENOVO_USB_HDD/zhouchao/616_cut/" + file_transform(input_dir=input_dir, output_dir=output_dir, selected_bands=selected_bands) \ No newline at end of file