python中绘制case(订单) l(长) w(宽) h(高) num(数量) 1 170 110 27 7 1 210 200 30 1的装载在长为270宽为200高为90的箱子上,绘制三维长方体图,每个商品用长方体表示、长方体不可以重叠且颜色不同

时间: 2023-12-03 10:45:50 浏览: 92
这个问题可以通过使用Python中的matplotlib库来解决。以下是一个基本的示例代码: ```python import matplotlib.pyplot as plt from mpl_toolkits.mplot3d import Axes3D # 订单中的商品信息 items = [ {'l': 170, 'w': 110, 'h': 27, 'num': 7}, {'l': 210, 'w': 200, 'h': 30, 'num': 1}, {'l': 1, 'w': 1, 'h': 1, 'num': 0} # 用于表示箱子 ] # 箱子的尺寸 box_l = 270 box_w = 200 box_h = 90 # 创建一个三维坐标系 fig = plt.figure() ax = fig.add_subplot(111, projection='3d') # 绘制箱子 box_x = [0, box_l, box_l, 0, 0, 0, box_l, box_l] box_y = [0, 0, box_w, box_w, 0, box_w, box_w, 0] box_z = [0, 0, 0, 0, box_h, box_h, box_h, box_h] ax.plot(box_x, box_y, box_z) # 绘制商品 start_x = 0 start_y = 0 start_z = 0 for item in items: for i in range(item['num']): end_x = start_x + item['l'] end_y = start_y + item['w'] end_z = start_z + item['h'] x = [start_x, end_x, end_x, start_x, start_x, start_x, end_x, end_x] y = [start_y, start_y, end_y, end_y, start_y, end_y, end_y, start_y] z = [start_z, start_z, start_z, start_z, end_z, end_z, end_z, end_z] ax.plot(x, y, z) start_x += item['l'] start_y += item['w'] start_x = 0 # 设置坐标轴范围 ax.set_xlim3d(0, box_l) ax.set_ylim3d(0, box_w) ax.set_zlim3d(0, box_h) # 显示图形 plt.show() ``` 该代码将绘制一个三维长方体图,其中箱子用红色线条表示,每个商品使用不同的颜色表示,并且它们不会重叠。您可以根据需要修改商品和箱子的尺寸和数量。
阅读全文

相关推荐

#define _CRT_SECURE_NO_WARNINGS #pragma comment(linker, "/subsystem:windows /entry:mainCRTStartup") #include <graphics.h> // EasyX图形库头文件 #include <conio.h> #include <stdio.h> #include <stdlib.h> #include <windows.h> #include <time.h> // 宏定义 #define TRUE 1 #define FALSE 0 #define MAX_SIZE 7 // 最大拼图规模 #define N 9 // 拼图块种类数 #define ROWS 3 // 行数 #define COLS 3 // 列数 // 全局变量 int WINDOW_WIDTH = 1000; // 窗口宽度 int WINDOW_HEIGHT =1000; // 窗口高度 int BUTTON_X = 300; // 按钮X坐标 int BUTTON_Y = 200; // 按钮Y坐标 int BUTTON_WIDTH = 200; // 按钮宽度 int BUTTON_HEIGHT = 50; // 按钮高度 int degrees[] = { 3,4,5,6,7 }; // 难度级别对应的拼图尺寸 char str[N][1000]; // 存储传统文化描述的数组 int key = 0; // 全局键值变量 ExMessage msg; // 消息结构体变量 // 新增计时相关全局变量 time_t start_time; // 游戏开始时间 int is_time_up = FALSE; // 超时标志 const int TIMELIMIT = 120; // 2分钟倒计时(秒) // 可选图片结构体 typedef struct { const char* name; // 图片名称 int num; // 图片编号 } Pictures; // 图片资源数组 Pictures picture[] = { {"京剧脸谱",1}, {"中国结",2}, {"剪纸",3}, {"书法",4}, {"国画",5}, {"陶瓷",6}, {"刺绣",7}, {"皮影",8}, {"灯笼",9} }; // 拼图游戏结构体 typedef struct { int size; //拼图规模 int matrix[MAX_SIZE][MAX_SIZE]; //拼图块版块 int emptyRow, emptyCol; //空白格位置 int moves; //移动步数 int picIndex; //选择的图片索引 int species[N]; //选择的图片 } Blocks; void ReadData();//数据读取函数(从文件读取传统文化描述) void WriteSingleSelectedData(int i);//显示单个传统文化描述 void WriteAllData();//显示所有传统文化描述 void GetImage(const char* filename, int lx, int ly);//加载并显示图片 void cut_image(const char* filename, int size);//切割图片函数(备用) int isClick(int x, int y);//按钮点击检测函数 void Button(int, int, const char* word);//绘制按钮函数 int ModeSelection();//模式选择界面 int Selectpicture();//图片选择界面 void Information(int key);//游戏说明界面 void InitPuzzle(Blocks* game, int size, int picIndex);//初始化拼图游戏 int CheckWin(Blocks* game);//检查是否胜利 int MoveTile(Blocks* game, int direction);//移动拼图块 void DrawPuzzle(Blocks* game);//绘制拼图界面 void ShowWinScreen(Blocks* game);//显示胜利界面 void ShowTimeOverScreen();//显示超时界面 void PuzzlePage(int degree, int picIndex);//拼图游戏主循环 void Menu();//菜单 int main() { // 初始化随机数种子 srand((unsigned)time(NULL)); // 读取传统文化描述数据 ReadData(); // 进入主菜单循环 while (TRUE) { Menu(); } closegraph();//关闭图形窗口 return 0; } //数据读取函数(从文件读取传统文化描述) void ReadData() { FILE* file = fopen("data.txt", "r"); if (file == NULL) { // 文件打开失败时使用默认文本 for (int i = 0; i < N; i++) { sprintf(str[i], "元素 %d 描述加载失败", i + 1); } return; } // 逐行读取描述 for (int i = 0; i < N; i++) { if (fgets(str[i], sizeof(str[i]), file) != NULL) { // 去除换行符 size_t len = strlen(str[i]); if (len > 0 && str[i][len - 1] == '\n') { str[i][len - 1] = '\0'; } } } fclose(file); } //显示单个传统文化描述 void WriteSingleSelectedData(int i) { outtextxy(0, 0, str[i]); } //显示所有传统文化描述 void WriteAllData() { for (int i = 0; i < N; i++) { outtextxy(i, i * 5, str[i]); } } //加载并显示图片 void GetImage(const char* filename, int lx, int ly) { IMAGE image; loadimage(&image, filename); putimage(lx, ly, &image); } //切割图片函数 void cut_image(const char* filename, int size) { IMAGE img; loadimage(&img, filename); // 加载图片 if (img.getwidth() == 0 || img.getheight() == 0) { // 加载失败 outtextxy(100, 100, "图片加载失败!"); return; } // 获取图片宽度和高度 int width = img.getwidth(); int height = img.getheight(); //目标宽高 int targetWidth = 200; int targetHeight = 200; // 定义切割块的大小 const int blockWidth = width / size; const int blockHeight = height / size; // 分配二维指针数组 IMAGE*** blocks = (IMAGE***)malloc(size * sizeof(IMAGE*)); for (int i = 0; i < size; i++) { blocks[i] = (IMAGE**)malloc(size * sizeof(IMAGE*)); for (int j = 0; j < size; j++) { blocks[i][j] = (IMAGE*)malloc(sizeof(IMAGE)); /*切割*/ // 初始化每个块的大小 blocks[i][j] = new IMAGE(blockWidth, blockHeight); // 从原图中截取对应区域 // 切换到img作为绘图设备 SetWorkingImage(&img); getimage(blocks[i][j], j * blockWidth, i * blockHeight, blockWidth, blockHeight); SetWorkingImage(); // 恢复到窗口 Resize(blocks[i][j], targetWidth, targetHeight); } } for (int i = 0; i < size; i++) { for (int j = 0; j < size; j++) { //putimage(200 + blockWidth * i, 200 + blockHeight * j, blocks[i][j]); if (i == size - 1 && i == j) { setfillcolor(RGB(30, 30, 30)); solidrectangle(100 + targetWidth * i, 100 + targetHeight * j, targetWidth + 100 + targetWidth * i, targetHeight + 100 + targetHeight * j); return; } putimage(100 + targetWidth * i, 100 + targetHeight * j, blocks[j][i]); setlinecolor(RGB(0, 0, 0)); rectangle(100 + targetWidth * i, 100 + targetHeight * j, targetWidth + 100 + targetWidth * i, targetHeight + 100 + targetHeight * j); } } //释放内存 for (int i = 0; i < size; i++) { for (int j = 0; j < size; j++) { delete blocks[i][j]; } free(blocks[i]); } free(blocks); } //按钮点击检测函数 int isClick(int x, int y) { // 检查鼠标消息队列中的所有消息 while (peekmessage(&msg, EX_MOUSE)) { if (msg.message == WM_LBUTTONDOWN) { // 检查是否在按钮区域内 int inArea = (msg.x >= BUTTON_X + x && msg.y >= BUTTON_Y + y && msg.x <= BUTTON_X + x + BUTTON_WIDTH && msg.y <= BUTTON_Y + y + BUTTON_HEIGHT); // 移除已处理的消息 while (peekmessage(&msg, EX_MOUSE)); return inArea; } // 移除其他鼠标消息 peekmessage(&msg, EX_MOUSE, PM_REMOVE); } return FALSE; } //绘制按钮函数 void Button(int x, int y, const char* word) { //设置按钮样式 setbkmode(TRANSPARENT);//设置背景模式透明 setfillcolor(RGB(231, 250, 235)); solidroundrect(BUTTON_X + x, BUTTON_Y + y, BUTTON_X + x + BUTTON_WIDTH, BUTTON_Y + y + BUTTON_HEIGHT, 20, 20);//透明边框矩形图 // 设置文字样式 settextstyle(50, 15, "华文宋体"); setbkmode(TRANSPARENT); settextcolor(RGB(35, 30, 30)); // 计算文字居中位置输出 int w = (BUTTON_WIDTH - textwidth(word)) / 2; int h = (BUTTON_HEIGHT - textheight(word)) / 2; outtextxy(BUTTON_X + x + w, BUTTON_Y + y + h, word); } //模式选择界面 int ModeSelection() { cleardevice(); GetImage("picture/bk.jpg", 0, 0); // 五种难度按钮 const char* str[] = { "简单模式","初级模式","中级模式","高级模式","终极模式" }; for (int i = 0; i < 5; i++) { Button(100, i * 100, str[i]); } // 显示提示信息 settextstyle(35, 20, "华文宋体"); outtextxy(300, 120, "请点击选择游戏难度"); // 等待选择 while (TRUE) { // 处理所有鼠标消息 while (peekmessage(&msg, EX_MOUSE)) { if (msg.message == WM_LBUTTONDOWN) { // 检查点击了哪个按钮 for (int i = 0; i < 5; i++) { int buttonTop = BUTTON_Y + i * 100; int buttonBottom = buttonTop + BUTTON_HEIGHT; if (msg.x >= BUTTON_X && msg.x <= BUTTON_X + BUTTON_WIDTH && msg.y >= buttonTop && msg.y <= buttonBottom) { return degrees[i]; } } } // 移除已处理的消息 peekmessage(&msg, EX_MOUSE, PM_REMOVE); } Sleep(10); } } //图片选择界面 int Selectpicture() { cleardevice(); const int startX = 0, startY = 10; int selected = -1; // 3x3网格显示9种传统文化图片选项 for (int i = 0; i < 9; i++) { int row = i / 3, col = i % 3; Button(startX + col * 180, startY + row * 150, picture[i].name); } // 显示提示信息 settextstyle(35, 20, "华文宋体"); outtextxy(300, 120, "请点击选择拼图图片"); // 等待选择 while (selected == -1) { // 处理所有鼠标消息 while (peekmessage(&msg, EX_MOUSE)) { if (msg.message == WM_LBUTTONDOWN) { // 检查点击了哪个图片按钮 for (int i = 0; i < 9; i++) { int row = i / 3, col = i % 3; int buttonX = BUTTON_X + col * 200; int buttonY = BUTTON_Y + row * 150; if (msg.x >= buttonX && msg.x <= buttonX + BUTTON_WIDTH && msg.y >= buttonY && msg.y <= buttonY + BUTTON_HEIGHT) { selected = i; break; } } } // 移除已处理的消息 peekmessage(&msg, EX_MOUSE, PM_REMOVE); } Sleep(10); } return selected; } //游戏说明界面 void Information(int key) { cleardevice(); GetImage("picture/bk.jpg", 0, 0); // 设置文本样式 settextcolor(RGB(255, 215, 0)); // 金色 setbkmode(TRANSPARENT); settextstyle(25, 10, "华文宋体"); // 游戏说明文本 const char instructions[] = "这是一个传统文化主题的数字拼图游戏。\n" "目标是通过移动拼图块,使所有数字按顺序排列,右下角为空白格。\n" "每个数字对应一种中国传统文化元素:\n" "1: 京剧脸谱\n2: 中国结\n3: 剪纸\n" "4: 书法\n5: 国画\n6: 陶瓷\n" "7: 刺绣\n8: 皮影\n9: 灯笼\n\n" "操作方式:\n" "方向键 - 移动拼图\n" "ESC - 退出游戏\n" "L - 查看传统文化元素列表\n" "点击右下角图标返回主菜单..."; // 计算文本位置使其居中 int startX = 50, startY = 100; int lineHeight = 50; // 分行输出文本 char* context = NULL; char* line = strtok_s((char*)instructions, "\n", &context); while (line != NULL) { outtextxy(startX, startY, line); startY += lineHeight; line = strtok_s(NULL, "\n", &context); } // 等待按键并处理消息 while (true) { // 检查按键事件 if (kbhit()) { if (_getch()) { // 返回前清除消息队列 while (peekmessage(&msg, EX_MOUSE | EX_KEY, PM_REMOVE)); return; // 直接返回,不调用Menu() } } // 检查鼠标点击"返回"按钮 Button(400, 500, "返回主菜单"); if (isClick(400, 500)) { return; // 返回调用者 } Sleep(10); // 避免CPU占用过高 } } //初始化拼图游戏 void InitPuzzle(Blocks* game, int size, int picIndex) { game->size = size; game->picIndex = picIndex; game->moves = 0; // 初始化拼图数据 int num = 1; for (int i = 0; i < size; i++) { for (int j = 0; j < size; j++) { game->matrix[i][j] = (i == size - 1 && j == size - 1) ? 0 : num++; } } game->emptyRow = size - 1; game->emptyCol = size - 1; // 随机打乱拼图(减少循环次数,避免卡顿) for (int i = 0; i < size * size * 20; i++) { // 原为100,现为20 int dir = rand() % 4; int newRow = game->emptyRow, newCol = game->emptyCol; switch (dir) { case 0: newRow--; break; // 上 case 1: newRow++; break; // 下 case 2: newCol--; break; // 左 case 3: newCol++; break; // 右 } // 检查移动是否有效 if (newRow >= 0 && newRow < size && newCol >= 0 && newCol < size) { // 交换空白格和目标格 game->matrix[game->emptyRow][game->emptyCol] = game->matrix[newRow][newCol]; game->matrix[newRow][newCol] = 0; game->emptyRow = newRow; game->emptyCol = newCol; } } // 记录开始时间 start_time = time(NULL); is_time_up = FALSE; // 重置超时标志 } //检查是否胜利 int CheckWin(Blocks* game) { int num = 1; for (int i = 0; i < game->size; i++) { for (int j = 0; j < game->size; j++) { // 检查右下角是否为空白格 if (i == game->size - 1 && j == game->size - 1) { if (game->matrix[i][j] != 0) return FALSE; } else { // 检查其他格子是否按顺序排列 if (game->matrix[i][j] != num++) return FALSE; } } } return TRUE; } //移动拼图块 int MoveTile(Blocks* game, int direction) { int newRow = game->emptyRow; int newCol = game->emptyCol; // 计算目标位置 switch (direction) { case 0: newRow--; break; // 上 case 1: newRow++; break; // 下 case 2: newCol--; break; // 左 case 3: newCol++; break; // 右 default: return FALSE; } // 检查边界 if (newRow < 0 || newRow >= game->size || newCol < 0 || newCol >= game->size) { return FALSE; } // 交换空白格和目标格 game->matrix[game->emptyRow][game->emptyCol] = game->matrix[newRow][newCol]; game->matrix[newRow][newCol] = 0; game->emptyRow = newRow; game->emptyCol = newCol; game->moves++; return TRUE; } // 绘制拼图界面(续) void DrawPuzzle(Blocks* game) { cleardevice(); GetImage("picture/bk.jpg", 0, 0); // 设置文本样式 settextstyle(30, 15, "华文宋体"); setbkmode(TRANSPARENT); // 显示标题和步数 settextcolor(WHITE); outtextxy(50, 20, "传统文化拼图游戏"); char movesText[50]; sprintf(movesText, "步数: %d", game->moves); outtextxy(50, 60, movesText); // 显示倒计时(调整位置,避免超出窗口) time_t current_time = time(NULL); int elapsed = (int)(current_time - start_time); int remaining = TIMELIMIT - elapsed; if (remaining < 0) remaining = 0; // 防止负数显示 char time_str[50]; sprintf(time_str, "剩余时间: %02d:%02d", remaining / 60, remaining % 60); // 时间小于10秒时显示红色警告 settextcolor(remaining <= 10 ? RED : WHITE); outtextxy(WINDOW_WIDTH - 300, 20, time_str); // 仅设置超时标志,不直接跳转界面 if (remaining == 0) { is_time_up = TRUE; } // 计算拼图块大小和位置 int blockSize = 80; int startX = (WINDOW_WIDTH - game->size * blockSize) / 2; int startY = 150; // 加载选中的图片 char filename[100]; sprintf(filename, "picture/%d.jpg", game->picIndex + 1); IMAGE img; loadimage(&img, filename); // 检查图片是否成功加载 if (img.getwidth() <= 0 || img.getheight() <= 0) { // 如果图片加载失败,使用默认数字块并显示提示 outtextxy(200, 200, "图片加载失败,使用数字模式"); for (int i = 0; i < game->size; i++) { for (int j = 0; j < game->size; j++) { int x = startX + j * blockSize; int y = startY + i * blockSize; if (game->matrix[i][j] == 0) { // 绘制空白格 setfillcolor(RGB(200, 200, 200)); fillrectangle(x, y, x + blockSize, y + blockSize); settextcolor(BLACK); outtextxy(x + blockSize / 2 - 10, y + blockSize / 2 - 10, "●"); } else { // 绘制拼图块 setfillcolor(RGB(231, 250, 235)); fillrectangle(x, y, x + blockSize, y + blockSize); // 显示数字 char numText[10]; sprintf(numText, "%d", game->matrix[i][j]); settextcolor(BLACK); outtextxy(x + blockSize / 2 - 10, y + blockSize / 2 - 10, numText); } // 绘制边框 setlinecolor(BLACK); rectangle(x, y, x + blockSize, y + blockSize); } } } else { // 图片加载成功,切割图片并显示 int imgWidth = img.getwidth(); int imgHeight = img.getheight(); for (int i = 0; i < game->size; i++) { for (int j = 0; j < game->size; j++) { int x = startX + j * blockSize; int y = startY + i * blockSize; if (game->matrix[i][j] == 0) { // 绘制空白格 setfillcolor(RGB(200, 200, 200)); fillrectangle(x, y, x + blockSize, y + blockSize); } else { // 计算当前拼图块在原图中的位置 int pieceIndex = game->matrix[i][j] - 1; // 0到size*size-1 int pieceRow = pieceIndex / game->size; int pieceCol = pieceIndex % game->size; // 计算在原图中的坐标和大小 int srcX = pieceCol * (imgWidth / game->size); int srcY = pieceRow * (imgHeight / game->size); int pieceWidth = imgWidth / game->size; int pieceHeight = imgHeight / game->size; // 切割并显示拼图块 IMAGE piece; loadimage(&piece, NULL, pieceWidth, pieceHeight); // 设置原图为当前绘图设备 SetWorkingImage(&img); // 从原图中切割指定区域 getimage(&piece, srcX, srcY, pieceWidth, pieceHeight); // 恢复默认绘图设备 SetWorkingImage(NULL); putimage(x, y, &piece); } // 绘制边框 setlinecolor(BLACK); rectangle(x, y, x + blockSize, y + blockSize); } } } // 显示操作提示 settextstyle(20, 10, "华文宋体"); outtextxy(50, WINDOW_HEIGHT - 60, "方向键: 移动拼图 ESC: 退出游戏 L: 查看元素列表"); } // 显示胜利界面(移除音效) void ShowWinScreen(Blocks* game) { cleardevice(); GetImage("picture/bk.jpg", 0, 0); // 设置胜利信息样式 settextstyle(50, 25, "华文行楷"); setbkmode(TRANSPARENT); settextcolor(RGB(255, 215, 0)); // 金色 // 显示胜利信息 outtextxy(WINDOW_WIDTH / 2 - 150, WINDOW_HEIGHT / 2 - 100, "恭喜你完成了拼图!"); // 显示步数 char movesText[50]; sprintf(movesText, "总步数: %d", game->moves); outtextxy(WINDOW_WIDTH / 2 - 100, WINDOW_HEIGHT / 2, movesText); // 显示用时 time_t end_time = time(NULL); int total_seconds = (int)(end_time - start_time); char timeText[50]; sprintf(timeText, "用时: %02d:%02d", total_seconds / 60, total_seconds % 60); outtextxy(WINDOW_WIDTH / 2 - 100, WINDOW_HEIGHT / 2 + 50, timeText); // 显示返回提示 settextstyle(30, 15, "华文宋体"); outtextxy(WINDOW_WIDTH / 2 - 100, WINDOW_HEIGHT / 2 + 100, "按任意键返回主菜单"); // 等待按键 _getch(); } // 显示超时界面(移除音效) void ShowTimeOverScreen() { cleardevice(); GetImage("picture/bk.jpg", 0, 0); // 设置文字样式 settextstyle(50, 25, "华文行楷"); setbkmode(TRANSPARENT); settextcolor(RED); // 显示超时信息 outtextxy(WINDOW_WIDTH / 2 - 200, WINDOW_HEIGHT / 2 - 100, "时间到!拼图失败!"); // 显示返回提示 settextstyle(30, 15, "华文宋体"); outtextxy(WINDOW_WIDTH / 2 - 150, WINDOW_HEIGHT / 2 + 50, "按任意键返回主菜单"); // 等待按键 _getch(); } // 拼图游戏主循环(移除音效) void PuzzlePage(int degree, int picIndex) { cleardevice(); GetImage("picture/bk.jpg", 0, 0); Blocks game; InitPuzzle(&game, degree, picIndex); while (TRUE) { DrawPuzzle(&game); if (CheckWin(&game)) { ShowWinScreen(&game); return; } if (is_time_up) { ShowTimeOverScreen(); return; } // 处理键盘输入 if (peekmessage(&msg, EX_KEY)) { if (msg.message == WM_KEYDOWN) { switch (msg.vkcode) { case VK_UP: MoveTile(&game, 0); break; case VK_DOWN: MoveTile(&game, 1); break; case VK_LEFT: MoveTile(&game, 2); break; case VK_RIGHT: MoveTile(&game, 3); break; case 'L': // 显示传统文化元素列表 Information(picIndex); break; case VK_ESCAPE: return; // 返回上级菜单 } } } Sleep(50); // 控制刷新率 } } // 主菜单界面(移除音效) void Menu() { // 初始化图形窗口 initgraph(WINDOW_WIDTH, WINDOW_HEIGHT); // 设置背景 GetImage("picture/bk.jpg", 0, 0); // 设置标题样式 settextcolor(RGB(39, 220, 220)); setbkmode(TRANSPARENT); settextstyle(100, 30, "华文行楷"); outtextxy(150, 50, "传统文化拼图游戏"); // 绘制菜单按钮 const char* word[] = { "开始游戏", "游戏说明", "退出" }; for (int i = 0; i < 3; i++) { Button(100, i * 150, word[i]); } // 处理菜单选择 while (1) { // 处理所有鼠标消息 while (peekmessage(&msg, EX_MOUSE)) { if (msg.message == WM_LBUTTONDOWN) { // 检查开始游戏按钮 if (msg.x >= BUTTON_X + 100 && msg.x <= BUTTON_X + 100 + BUTTON_WIDTH && msg.y >= BUTTON_Y + 0 && msg.y <= BUTTON_Y + 0 + BUTTON_HEIGHT) { int degree = ModeSelection(); int picIndex = Selectpicture(); PuzzlePage(degree, picIndex); return; } // 检查游戏说明按钮 else if (msg.x >= BUTTON_X + 100 && msg.x <= BUTTON_X + 100 + BUTTON_WIDTH && msg.y >= BUTTON_Y + 150 && msg.y <= BUTTON_Y + 150 + BUTTON_HEIGHT) { Information(key); return; } // 检查退出按钮 else if (msg.x >= BUTTON_X + 100 && msg.x <= BUTTON_X + 100 + BUTTON_WIDTH && msg.y >= BUTTON_Y + 300 && msg.y <= BUTTON_Y + 300 + BUTTON_HEIGHT) { closegraph(); exit(0); } } // 移除已处理的消息 peekmessage(&msg, EX_MOUSE, PM_REMOVE); } Sleep(10); } } 在游戏说明界面中,点击L/l,可以进入传统文化介绍界面,界面中的文字来源于data.txt,点击返回键后,返回游戏说明界面

import time, sensor, image,ustruct from pyb import UART,LED from image import SEARCH_EX, SEARCH_DS #从imgae模块引入SEARCH_EX和SEARCH_DS。使用from import仅仅引入SEARCH_EX, #SEARCH_DS两个需要的部分,而不把image模块全部引入。 # Reset sensor sensor.reset() # Set sensor settings sensor.set_contrast(1) sensor.set_gainceiling(16) # Max resolution for template matching with SEARCH_EX is QQVGA sensor.set_framesize(sensor.QQVGA) # You can set windowing to reduce the search image. #sensor.set_windowing(((640-80)//2, (480-60)//2, 80, 60)) sensor.set_pixformat(sensor.GRAYSCALE) LED(1).on() LED(2).on() LED(3).on() template1 = image.Image("1.pgm") template2 = image.Image("2.pgm") templates3 = ["3.pgm","3_1.pgm","3_2.pgm","3_3.pgm","3_4.pgm","3_5.pgm","3_6.pgm","3_7.pgm","3_8.pgm"] templates4 = ["4.pgm","4_1.pgm","4_2.pgm","4_3.pgm","4_4.pgm","4_5.pgm","4_6.pgm","4_7.pgm","4_8.pgm"] templates5 = ["5.pgm","5_1.pgm","5_2.pgm","5_3.pgm","5_4.pgm","5_5.pgm","5_6.pgm","5_7.pgm","5_8.pgm"] templates6 = ["6.pgm","6_1.pgm","6_2.pgm","6_3.pgm","6_4.pgm","6_5.pgm","6_6.pgm","6_7.pgm","6_8.pgm"] templates7 = ["7.pgm","7_1.pgm","7_2.pgm","7_3.pgm","7_4.pgm","7_5.pgm","7_6.pgm","7_7.pgm","7_8.pgm"] templates8 = ["8.pgm","8_1.pgm","8_2.pgm","8_3.pgm","8_4.pgm","8_5.pgm","8_6.pgm","8_7.pgm","8_8.pgm"] #加载模板图片 clock = time.clock() uart = UART(3,115200) #定义串口3变量 uart.init(115200, bits=8, parity=None, stop=1) # init with given parameters def outuart(x,num): global uart #frame=[0x2C,18,cx%0xff,int(cx/0xff),cy%0xff,int(cy/0xff),0x5B]; #data = bytearray(frame) data = ustruct.pack("<bbhhhhb", #格式为俩个字符俩个短整型(2字节) 0x2C, #帧头1 0x12, #帧头2 int(x), # up sample by 4 #数据1 int(num), # up sample by 4 #数据2 int(0), # up sample by 4 #数据1 int(0), # up sample by 4 #数据2 0x5B) for x in range(5): uart.write(data)#必须要传入一个字节数组 time.sleep_ms(1) print(num) # Run template matching while (True): clock.tick() img = sensor.snapshot() num=0 r = img.find_template(template1, 0.70, step=5, search=SEARCH_EX) #, roi=(10, 0, 60, 60)) if r: print(r) #img.draw_rectangle(r) print('1') num=1 outuart(0,num) for x in range(5): LED(1).on() LED(2).off() LED(3).off() time.sleep_ms(100) LED(1).on() LED(2).on() LED(3).on() time.sleep_ms(100) r2_0 = img.find_template(template2, 0.70, step=5, search=SEARCH_EX) if r2_0: print(r2_0) #img.draw_rectangle(r1_3) print('2') num=2 outuart(0,num) for x in range(5): LED(1).on() LED(2).off() LED(3).off() time.sleep_ms(100) LED(1).on() LED(2).on() LED(3).on() time.sleep_ms(100) r3_0 = img.find_template(image.Image(templates3[0]), 0.70, step=5, search=SEARCH_EX) if r3_0: print(r3_0) #img.draw_rectangle(r1_1) print('3') num=3 outuart(0,num) for x in range(5): LED(1).on() LED(2).off() LED(3).off() time.sleep_ms(100) LED(1).on() LED(2).on() LED(3).on() time.sleep_ms(100) r4_0 = img.find_template(image.Image(templates4[0]), 0.70, step=5, search=SEARCH_EX) if r4_0: print(r4_0) #img.draw_rectangle(r1_1) print('4') num=4 outuart(0,num) for x in range(5): LED(1).on() LED(2).off() LED(3).off() time.sleep_ms(100) LED(1).on() LED(2).on() LED(3).on() time.sleep_ms(100) r5_0 = img.find_template(image.Image(templates5[0]), 0.70, step=5, search=SEARCH_EX) if r5_0: print(r5_0) #img.draw_rectangle(r1_1) print('5') num=5 outuart(0,num) for x in range(5): LED(1).on() LED(2).off() LED(3).off() time.sleep_ms(100) LED(1).on() LED(2).on() LED(3).on() time.sleep_ms(100) r6_0 = img.find_template(image.Image(templates6[0]), 0.70, step=5, search=SEARCH_EX) if r6_0: print(r6_0) #img.draw_rectangle(r1_1) print('6') num=6 outuart(0,num) for x in range(5): LED(1).on() LED(2).off() LED(3).off() time.sleep_ms(100) LED(1).on() LED(2).on() LED(3).on() time.sleep_ms(100) r7_0 = img.find_template(image.Image(templates7[0]), 0.70, step=5, search=SEARCH_EX) if r7_0: print(r7_0) #img.draw_rectangle(r1_1) print('7') num=7 outuart(0,num) for x in range(5): LED(1).on() LED(2).off() LED(3).off() time.sleep_ms(100) LED(1).on() LED(2).on() LED(3).on() time.sleep_ms(100) r8_0 = img.find_template(image.Image(templates8[0]), 0.70, step=5, search=SEARCH_EX) if r8_0: print(r8_0) #img.draw_rectangle(r1_1) print('8') num=8 outuart(0,num) for x in range(5): LED(1).on() LED(2).off() LED(3).off() time.sleep_ms(100) LED(1).on() LED(2).on() LED(3).on() time.sleep_ms(100) if num!=0: while(True): clock.tick() img = sensor.snapshot() if num==1: outuart(0,num) if num==2: outuart(0,num) if num==3: r3_0 = img.find_template(image.Image(templates3[0]), 0.70, step=5, search=SEARCH_EX) if r3_0: print(r3_0) #img.draw_rectangle(r1_1) print('3') outuart(r3_0[0],num) for x in range(5): LED(1).on() LED(2).off() LED(3).off() time.sleep_ms(100) LED(1).on() LED(2).on() LED(3).on() time.sleep_ms(100) r3_1 = img.find_template(image.Image(templates3[1]), 0.70, step=5, search=SEARCH_EX) if r3_1: print(r3_1) #img.draw_rectangle(r1_1) print('3') outuart(r3_1[0],num) for x in range(5): LED(1).on() LED(2).off() LED(3).off() time.sleep_ms(100) LED(1).on() LED(2).on() LED(3).on() time.sleep_ms(100) r3_2 = img.find_template(image.Image(templates3[2]), 0.70, step=5, search=SEARCH_EX) if r3_2: print(r3_2) #img.draw_rectangle(r1_2) print('3') outuart(r3_2[0],num) for x in range(5): LED(1).on() LED(2).off() LED(3).off() time.sleep_ms(100) LED(1).on() LED(2).on() LED(3).on() time.sleep_ms(100) r3_3 = img.find_template(image.Image(templates3[3]), 0.70, step=5, search=SEARCH_EX) if r3_3: print(r3_3) #img.draw_rectangle(r1_2) print('3') outuart(r3_3[0],num) for x in range(5): LED(1).on() LED(2).off() LED(3).off() time.sleep_ms(100) LED(1).on() LED(2).on() LED(3).on() time.sleep_ms(100) r3_4 = img.find_template(image.Image(templates3[4]), 0.70, step=5, search=SEARCH_EX) if r3_4: print(r3_4) #img.draw_rectangle(r1_2) print('3') outuart(r3_4[0],num) for x in range(5): LED(1).on() LED(2).off() LED(3).off() time.sleep_ms(100) LED(1).on() LED(2).on() LED(3).on() time.sleep_ms(100) r3_5 = img.find_template(image.Image(templates3[5]), 0.70, step=5, search=SEARCH_EX) if r3_5: print(r3_5) #img.draw_rectangle(r1_2) print('3') outuart(r3_5[0],num) for x in range(5): LED(1).on() LED(2).off() LED(3).off() time.sleep_ms(100) LED(1).on() LED(2).on() LED(3).on() time.sleep_ms(100) r3_6 = img.find_template(image.Image(templates3[6]), 0.70, step=5, search=SEARCH_EX) if r3_6: print(r3_6) #img.draw_rectangle(r1_2) print('3') outuart(r3_6[0],num) for x in range(5): LED(1).on() LED(2).off() LED(3).off() time.sleep_ms(100) LED(1).on() LED(2).on() LED(3).on() time.sleep_ms(100) r3_7 = img.find_template(image.Image(templates3[7]), 0.70, step=5, search=SEARCH_EX) if r3_7: print(r3_7) #img.draw_rectangle(r1_2) print('3') outuart(r3_7[0],num) for x in range(5): LED(1).on() LED(2).off() LED(3).off() time.sleep_ms(100) LED(1).on() LED(2).on() LED(3).on() time.sleep_ms(100) r3_8 = img.find_template(image.Image(templates3[8]), 0.70, step=5, search=SEARCH_EX) if r3_8: print(r3_8) #img.draw_rectangle(r1_2) print('3') outuart(r3_8[0],num) for x in range(5): LED(1).on() LED(2).off() LED(3).off() time.sleep_ms(100) LED(1).on() LED(2).on() LED(3).on() time.sleep_ms(100) if num==4: r4_0 = img.find_template(image.Image(templates4[0]), 0.70, step=5, search=SEARCH_EX) if r4_0: print(r4_0) #img.draw_rectangle(r1_1) print('4') outuart(r4_0[0],num) for x in range(5): LED(1).on() LED(2).off() LED(3).off() time.sleep_ms(100) LED(1).on() LED(2).on() LED(3).on() time.sleep_ms(100) r4_1 = img.find_template(image.Image(templates4[1]), 0.70, step=5, search=SEARCH_EX) if r4_1: print(r4_1) #img.draw_rectangle(r1_1) print('4') outuart(r4_1[0],num) for x in range(5): LED(1).on() LED(2).off() LED(3).off() time.sleep_ms(100) LED(1).on() LED(2).on() LED(3).on() time.sleep_ms(100) r4_2 = img.find_template(image.Image(templates4[2]), 0.70, step=5, search=SEARCH_EX) if r4_2: print(r4_2) #img.draw_rectangle(r1_2) print('4') outuart(r4_2[0],num) for x in range(5): LED(1).on() LED(2).off() LED(3).off() time.sleep_ms(100) LED(1).on() LED(2).on() LED(3).on() time.sleep_ms(100) r4_3 = img.find_template(image.Image(templates4[3]), 0.70, step=5, search=SEARCH_EX) if r4_3: print(r4_3) #img.draw_rectangle(r1_2) print('4') outuart(r4_3[0],num) for x in range(5): LED(1).on() LED(2).off() LED(3).off() time.sleep_ms(100) LED(1).on() LED(2).on() LED(3).on() time.sleep_ms(100) r4_4 = img.find_template(image.Image(templates4[4]), 0.70, step=5, search=SEARCH_EX) if r4_4: print(r4_4) #img.draw_rectangle(r1_2) print('4') outuart(r4_4[0],num) for x in range(5): LED(1).on() LED(2).off() LED(3).off() time.sleep_ms(100) LED(1).on() LED(2).on() LED(3).on() time.sleep_ms(100) r4_5 = img.find_template(image.Image(templates4[5]), 0.70, step=5, search=SEARCH_EX) if r4_5: print(r4_5) #img.draw_rectangle(r1_2) print('4') outuart(r4_5[0],num) for x in range(5): LED(1).on() LED(2).off() LED(3).off() time.sleep_ms(100) LED(1).on() LED(2).on() LED(3).on() time.sleep_ms(100) r4_6 = img.find_template(image.Image(templates4[6]), 0.70, step=5, search=SEARCH_EX) if r4_6: print(r4_6) #img.draw_rectangle(r1_2) print('4') outuart(r4_6[0],num) for x in range(5): LED(1).on() LED(2).off() LED(3).off() time.sleep_ms(100) LED(1).on() LED(2).on() LED(3).on() time.sleep_ms(100) r4_7 = img.find_template(image.Image(templates4[7]), 0.70, step=5, search=SEARCH_EX) if r4_7: print(r4_7) #img.draw_rectangle(r1_2) print('4') outuart(r4_7[0],num) for x in range(5): LED(1).on() LED(2).off() LED(3).off() time.sleep_ms(100) LED(1).on() LED(2).on() LED(3).on() time.sleep_ms(100) r4_8 = img.find_template(image.Image(templates4[8]), 0.70, step=5, search=SEARCH_EX) if r4_8: print(r4_8) #img.draw_rectangle(r1_2) print('4') outuart(r4_8[0],num) for x in range(5): LED(1).on() LED(2).off() LED(3).off() time.sleep_ms(100) LED(1).on() LED(2).on() LED(3).on() time.sleep_ms(100) if num==5: r5_0 = img.find_template(image.Image(templates5[0]), 0.70, step=5, search=SEARCH_EX) if r5_0: print(r5_0) #img.draw_rectangle(r1_1) print('5') outuart(r5_0[0],num) for x in range(5): LED(1).on() LED(2).off() LED(3).off() time.sleep_ms(100) LED(1).on() LED(2).on() LED(3).on() time.sleep_ms(100) r5_1 = img.find_template(image.Image(templates5[1]), 0.70, step=5, search=SEARCH_EX) if r5_1: print(r5_1) #img.draw_rectangle(r1_1) print('5') outuart(r5_1[0],num) for x in range(5): LED(1).on() LED(2).off() LED(3).off() time.sleep_ms(100) LED(1).on() LED(2).on() LED(3).on() time.sleep_ms(100) r5_2 = img.find_template(image.Image(templates5[2]), 0.70, step=5, search=SEARCH_EX) if r5_2: print(r5_2) #img.draw_rectangle(r1_2) print('5') outuart(r5_2[0],num) for x in range(5): LED(1).on() LED(2).off() LED(3).off() time.sleep_ms(100) LED(1).on() LED(2).on() LED(3).on() time.sleep_ms(100) r5_3 = img.find_template(image.Image(templates5[3]), 0.70, step=5, search=SEARCH_EX) if r5_3: print(r5_3) #img.draw_rectangle(r1_2) print('5') outuart(r5_3[0],num) for x in range(5): LED(1).on() LED(2).off() LED(3).off() time.sleep_ms(100) LED(1).on() LED(2).on() LED(3).on() time.sleep_ms(100) r5_4 = img.find_template(image.Image(templates5[4]), 0.70, step=5, search=SEARCH_EX) if r5_4: print(r5_4) #img.draw_rectangle(r1_2) print('5') outuart(r5_4[0],num) for x in range(5): LED(1).on() LED(2).off() LED(3).off() time.sleep_ms(100) LED(1).on() LED(2).on() LED(3).on() time.sleep_ms(100) r5_5 = img.find_template(image.Image(templates5[5]), 0.70, step=5, search=SEARCH_EX) if r5_5: print(r5_5) #img.draw_rectangle(r1_2) print('5') outuart(r5_5[0],num) for x in range(5): LED(1).on() LED(2).off() LED(3).off() time.sleep_ms(100) LED(1).on() LED(2).on() LED(3).on() time.sleep_ms(100) r5_6 = img.find_template(image.Image(templates5[6]), 0.70, step=5, search=SEARCH_EX) if r5_6: print(r5_6) #img.draw_rectangle(r1_2) print('5') outuart(r5_6[0],num) for x in range(5): LED(1).on() LED(2).off() LED(3).off() time.sleep_ms(100) LED(1).on() LED(2).on() LED(3).on() time.sleep_ms(100) r5_7 = img.find_template(image.Image(templates5[7]), 0.70, step=5, search=SEARCH_EX) if r5_7: print(r5_7) #img.draw_rectangle(r1_2) print('5') outuart(r5_7[0],num) for x in range(5): LED(1).on() LED(2).off() LED(3).off() time.sleep_ms(100) LED(1).on() LED(2).on() LED(3).on() time.sleep_ms(100) r5_8 = img.find_template(image.Image(templates5[8]), 0.70, step=5, search=SEARCH_EX) if r5_8: print(r5_8) #img.draw_rectangle(r1_2) print('5') outuart(r5_8[0],num) for x in range(5): LED(1).on() LED(2).off() LED(3).off() time.sleep_ms(100) LED(1).on() LED(2).on() LED(3).on() time.sleep_ms(100) if num==6: r6_0 = img.find_template(image.Image(templates6[0]), 0.70, step=5, search=SEARCH_EX) if r6_0: print(r6_0) #img.draw_rectangle(r1_1) print('6') outuart(r6_0[0],num) for x in range(5): LED(1).on() LED(2).off() LED(3).off() time.sleep_ms(100) LED(1).on() LED(2).on() LED(3).on() time.sleep_ms(100) r6_1 = img.find_template(image.Image(templates6[1]), 0.70, step=5, search=SEARCH_EX) if r6_1: print(r6_1) #img.draw_rectangle(r1_1) print('6') outuart(r6_1[0],num) for x in range(5): LED(1).on() LED(2).off() LED(3).off() time.sleep_ms(100) LED(1).on() LED(2).on() LED(3).on() time.sleep_ms(100) r6_2 = img.find_template(image.Image(templates6[2]), 0.70, step=5, search=SEARCH_EX) if r6_2: print(r6_2) #img.draw_rectangle(r1_2) print('6') outuart(r6_2[0],num) for x in range(5): LED(1).on() LED(2).off() LED(3).off() time.sleep_ms(100) LED(1).on() LED(2).on() LED(3).on() time.sleep_ms(100) r6_3 = img.find_template(image.Image(templates6[3]), 0.70, step=5, search=SEARCH_EX) if r6_3: print(r6_3) #img.draw_rectangle(r1_2) print('6') outuart(r6_3[0],num) for x in range(5): LED(1).on() LED(2).off() LED(3).off() time.sleep_ms(100) LED(1).on() LED(2).on() LED(3).on() time.sleep_ms(100) r6_4 = img.find_template(image.Image(templates6[4]), 0.70, step=5, search=SEARCH_EX) if r6_4: print(r6_4) #img.draw_rectangle(r1_2) print('6') outuart(r6_4[0],num) for x in range(5): LED(1).on() LED(2).off() LED(3).off() time.sleep_ms(100) LED(1).on() LED(2).on() LED(3).on() time.sleep_ms(100) r6_5 = img.find_template(image.Image(templates6[5]), 0.70, step=5, search=SEARCH_EX) if r6_5: print(r6_5) #img.draw_rectangle(r1_2) print('6') outuart(r6_5[0],num) for x in range(5): LED(1).on() LED(2).off() LED(3).off() time.sleep_ms(100) LED(1).on() LED(2).on() LED(3).on() time.sleep_ms(100) r6_6 = img.find_template(image.Image(templates6[6]), 0.70, step=5, search=SEARCH_EX) if r6_6: print(r6_6) #img.draw_rectangle(r1_2) print('6') outuart(r6_6[0],num) for x in range(5): LED(1).on() LED(2).off() LED(3).off() time.sleep_ms(100) LED(1).on() LED(2).on() LED(3).on() time.sleep_ms(100) r6_7 = img.find_template(image.Image(templates6[7]), 0.70, step=5, search=SEARCH_EX) if r6_7: print(r6_7) #img.draw_rectangle(r1_2) print('6') outuart(r6_7[0],num) for x in range(5): LED(1).on() LED(2).off() LED(3).off() time.sleep_ms(100) LED(1).on() LED(2).on() LED(3).on() time.sleep_ms(100) r6_8 = img.find_template(image.Image(templates6[8]), 0.70, step=5, search=SEARCH_EX) if r6_8: print(r6_8) #img.draw_rectangle(r1_2) print('6') outuart(r6_8[0],num) for x in range(5): LED(1).on() LED(2).off() LED(3).off() time.sleep_ms(100) LED(1).on() LED(2).on() LED(3).on() time.sleep_ms(100) if num==7: r7_0 = img.find_template(image.Image(templates7[0]), 0.70, step=5, search=SEARCH_EX) if r7_0: print(r7_0) #img.draw_rectangle(r1_1) print('7') outuart(r7_0[0],num) for x in range(5): LED(1).on() LED(2).off() LED(3).off() time.sleep_ms(100) LED(1).on() LED(2).on() LED(3).on() time.sleep_ms(100) r7_1 = img.find_template(image.Image(templates7[1]), 0.70, step=5, search=SEARCH_EX) if r7_1: print(r7_1) #img.draw_rectangle(r1_1) print('7') outuart(r7_1[0],num) for x in range(5): LED(1).on() LED(2).off() LED(3).off() time.sleep_ms(100) LED(1).on() LED(2).on() LED(3).on() time.sleep_ms(100) r7_2 = img.find_template(image.Image(templates7[2]), 0.70, step=5, search=SEARCH_EX) if r7_2: print(r7_2) #img.draw_rectangle(r1_2) print('7') outuart(r7_2[0],num) for x in range(5): LED(1).on() LED(2).off() LED(3).off() time.sleep_ms(100) LED(1).on() LED(2).on() LED(3).on() time.sleep_ms(100) r7_3 = img.find_template(image.Image(templates7[3]), 0.70, step=5, search=SEARCH_EX) if r7_3: print(r7_3) #img.draw_rectangle(r1_2) print('7') outuart(r7_3[0],num) for x in range(5): LED(1).on() LED(2).off() LED(3).off() time.sleep_ms(100) LED(1).on() LED(2).on() LED(3).on() time.sleep_ms(100) r7_4 = img.find_template(image.Image(templates7[4]), 0.70, step=5, search=SEARCH_EX) if r7_4: print(r7_4) #img.draw_rectangle(r1_2) print('7') outuart(r7_4[0],num) for x in range(5): LED(1).on() LED(2).off() LED(3).off() time.sleep_ms(100) LED(1).on() LED(2).on() LED(3).on() time.sleep_ms(100) r7_5 = img.find_template(image.Image(templates7[5]), 0.70, step=5, search=SEARCH_EX) if r7_5: print(r7_5) #img.draw_rectangle(r1_2) print('7') outuart(r7_5[0],num) for x in range(5): LED(1).on() LED(2).off() LED(3).off() time.sleep_ms(100) LED(1).on() LED(2).on() LED(3).on() time.sleep_ms(100) r7_6 = img.find_template(image.Image(templates7[6]), 0.70, step=5, search=SEARCH_EX) if r7_6: print(r7_6) #img.draw_rectangle(r1_2) print('7') outuart(r7_6[0],num) for x in range(5): LED(1).on() LED(2).off() LED(3).off() time.sleep_ms(100) LED(1).on() LED(2).on() LED(3).on() time.sleep_ms(100) r7_7 = img.find_template(image.Image(templates7[7]), 0.70, step=5, search=SEARCH_EX) if r7_7: print(r7_7) #img.draw_rectangle(r1_2) print('7') outuart(r7_7[0],num) for x in range(5): LED(1).on() LED(2).off() LED(3).off() time.sleep_ms(100) LED(1).on() LED(2).on() LED(3).on() time.sleep_ms(100) r7_8 = img.find_template(image.Image(templates7[8]), 0.70, step=5, search=SEARCH_EX) if r7_8: print(r7_8) #img.draw_rectangle(r1_2) print('7') outuart(r7_8[0],num) for x in range(5): LED(1).on() LED(2).off() LED(3).off() time.sleep_ms(100) LED(1).on() LED(2).on() LED(3).on() time.sleep_ms(100) if num==8: r8_0 = img.find_template(image.Image(templates8[0]), 0.70, step=5, search=SEARCH_EX) if r8_0: print(r8_0) #img.draw_rectangle(r1_1) print('8') outuart(r8_0[0],num) for x in range(5): LED(1).on() LED(2).off() LED(3).off() time.sleep_ms(100) LED(1).on() LED(2).on() LED(3).on() time.sleep_ms(100) r8_1 = img.find_template(image.Image(templates8[1]), 0.70, step=5, search=SEARCH_EX) if r8_1: print(r8_1) #img.draw_rectangle(r1_1) print('8') outuart(r8_1[0],num) for x in range(5): LED(1).on() LED(2).off() LED(3).off() time.sleep_ms(100) LED(1).on() LED(2).on() LED(3).on() time.sleep_ms(100) r8_2 = img.find_template(image.Image(templates8[2]), 0.70, step=5, search=SEARCH_EX) if r8_2: print(r8_2) #img.draw_rectangle(r1_2) print('8') outuart(r8_2[0],num) for x in range(5): LED(1).on() LED(2).off() LED(3).off() time.sleep_ms(100) LED(1).on() LED(2).on() LED(3).on() time.sleep_ms(100) r8_3 = img.find_template(image.Image(templates8[3]), 0.70, step=5, search=SEARCH_EX) if r8_3: print(r8_3) #img.draw_rectangle(r1_2) print('8') outuart(r8_3[0],num) for x in range(5): LED(1).on() LED(2).off() LED(3).off() time.sleep_ms(100) LED(1).on() LED(2).on() LED(3).on() time.sleep_ms(100) r8_4 = img.find_template(image.Image(templates8[4]), 0.70, step=5, search=SEARCH_EX) if r8_4: print(r8_4) #img.draw_rectangle(r1_2) print('8') outuart(r8_4[0],num) for x in range(5): LED(1).on() LED(2).off() LED(3).off() time.sleep_ms(100) LED(1).on() LED(2).on() LED(3).on() time.sleep_ms(100) r8_5 = img.find_template(image.Image(templates8[5]), 0.70, step=5, search=SEARCH_EX) if r8_5: print(r8_5) #img.draw_rectangle(r1_2) print('8') outuart(r8_5[0],num) for x in range(5): LED(1).on() LED(2).off() LED(3).off() time.sleep_ms(100) LED(1).on() LED(2).on() LED(3).on() time.sleep_ms(100) r8_6 = img.find_template(image.Image(templates8[6]), 0.70, step=5, search=SEARCH_EX) if r8_6: print(r8_6) #img.draw_rectangle(r1_2) print('8') outuart(r8_6[0],num) for x in range(5): LED(1).on() LED(2).off() LED(3).off() time.sleep_ms(100) LED(1).on() LED(2).on() LED(3).on() time.sleep_ms(100) if r8_7: print(r8_7) #img.draw_rectangle(r1_2) print('8') outuart(r8_7[0],num) for x in range(5): LED(1).on() LED(2).off() LED(3).off() time.sleep_ms(100) LED(1).on() LED(2).on() LED(3).on() time.sleep_ms(100) r8_8 = img.find_template(image.Image(templates3[8]), 0.70, step=5, search=SEARCH_EX) if r8_8: print(r8_8) #img.draw_rectangle(r1_2) print('8') outuart(r8_8[0],num) for x in range(5): LED(1).on() LED(2).off() LED(3).off() time.sleep_ms(100) LED(1).on() LED(2).on() LED(3).on() time.sleep_ms(100) print(clock.fps()) print(clock.fps())

帮我注释代码,并检查代码问题,我想得到空间解卷积之后的单细胞分辨率的可视化df = point_cell_count_pos df['cell_coord'] = df['cell_coord'].apply(ast.literal_eval) ## 对'cell_coord'列中的细胞坐标转换成列表类型 point_xy_cls_ori_proptofreq = [] ## 创建一个存储所有绘制图形的细胞的坐标,所在spot的该细胞类型的实际丰度,细胞类型,所在spot的barcode,所在spot实际画的细胞数,所在spot的细胞数 ct_Freq_allbc=pd.DataFrame(columns=celltype_Frequency_c2l.columns) ## 转换后的每个spot细胞丰度,未过滤 for bc in df.index: xy_ori_coord = df.loc[bc, "cell_coord"] # 获取该spot的所有细胞坐标列表 num = len(xy_ori_coord) if num == 0: continue bcneighbors = bc.split("_") # 得到所有邻居spot的barcode ctfreqidx = celltype_Frequency_c2l.index.isin(bcneighbors) ## 检查所有邻居spot是否在组织区域, if sum(ctfreqidx) == len(bcneighbors): ## 所有邻居spot都在组织区域,或者该spot本来就在组织区域 # 提取所有邻近spot的cell2location解卷积结果,逐列取平均,作为该spot的解卷积结果 allctfreq_bc = pd.DataFrame(celltype_Frequency_c2l.loc[ctfreqidx, :].mean(axis=0)).T elif sum(ctfreqidx) > 0: # 部分邻居spot在组织区域,按列求和,再除于邻居spot的个数 ct = list(celltype_Frequency_c2l.index[ctfreqidx].values) allctfreq_bc = pd.DataFrame(celltype_Frequency_c2l.loc[ct, :].sum(axis=0) / len(bcneighbors)).T else: continue ############方法一: # 计算该spot的每一种细胞类型的丰度占比 allctprop_bc = allctfreq_bc.div(allctfreq_bc.sum(axis=1), axis=0) # 转换成丰度的占比之后,把spot的细胞核识别细胞数量与占比相乘得到实际的丰度 allctprop_bc = allctprop_bc.apply(lambda x: x * num, axis=1) ct_Freq_allbc.loc[bc] = allctprop_bc.loc[0] ## 保存转换后的丰度矩阵 celltype_Proptofreq_bc = [] filterctProptofreq_bc = [] # 过滤掉的细胞类型 for cls in celltype_Frequency_c2l.columns: real_cell_freq = allctprop_bc.at[0,cls] # 使用细胞丰度的阈值进行过滤 if real_cell_freq >= ctfreq_threshold: celltype_Proptofreq_bc.append([real_cell_freq,cls, bc]) else: filterctProptofreq_bc.append([real_cell_freq,cls, bc]) celltype_Proptofreq_bc.sort(key=lambda x: x[0]) ## 排序 filterctProptofreq_bc.sort(key=lambda x: x[0], reverse=True) ## 低于阈值的细胞类型,从高到低排序 point = num ## 剩余的细胞数 bcplotpoint = 0 ## 已经分配好身份的细胞数 candidate = list(range(num)) # 可选择的点索引 for celltypefreq,celltype,_ in celltype_Proptofreq_bc: if point <= 0: break vis_point = math.ceil(celltypefreq) ## 向上取整,得到该细胞类型的细胞丰度画的细胞数 celltype_vis_point = min(vis_point, point) ## 实际画的细胞数,不会超过剩余的细胞数 selected = np.random.choice(candidate, celltype_vis_point, replace=False) for s in selected: x, y = xy_ori_coord[s] ## 提取坐标 point_xy_cls_ori_proptofreq.append([x,y,celltypefreq,celltype,bc,celltype_vis_point,num]) ## 分配细胞类型标签 point -= celltype_vis_point bcplotpoint += celltype_vis_point candidate = list(set(candidate) - set(selected)) # 20240913修改15:00 # 只考虑了应该画出来的细胞类型实际丰度大于剩余细胞数的情况, # 但是我们可能少考虑了小于剩余细胞数的情况 # 就是如果细胞类型很少了,他的丰度也不高,少于剩余细胞数,然后就会丢失一部分细胞,没有身份也就不会被画出来 # 对没有身份这部分细胞进行填补,把没有分配到身份的细胞分配一个丰度最高的过滤掉的细胞类型,按顺序填完 if bcplotpoint < num: notagpoint = num - bcplotpoint ## 剩余的未分配身份的细胞数 for ftcelltypefreq,ftcelltype,_ in filterctProptofreq_bc: if notagpoint <= 0: break ftvis_point = math.ceil(ftcelltypefreq) ## 向上取整,得到该细胞类型的细胞丰度画的细胞数 ftcelltype_vis_point = min(ftvis_point, notagpoint) ## 实际画的细胞数,不会超过剩余未分配身份的细胞数 ftselected = np.random.choice(candidate, ftcelltype_vis_point, replace=False) for z in ftselected: x, y = xy_ori_coord[z] point_xy_cls_ori_proptofreq.append([x,y,ftcelltypefreq,ftcelltype,bc,ftcelltype_vis_point,num]) notagpoint -= ftcelltype_vis_point candidate = list(set(candidate) - set(ftselected))

#define _CRT_SECURE_NO_WARNINGS #include "communication_system.h" #include <QFile> #include <QDataStream> #include <QDebug> #include <complex> #include <random> #include <cmath> #include <cstring> #include <algorithm> #include <QAudioDeviceInfo> #include <QAudioInput> #include <QAudioOutput> #include <QTimer> #include <QBuffer> #include <QtMath> #include <QValueAxis> #include <QChart> #include <QLineSeries> const float FSK_FREQ_SHIFT = 1000.0f; int find_frame_header(const float* data, int length, const QByteArray& header); #ifndef M_PI #define M_PI 3.14159265358979323846 #endif #ifndef M_PI_2 #define M_PI_2 (M_PI / 2.0) #endif #ifndef M_PI_4 #define M_PI_4 (M_PI / 4.0) #endif // ================= 初始化函数 ================= void init_transmitter(Transmitter* tx, SignalType sig_type, ModulationType mod_type, float carrier_freq, int sample_rate) { if (tx->samples) { delete[] tx->samples; } if (tx->modulated) { delete[] tx->modulated; } memset(tx, 0, sizeof(Transmitter)); tx->signal_type = sig_type; tx->mod_type = mod_type; tx->carrier_freq = carrier_freq; tx->sample_rate = sample_rate; tx->samples = new float[MAX_SAMPLES](); tx->modulated = new float[MAX_SAMPLES](); tx->bit_count = 0; tx->original_bit_count = 0; } void init_receiver(Receiver* rx, float carrier_freq, int sample_rate) { if (rx->received) { delete[] rx->received; } if (rx->demodulated) { delete[] rx->demodulated; } memset(rx, 0, sizeof(Receiver)); rx->carrier_freq = carrier_freq; rx->sample_rate = sample_rate; rx->received = new float[MAX_SAMPLES](); rx->demodulated = new float[MAX_SAMPLES](); rx->bit_count = 0; rx->sample_count = 0; memset(rx->decoded_text, 0, sizeof(rx->decoded_text)); memset(rx->binary_data, 0, sizeof(rx->binary_data)); } // ================= 信号生成 ================= void generate_signal(Transmitter* tx, int duration_ms) { int num_samples = (duration_ms * tx->sample_rate) / 1000; if (num_samples > MAX_SAMPLES) num_samples = MAX_SAMPLES; tx->sample_count = num_samples; switch (tx->signal_type) { case SINE_WAVE: for (int i = 0; i < num_samples; i++) { float t = static_cast<float>(i) / tx->sample_rate; tx->samples[i] = sin(2 * M_PI * 5.0f * t); } break; case SQUARE_WAVE: for (int i = 0; i < num_samples; i++) { float t = static_cast<float>(i) / tx->sample_rate; tx->samples[i] = (sin(2 * M_PI * 3.0f * t) > 0 ? 1.0f : -1.0f); } break; case SAWTOOTH_WAVE: for (int i = 0; i < num_samples; i++) { float t = static_cast<float>(i) / tx->sample_rate; tx->samples[i] = 2.0f * (t * 4.0f - floorf(t * 4.0f + 0.5f)); } break; case RANDOM_DATA: { std::random_device rd; std::mt19937 gen(rd()); std::uniform_real_distribution<float> dist(-1.0f, 1.0f); for (int i = 0; i < num_samples; i++) { tx->samples[i] = dist(gen); } break; } case TEXT_DATA: case FILE_DATA: // 在专用函数中处理 break; } } // ================= 文本数据处理 ================= void set_text_data(Transmitter* tx, const char* text) { // 直接存储UTF-8字节序列 size_t len = strlen(text); if (len >= MAX_TEXT_LENGTH) len = MAX_TEXT_LENGTH - 1; memcpy(tx->text_data, text, len); tx->text_data[len] = '\0'; tx->bit_count = 0; // 添加帧头:0x55 (01010101) uint8_t header = 0x55; for (int j = 7; j >= 0; j--) { if (tx->bit_count < MAX_BITS) { tx->binary_data[tx->bit_count++] = (header >> j) & 1; } } // 处理整个UTF-8字节序列 for (size_t i = 0; i < len; i++) { uint8_t c = (uint8_t)tx->text_data[i]; for (int j = 7; j >= 0; j--) { if (tx->bit_count < MAX_BITS) { tx->binary_data[tx->bit_count++] = (c >> j) & 1; } } } // 添加帧尾:0xAA (10101010) uint8_t footer = 0xAA; for (int j = 7; j >= 0; j--) { if (tx->bit_count < MAX_BITS) { tx->binary_data[tx->bit_count++] = (footer >> j) & 1; } } // 生成信号波形 tx->sample_count = tx->bit_count * static_cast<int>(SAMPLES_PER_BIT); if (tx->sample_count > MAX_SAMPLES) { tx->sample_count = MAX_SAMPLES; tx->bit_count = tx->sample_count / static_cast<int>(SAMPLES_PER_BIT); } for (int i = 0; i < tx->bit_count; i++) { for (int j = 0; j < static_cast<int>(SAMPLES_PER_BIT); j++) { int idx = i * static_cast<int>(SAMPLES_PER_BIT) + j; if (idx < MAX_SAMPLES) { tx->samples[idx] = tx->binary_data[i] ? 1.0f : -1.0f; } } } // 汉明编码 int original_bit_count = tx->bit_count; encode_hamming(tx->binary_data, tx->bit_count); tx->original_bit_count = original_bit_count; } // ================= 文件数据处理 ================= void load_file_data(Transmitter* tx, const char* filename) { strncpy(tx->filename, filename, 255); tx->filename[255] = '\0'; QFile file(filename); if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { qWarning() << "无法打开文件:" << filename; return; } QTextStream in(&file); tx->sample_count = 0; while (!in.atEnd() && tx->sample_count < MAX_SAMPLES) { QString line = in.readLine(); bool ok; float value = line.toFloat(&ok); if (ok) { tx->samples[tx->sample_count++] = value; } } file.close(); } // ================= 调制函数 ================= void modulate_signal(Transmitter* tx) { qDebug() << "调制信号 - 类型:" << tx->mod_type << "载波频率:" << tx->carrier_freq << "采样数:" << tx->sample_count; if (tx->signal_type == TEXT_DATA && tx->bit_count == 0) { qWarning() << "文本数据未设置!"; return; } if (tx->sample_count == 0) return; for (int i = 0; i < tx->sample_count; i++) { float t = static_cast<float>(i) / tx->sample_rate; float carrier = sin(2 * M_PI * tx->carrier_freq * t); float quadrature = cos(2 * M_PI * tx->carrier_freq * t); switch (tx->mod_type) { case BPSK: { float symbol = 0.0f; if (tx->signal_type == TEXT_DATA) { int bit_index = i / static_cast<int>(SAMPLES_PER_BIT); if (bit_index < tx->bit_count) { symbol = tx->binary_data[bit_index] ? 1.0f : -1.0f; } } else { symbol = tx->samples[i]; } tx->modulated[i] = symbol * carrier; break; } case QPSK: { if (tx->signal_type == TEXT_DATA) { int symbol_index = i / static_cast<int>(2 * SAMPLES_PER_BIT); if (symbol_index < tx->bit_count / 2) { int bit_index = symbol_index * 2; int bit1 = tx->binary_data[bit_index]; int bit2 = tx->binary_data[bit_index + 1]; float I = (bit1 == 0) ? -1.0f : 1.0f; float Q = (bit2 == 0) ? -1.0f : 1.0f; tx->modulated[i] = I * carrier + Q * quadrature; } else { tx->modulated[i] = 0.0f; } } else { tx->modulated[i] = tx->samples[i] * carrier; } break; } case FSK: { float base_freq = tx->carrier_freq; float freq_shift = 500.0f; if (tx->signal_type == TEXT_DATA) { int bit_index = i / static_cast<int>(SAMPLES_PER_BIT); float freq = base_freq; if (bit_index < tx->bit_count) { freq += tx->binary_data[bit_index] ? freq_shift : -freq_shift; } tx->modulated[i] = sin(2 * M_PI * freq * t); } else { float freq = base_freq + (tx->samples[i] > 0 ? freq_shift : -freq_shift); tx->modulated[i] = sin(2 * M_PI * freq * t); } break; } case AM: { float modulation_index = 0.8f; float analog = 0.0f; if (tx->signal_type == TEXT_DATA) { int bit_index = i / static_cast<int>(SAMPLES_PER_BIT); if (bit_index < tx->bit_count) { analog = tx->binary_data[bit_index] ? 1.0f : -1.0f; } } else { analog = tx->samples[i]; } tx->modulated[i] = (1.0f + modulation_index * analog) * carrier; break; } } } //添加噪声 apply_noise(tx->modulated, tx->sample_count, 80.0f); // 10dB SNR } // ================= 解调函数 ================= void demodulate_bpsk(Receiver* rx) { float last_filtered = 0.0f; const float alpha = 0.1f; // 低通滤波系数 for (int i = 0; i < rx->sample_count; i++) { float t = static_cast<float>(i) / rx->sample_rate; float carrier = sin(2 * M_PI * rx->carrier_freq * t); // 相干解调 float mixed = rx->received[i] * carrier; // 低通滤波 float filtered = alpha * mixed + (1.0f - alpha) * last_filtered; last_filtered = filtered; rx->demodulated[i] = filtered; // 调试输出 qDebug() << "Demodulated sample at index" << i << ": " << filtered; } } void demodulate_qpsk(Receiver* rx) { float last_i = 0.0f, last_q = 0.0f; const float alpha = 0.1f; // 低通滤波系数 for (int i = 0; i < rx->sample_count; i++) { float t = static_cast<float>(i) / rx->sample_rate; float carrier = sin(2 * M_PI * rx->carrier_freq * t); float quadrature = cos(2 * M_PI * rx->carrier_freq * t); // I路解调 float i_mixed = rx->received[i] * carrier; float i_filtered = alpha * i_mixed + (1.0f - alpha) * last_i; last_i = i_filtered; // Q路解调 float q_mixed = rx->received[i] * quadrature; float q_filtered = alpha * q_mixed + (1.0f - alpha) * last_q; last_q = q_filtered; // 合并为幅度信号 rx->demodulated[i] = sqrt(i_filtered * i_filtered + q_filtered * q_filtered); } } void demodulate_fsk(Receiver* rx) { if (rx->sample_count == 0) return; // 创建两个频率的载波表,优化计算效率 QVector<float> carrier1(rx->sample_count); QVector<float> carrier2(rx->sample_count); for (int i = 0; i < rx->sample_count; i++) { float t = static_cast<float>(i) / rx->sample_rate; carrier1[i] = qSin(2 * M_PI * rx->carrier_freq * t); carrier2[i] = qSin(2 * M_PI * (rx->carrier_freq + FSK_FREQ_SHIFT) * t); } // 滑动窗口相关器实现FSK解调 const int window_size = SAMPLES_PER_BIT / 2; // 相关窗口大小 QVector<float> demod_result(rx->sample_count); for (int i = window_size; i < rx->sample_count - window_size; i++) { float sum1 = 0.0f; float sum2 = 0.0f; // 计算窗口内与两个载波的相关性 for (int j = -window_size; j <= window_size; j++) { sum1 += rx->received[i + j] * carrier1[i + j]; sum2 += rx->received[i + j] * carrier2[i + j]; } // 归一化并计算差分 demod_result[i] = (sum1 - sum2) / (2 * window_size + 1); } // 低通滤波以平滑结果 const float alpha = 0.1f; float filtered = 0.0f; for (int i = 0; i < rx->sample_count; i++) { filtered = alpha * demod_result[i] + (1.0f - alpha) * filtered; rx->demodulated[i] = filtered; } qDebug() << "FSK解调完成,样本数:" << rx->sample_count; } // 改进AM解调:使用非相干解调 void demodulate_am(Receiver* rx) { float last_env = 0.0f; const float alpha = 0.05f; // 包络检测系数 for (int i = 0; i < rx->sample_count; i++) { // 计算包络 float env = fabs(rx->received[i]); env = alpha * env + (1.0f - alpha) * last_env; last_env = env; // 移除DC分量 static float dc_estimate = 0.0f; const float dc_alpha = 0.001f; dc_estimate = (1 - dc_alpha) * dc_estimate + dc_alpha * env; rx->demodulated[i] = env - dc_estimate; } // 归一化 float max_val = 0.001f; for (int i = 0; i < rx->sample_count; i++) { if (fabs(rx->demodulated[i]) > max_val) max_val = fabs(rx->demodulated[i]); } float scale = 1.0f / max_val; for (int i = 0; i < rx->sample_count; i++) { rx->demodulated[i] *= scale; } } void demodulate_signal(Receiver* rx) { qDebug() << "解调信号 - 类型:" << rx->mod_type << "载波频率:" << rx->carrier_freq << "采样数:" << rx->sample_count; if (rx->sample_count <= 0) return; // 确保有足够内存 if (rx->demodulated == nullptr) { rx->demodulated = new float[MAX_SAMPLES](); } // 根据调制类型选择解调方法 switch (rx->mod_type) { case BPSK: demodulate_bpsk(rx); break; case QPSK: demodulate_qpsk(rx); break; case FSK: demodulate_fsk(rx); break; case AM: demodulate_am(rx); break; } } // ================= 解码函数 ================= void decode_signal(Receiver* rx) { if (rx->sample_count == 0) return; if (rx->signal_type != TEXT_DATA) return; // 计算比特数 rx->bit_count = rx->sample_count / SAMPLES_PER_BIT; if (rx->bit_count > MAX_BITS) rx->bit_count = MAX_BITS; // 寻找帧头进行同步 (假设帧头为01010101) const QByteArray frame_header = QByteArray::fromHex("55"); // 01010101 //const int header_length = frame_header.size() * 8; int sync_offset = find_frame_header(rx->demodulated, rx->sample_count, frame_header); if (sync_offset < 0) { qWarning() << "帧同步失败,无法找到帧头"; return; } qDebug() << "帧同步成功,偏移量:" << sync_offset << "样本"; // 从同步点开始解码 int start_index = sync_offset + 8 * SAMPLES_PER_BIT; int decoded_bits = 0; // 使用自适应阈值解码 for (int i = 0; i < rx->bit_count; i++) { int bit_start = static_cast<int>(start_index + i * SAMPLES_PER_BIT); if (bit_start >= rx->sample_count) break; // int mid_point = bit_start + SAMPLES_PER_BIT / 2; // if (mid_point >= rx->sample_count) continue; // 计算当前比特周期的平均值作为阈值 float sum = 0.0f; //int valid_samples = 0; for (int j = 0; j < SAMPLES_PER_BIT; j++) { if (bit_start + j < rx->sample_count) { sum += rx->demodulated[bit_start + j]; //valid_samples++; } } // if (valid_samples > 0) { float threshold = sum / SAMPLES_PER_BIT; // 取中间点作为判决点 int mid_point = bit_start + SAMPLES_PER_BIT / 2; float mid_value = rx->demodulated[mid_point]; rx->binary_data[i] = (mid_value > threshold) ? 1 : 0; decoded_bits++; } rx->bit_count = decoded_bits; // 汉明解码 int decoded_bit_count = decode_hamming(rx->binary_data, rx->bit_count); if (decoded_bit_count <= 0) { qWarning() << "汉明解码失败,无法纠正错误"; return; } // 转换为文本 QByteArray byteArray; int byte_count = decoded_bit_count / 8; // 跳过帧头(1字节)和帧尾(1字节) int start_byte = 1; int end_byte = byte_count - 1; for (int i = 0; i < byte_count; i++) { uint8_t byte = 0; for (int j = 0; j < 8; j++) { int bit_index = i * 8 + j; if (bit_index < decoded_bit_count) { byte = (byte << 1) | (rx->binary_data[bit_index] ? 1 : 0); } } byteArray.append(byte); } QString decodedText = QString::fromUtf8(byteArray); strncpy(rx->decoded_text, decodedText.toUtf8().constData(), MAX_TEXT_LENGTH - 1); rx->decoded_text[MAX_TEXT_LENGTH - 1] = '\0'; qDebug() << "解码成功,文本:" << decodedText; } // 添加帧头搜索函数 int find_frame_header(const float* data, int length, const QByteArray& header) { const int header_bits = header.size() * 8; const int search_range = length - header_bits * SAMPLES_PER_BIT; if (search_range <= 0) return -1; // 计算帧头的理想比特值 QVector<int> header_bits_vector(header_bits); for (int i = 0; i < header.size(); i++) { for (int j = 0; j < 8; j++) { header_bits_vector[i * 8 + j] = (header[i] >> (7 - j)) & 1; } } // 在数据中搜索最佳匹配位置 int best_offset = -1; float best_correlation = -FLT_MAX; for (int offset = 0; offset < search_range; offset += SAMPLES_PER_BIT / 4) { float correlation = 0.0f; for (int i = 0; i < header_bits; i++) { int bit_start = offset + i * SAMPLES_PER_BIT; float bit_value = 0.0f; if (bit_start + SAMPLES_PER_BIT >= length) break; // 计算该比特周期的平均值 for (int j = 0; j < SAMPLES_PER_BIT; j++) { //if (bit_start + j < length) { bit_value += data[bit_start + j]; // } } bit_value /= SAMPLES_PER_BIT; // 计算相关性(与理想值比较) float expected = header_bits_vector[i] ? 1.0f : -1.0f; correlation += bit_value * expected; } // 找到最佳匹配 if (correlation > best_correlation) { best_correlation = correlation; best_offset = offset; } } // 如果相关性高于阈值,返回最佳偏移 if (best_correlation > header_bits * 0.7f) { return best_offset; } return -1; // 未找到匹配 } // ================= 汉明编解码 ================= void encode_hamming(uint8_t* data, int& bit_count) { int original_count = bit_count; int hamming_count = (original_count * 7) / 4; if (hamming_count > MAX_BITS) hamming_count = MAX_BITS; uint8_t* hamming_data = new uint8_t[MAX_BITS](); int h_index = 0; for (int i = 0; i < original_count; i += 4) { if (h_index + 7 >= MAX_BITS) break; if (i + 3 >= original_count) break; uint8_t d1 = data[i]; uint8_t d2 = data[i + 1]; uint8_t d3 = data[i + 2]; uint8_t d4 = data[i + 3]; // 确保数据位是0或1 // d1 = data[i] ? 1 : 0; // d2 = data[i + 1] ? 1 : 0; // d3 = data[i + 2] ? 1 : 0; // d4 = data[i + 3] ? 1 : 0; // 计算校验位 uint8_t p1 = d1 ^ d2 ^ d4; uint8_t p2 = d1 ^ d3 ^ d4; uint8_t p3 = d2 ^ d3 ^ d4; hamming_data[h_index++] = p1; hamming_data[h_index++] = p2; hamming_data[h_index++] = d1; hamming_data[h_index++] = p3; hamming_data[h_index++] = d2; hamming_data[h_index++] = d3; hamming_data[h_index++] = d4; } // 复制回原数组 memcpy(data, hamming_data, h_index); bit_count = h_index; delete[] hamming_data; } int decode_hamming(uint8_t* data, int bit_count) { int decoded_count = 0; uint8_t* decoded_data = new uint8_t[MAX_BITS](); for (int i = 0; i < bit_count; i += 7) { if (i + 6 >= bit_count) break; if (decoded_count + 4 >= MAX_BITS) break; uint8_t p1 = data[i]; uint8_t p2 = data[i + 1]; uint8_t d1 = data[i + 2]; uint8_t p3 = data[i + 3]; uint8_t d2 = data[i + 4]; uint8_t d3 = data[i + 5]; uint8_t d4 = data[i + 6]; // 确保数据位是0或1 /* p1 = data[i] ? 1 : 0; p2 = data[i + 1] ? 1 : 0; d1 = data[i + 2] ? 1 : 0; p3 = data[i + 3] ? 1 : 0; d2 = data[i + 4] ? 1 : 0; d3 = data[i + 5] ? 1 : 0; d4 = data[i + 6] ? 1 : 0;*/ // 计算校验子 uint8_t s1 = p1 ^ d1 ^ d2 ^ d4; uint8_t s2 = p2 ^ d1 ^ d3 ^ d4; uint8_t s3 = p3 ^ d2 ^ d3 ^ d4; int error_pos = s1 + (s2 << 1) + (s3 << 2); // 错误纠正 if (error_pos > 0) { switch (error_pos) { case 1: p1 ^= 1; break; case 2: p2 ^= 1; break; case 3: d1 ^= 1; break; case 4: p3 ^= 1; break; case 5: d2 ^= 1; break; case 6: d3 ^= 1; break; case 7: d4 ^= 1; break; } } // 提取数据位 decoded_data[decoded_count++] = d1; decoded_data[decoded_count++] = d2; decoded_data[decoded_count++] = d3; decoded_data[decoded_count++] = d4; } // 复制回原数组 memcpy(data, decoded_data, decoded_count); delete[] decoded_data; return decoded_count; } // ================= 文件操作 ================= void save_signal_to_file(const char* filename, const float* data, int count, int sample_rate, SignalType sig_type, ModulationType mod_type) { QFile file(filename); if (!file.open(QIODevice::WriteOnly)) { qWarning() << "无法打开文件进行写入:" << filename; return; } QDataStream out(&file); out.setVersion(QDataStream::Qt_5_9); // 写入文件头 out << static_cast<qint32>(sample_rate); out << static_cast<qint32>(sig_type); out << static_cast<qint32>(mod_type); out << static_cast<qint32>(count); // 写入数据 for (int i = 0; i < count; i++) { out << static_cast<float>(data[i]); } file.close(); } void load_signal_from_file(const char* filename, float* data, int* count, int* sample_rate, SignalType* sig_type, ModulationType* mod_type) { QFile file(filename); if (!file.open(QIODevice::ReadOnly)) { qWarning() << "无法打开文件进行读取:" << filename; return; } QDataStream in(&file); in.setVersion(QDataStream::Qt_5_9); // 读取文件头 qint32 sr, st, mt, c; in >> sr; in >> st; in >> mt; in >> c; *sample_rate = sr; *sig_type = static_cast<SignalType>(st); *mod_type = static_cast<ModulationType>(mt); *count = (c > MAX_SAMPLES) ? MAX_SAMPLES : c; // 读取数据 for (int i = 0; i < *count; i++) { float value; in >> value; data[i] = value; } file.close(); } // ================= 噪声处理 ================= void apply_noise(float* signal, int count, float snr_db) { // 计算信号功率 float signal_power = 0.0f; for (int i = 0; i < count; i++) { signal_power += signal[i] * signal[i]; } signal_power /= static_cast<float>(count); // 计算噪声功率 float snr_linear = powf(10.0f, snr_db / 10.0f); float noise_power = signal_power / snr_linear; float noise_stddev = sqrtf(noise_power); // 生成高斯噪声 std::random_device rd; std::mt19937 gen(rd()); std::normal_distribution<float> dist(0.0f, noise_stddev); // 添加噪声 for (int i = 0; i < count; i++) { signal[i] += dist(gen); } } // ================= GUI实现 ================= CommunicationSystemGUI::CommunicationSystemGUI(QWidget* parent) : QMainWindow(parent), sourceWaveformView(new QChartView(this)), sourceSpectrumView(new QChartView(this)), modulatedWaveformView(new QChartView(this)), modulatedSpectrumView(new QChartView(this)), receivedWaveformView(new QChartView(this)), receivedSpectrumView(new QChartView(this)), demodulatedWaveformView(new QChartView(this)), audioInput(nullptr), audioOutput(nullptr) { // 初始化窗口 setWindowTitle("数字通信系统"); resize(1200, 800); // 创建主布局 QWidget* centralWidget = new QWidget(this); QVBoxLayout* mainLayout = new QVBoxLayout(centralWidget); setCentralWidget(centralWidget); // 创建分割器 QSplitter* mainSplitter = new QSplitter(Qt::Vertical, centralWidget); mainLayout->addWidget(mainSplitter); // 创建控制面板 QWidget* controlWidget = new QWidget(); QHBoxLayout* controlLayout = new QHBoxLayout(controlWidget); // 创建发送方控制组 createTransmitterGroup(); controlLayout->addWidget(transmitterGroup); // 创建接收方控制组 createReceiverGroup(); controlLayout->addWidget(receiverGroup); mainSplitter->addWidget(controlWidget); // 创建波形显示区域 createWaveformDisplays(); mainSplitter->addWidget(waveformFrame); // 设置分割器比例 mainSplitter->setSizes({ 200, 600 }); // 创建状态栏 statusBar = new QStatusBar(); setStatusBar(statusBar); // 创建状态栏组件 transmitStatusLabel = new QLabel("发送状态: 空闲"); receiveStatusLabel = new QLabel("接收状态: 空闲"); transmitProgressBar = new QProgressBar(); transmitProgressBar->setRange(0, 100); transmitProgressBar->setFixedWidth(150); transmitProgressBar->setTextVisible(true); receiveProgressBar = new QProgressBar(); receiveProgressBar->setRange(0, 100); receiveProgressBar->setFixedWidth(150); receiveProgressBar->setTextVisible(true); // 添加到状态栏 statusBar->addPermanentWidget(transmitStatusLabel); statusBar->addPermanentWidget(transmitProgressBar); statusBar->addPermanentWidget(receiveStatusLabel); statusBar->addPermanentWidget(receiveProgressBar); // 确保接收器内存分配正确 receiver.received = new float[MAX_SAMPLES](); receiver.demodulated = new float[MAX_SAMPLES](); // 初始化通信模块 init_transmitter(&transmitter, SINE_WAVE, BPSK, CARRIER_FREQ, SAMPLE_RATE); init_receiver(&receiver, CARRIER_FREQ, SAMPLE_RATE); // 初始化音频设备 initAudio(); } CommunicationSystemGUI::~CommunicationSystemGUI() { delete[] transmitter.samples; delete[] transmitter.modulated; delete[] receiver.received; delete[] receiver.demodulated; delete audioInput; delete audioOutput; } void CommunicationSystemGUI::createTransmitterGroup() { transmitterGroup = new QGroupBox("发送方"); QGridLayout* layout = new QGridLayout(transmitterGroup); // 信号类型 layout->addWidget(new QLabel("信号类型:"), 0, 0); signalTypeCombo = new QComboBox(); signalTypeCombo->addItem("正弦波", SINE_WAVE); signalTypeCombo->addItem("方波", SQUARE_WAVE); signalTypeCombo->addItem("锯齿波", SAWTOOTH_WAVE); signalTypeCombo->addItem("随机数据", RANDOM_DATA); signalTypeCombo->addItem("文本数据", TEXT_DATA); signalTypeCombo->addItem("文件数据", FILE_DATA); layout->addWidget(signalTypeCombo, 0, 1); // 调制类型 layout->addWidget(new QLabel("调制类型:"), 1, 0); modulationTypeCombo = new QComboBox(); modulationTypeCombo->addItem("BPSK", BPSK); modulationTypeCombo->addItem("QPSK", QPSK); modulationTypeCombo->addItem("FSK", FSK); modulationTypeCombo->addItem("AM", AM); layout->addWidget(modulationTypeCombo, 1, 1); // 载波频率 layout->addWidget(new QLabel("载波频率(Hz):"), 2, 0); carrierFreqEdit = new QLineEdit(QString::number(CARRIER_FREQ)); carrierFreqEdit->setValidator(new QDoubleValidator(100, 10000, 2, this)); layout->addWidget(carrierFreqEdit, 2, 1); // 文本输入 layout->addWidget(new QLabel("文本数据:"), 3, 0, 1, 2); textDataEdit = new QTextEdit(); textDataEdit->setMaximumHeight(60); layout->addWidget(textDataEdit, 4, 0, 1, 2); // 按钮 setTextButton = new QPushButton("设置文本"); loadFileButton = new QPushButton("加载文件"); generateButton = new QPushButton("生成信号"); modulateButton = new QPushButton("调制"); transmitButton = new QPushButton("传输"); saveSignalButton = new QPushButton("保存信号"); layout->addWidget(setTextButton, 5, 0); layout->addWidget(loadFileButton, 5, 1); layout->addWidget(generateButton, 6, 0); layout->addWidget(modulateButton, 6, 1); layout->addWidget(transmitButton, 7, 0); layout->addWidget(saveSignalButton, 7, 1); // 连接信号槽 connect(signalTypeCombo, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &CommunicationSystemGUI::onSignalTypeChanged); connect(setTextButton, &QPushButton::clicked, this, &CommunicationSystemGUI::onSetTextData); connect(loadFileButton, &QPushButton::clicked, this, &CommunicationSystemGUI::onLoadFileData); connect(generateButton, &QPushButton::clicked, this, &CommunicationSystemGUI::onGenerateSignal); connect(modulateButton, &QPushButton::clicked, this, &CommunicationSystemGUI::onModulateSignal); connect(transmitButton, &QPushButton::clicked, this, &CommunicationSystemGUI::onTransmitSignal); connect(saveSignalButton, &QPushButton::clicked, this, &CommunicationSystemGUI::onSaveSignal); } void CommunicationSystemGUI::createReceiverGroup() { receiverGroup = new QGroupBox("接收方"); QGridLayout* layout = new QGridLayout(receiverGroup); // 按钮 loadSignalButton = new QPushButton("加载信号"); receiveButton = new QPushButton("接收"); demodulateButton = new QPushButton("解调"); decodeButton = new QPushButton("解码"); saveReceivedButton = new QPushButton("保存接收"); saveDecodedButton = new QPushButton("保存解码"); layout->addWidget(loadSignalButton, 0, 0); layout->addWidget(receiveButton, 0, 1); layout->addWidget(demodulateButton, 1, 0); layout->addWidget(decodeButton, 1, 1); layout->addWidget(saveReceivedButton, 2, 0); layout->addWidget(saveDecodedButton, 2, 1); // 解码文本显示 decodedTextEdit = new QTextEdit(); decodedTextEdit->setReadOnly(true); decodedTextEdit->setMaximumHeight(80); layout->addWidget(new QLabel("解码文本:"), 3, 0, 1, 2); layout->addWidget(decodedTextEdit, 4, 0, 1, 2); // 连接信号槽 connect(loadSignalButton, &QPushButton::clicked, this, &CommunicationSystemGUI::onLoadSignal); connect(receiveButton, &QPushButton::clicked, this, &CommunicationSystemGUI::onReceiveSignal); connect(demodulateButton, &QPushButton::clicked, this, &CommunicationSystemGUI::onDemodulateSignal); connect(decodeButton, &QPushButton::clicked, this, &CommunicationSystemGUI::onDecodeSignal); connect(saveReceivedButton, &QPushButton::clicked, this, &CommunicationSystemGUI::onSaveReceived); connect(saveDecodedButton, &QPushButton::clicked, this, &CommunicationSystemGUI::onSaveDecoded); } void CommunicationSystemGUI::createWaveformDisplays() { waveformFrame = new QFrame(); QGridLayout* gridLayout = new QGridLayout(waveformFrame); // 第一行:源信号波形(左)和调制信号波形(右) sourceWaveformView = new QChartView(); modulatedWaveformView = new QChartView(); gridLayout->addWidget(new QLabel("源信号波形"), 0, 0); gridLayout->addWidget(sourceWaveformView, 1, 0); gridLayout->addWidget(new QLabel("调制信号波形"), 0, 1); gridLayout->addWidget(modulatedWaveformView, 1, 1); // 第二行:接收信号波形(左)和解调信号波形(右) receivedWaveformView = new QChartView(); demodulatedWaveformView = new QChartView(); gridLayout->addWidget(new QLabel("接收信号波形"), 2, 0); gridLayout->addWidget(receivedWaveformView, 3, 0); gridLayout->addWidget(new QLabel("解调信号波形"), 2, 1); gridLayout->addWidget(demodulatedWaveformView, 3, 1); // 设置行和列的比例 gridLayout->setRowStretch(1, 1); gridLayout->setRowStretch(3, 1); gridLayout->setColumnStretch(0, 1); gridLayout->setColumnStretch(1, 1); } void CommunicationSystemGUI::initAudio() { QAudioFormat format; format.setSampleRate(SAMPLE_RATE); format.setChannelCount(1); format.setSampleSize(32); format.setCodec("audio/pcm"); format.setByteOrder(QAudioFormat::LittleEndian); format.setSampleType(QAudioFormat::Float); // 输入设备 QAudioDeviceInfo inputDevice = QAudioDeviceInfo::defaultInputDevice(); if (!inputDevice.isFormatSupported(format)) { qWarning() << "输入设备不支持请求的格式,使用最接近的匹配"; format = inputDevice.nearestFormat(format); } if (!inputDevice.isFormatSupported(format)) { qWarning() << "默认格式不支持,使用最接近的格式"; format = inputDevice.nearestFormat(format); } audioInput = new QAudioInput(inputDevice, format, this); // 输出设备 QAudioDeviceInfo outputDevice = QAudioDeviceInfo::defaultOutputDevice(); if (outputDevice.isNull()) { qWarning() << "未找到音频输出设备"; return; } if (!outputDevice.isFormatSupported(format)) { qWarning() << "默认格式不支持,使用最接近的格式"; format = outputDevice.nearestFormat(format); } audioOutput = new QAudioOutput(outputDevice, format, this); } // ================= 核心功能实现 ================= void CommunicationSystemGUI::onGenerateSignal() { transmitter.signal_type = static_cast<SignalType>(signalTypeCombo->currentData().toInt()); transmitter.mod_type = static_cast<ModulationType>(modulationTypeCombo->currentData().toInt()); transmitter.carrier_freq = carrierFreqEdit->text().toFloat(); if (transmitter.signal_type == TEXT_DATA) { onSetTextData(); } else if (transmitter.signal_type == FILE_DATA) { onLoadFileData(); } else { generate_signal(&transmitter, 1000); // 1秒信号 plotSignal(sourceWaveformView, transmitter.samples, transmitter.sample_count, "源信号波形"); } showStatusMessage("信号生成完成"); } void CommunicationSystemGUI::onModulateSignal() { if (transmitter.sample_count == 0) { QMessageBox::warning(this, "错误", "请先生成信号"); return; } modulate_signal(&transmitter); plotSignal(modulatedWaveformView, transmitter.modulated, transmitter.sample_count, "调制信号波形"); showStatusMessage("信号调制完成"); } void CommunicationSystemGUI::onSetTextData() { QString text = textDataEdit->toPlainText(); if (text.isEmpty()) { QMessageBox::warning(this, "错误", "请输入文本"); return; } // 使用QString确保正确处理中文 QByteArray utf8Data = text.toUtf8(); set_text_data(&transmitter, utf8Data.constData()); set_text_data(&transmitter, text.toUtf8().constData()); plotSignal(sourceWaveformView, transmitter.samples, transmitter.sample_count, "文本信号波形"); showStatusMessage("文本数据已设置"); } void CommunicationSystemGUI::onLoadFileData() { QString filename = QFileDialog::getOpenFileName(this, "打开数据文件"); if (!filename.isEmpty()) { load_file_data(&transmitter, filename.toUtf8().constData()); plotSignal(sourceWaveformView, transmitter.samples, transmitter.sample_count, "文件数据波形"); showStatusMessage("文件数据已加载"); } } void CommunicationSystemGUI::onDemodulateSignal() { if (receiver.sample_count == 0) { QMessageBox::warning(this, "错误", "没有可解调的信号"); return; } // 传递发送方的信号类型和调制类型 receiver.signal_type = transmitter.signal_type; receiver.mod_type = transmitter.mod_type; demodulate_signal(&receiver); plotSignal(demodulatedWaveformView, receiver.demodulated, receiver.sample_count, "解调信号波形"); // 如果是文本信号,自动解码 if (receiver.signal_type == TEXT_DATA) { onDecodeSignal(); } showStatusMessage("信号解调完成"); } void CommunicationSystemGUI::onDecodeSignal() { if (receiver.sample_count == 0) { QMessageBox::warning(this, "错误", "没有可解码的信号"); return; } decode_signal(&receiver); if (strlen(receiver.decoded_text) > 0) { decodedTextEdit->setPlainText(receiver.decoded_text); showStatusMessage("解码成功"); } else { decodedTextEdit->setPlainText("解码失败:请检查调制设置和信号质量"); showStatusMessage("解码失败"); } } void CommunicationSystemGUI::onSaveSignal() { if (transmitter.sample_count == 0) { QMessageBox::warning(this, "错误", "没有可保存的信号"); return; } QString filename = QFileDialog::getSaveFileName(this, "保存信号", "", "信号文件 (*.sig)"); if (!filename.isEmpty()) { save_signal_to_file(filename.toUtf8().constData(), transmitter.modulated, transmitter.sample_count, transmitter.sample_rate, transmitter.signal_type, transmitter.mod_type); showStatusMessage("信号已保存: " + filename); } } void CommunicationSystemGUI::onLoadSignal() { QString filename = QFileDialog::getOpenFileName(this, "加载信号", "", "信号文件 (*.sig)"); if (!filename.isEmpty()) { load_signal_from_file(filename.toUtf8().constData(), receiver.received, &receiver.sample_count, &receiver.sample_rate, &receiver.signal_type, &receiver.mod_type); plotSignal(receivedWaveformView, receiver.received, receiver.sample_count, "接收信号波形"); showStatusMessage("信号已加载: " + filename); } } void CommunicationSystemGUI::onSaveReceived() { if (receiver.sample_count == 0) { QMessageBox::warning(this, "错误", "没有可保存的接收信号"); return; } QString filename = QFileDialog::getSaveFileName(this, "保存接收信号", "", "信号文件 (*.sig)"); if (!filename.isEmpty()) { save_signal_to_file(filename.toUtf8().constData(), receiver.received, receiver.sample_count, receiver.sample_rate, receiver.signal_type, receiver.mod_type); showStatusMessage("接收信号已保存: " + filename); } } void CommunicationSystemGUI::onSaveDecoded() { if (receiver.signal_type == TEXT_DATA) { QString filename = QFileDialog::getSaveFileName(this, "保存解码文本", "", "文本文件 (*.txt)"); if (!filename.isEmpty()) { QFile file(filename); if (file.open(QIODevice::WriteOnly | QIODevice::Text)) { QTextStream out(&file); out << receiver.decoded_text; file.close(); showStatusMessage("解码文本已保存: " + filename); } } } else { QString filename = QFileDialog::getSaveFileName(this, "保存解码信号", "", "数据文件 (*.dat)"); if (!filename.isEmpty()) { QFile file(filename); if (file.open(QIODevice::WriteOnly | QIODevice::Text)) { QTextStream out(&file); for (int i = 0; i < receiver.sample_count; i++) { out << receiver.demodulated[i] << "\n"; } file.close(); showStatusMessage("解码信号已保存: " + filename); } } } } void CommunicationSystemGUI::onSignalTypeChanged(int index) { SignalType type = static_cast<SignalType>(signalTypeCombo->itemData(index).toInt()); textDataEdit->setEnabled(type == TEXT_DATA); setTextButton->setEnabled(type == TEXT_DATA); loadFileButton->setEnabled(type == FILE_DATA); } void CommunicationSystemGUI::onTransmitSignal() { if (audioOutput && transmitter.sample_count > 0) { // 更新状态 transmitStatusLabel->setText("发送状态: 传输中"); transmitProgressBar->setValue(0); // 准备输出缓冲区 QBuffer* outputBuffer = new QBuffer(this); outputBuffer->open(QIODevice::ReadWrite); // 计算传输时间(毫秒) float duration = (transmitter.sample_count * 1000.0f) / transmitter.sample_rate; outputBuffer->write(reinterpret_cast<const char*>(transmitter.modulated), transmitter.sample_count * sizeof(float)); outputBuffer->seek(0); // 开始播放 audioOutput->start(outputBuffer); showStatusMessage("开始传输信号..."); // 启动传输计时器 transmitTimer.start(); // 设置进度更新定时器 QTimer* progressTimer = new QTimer(this); connect(progressTimer, &QTimer::timeout, [=]() { int elapsed = transmitTimer.elapsed(); int progress = qMin(100, static_cast<int>((elapsed / duration) * 100)); transmitProgressBar->setValue(progress); if (progress >= 100) { progressTimer->stop(); progressTimer->deleteLater(); transmitStatusLabel->setText("发送状态: 完成"); showStatusMessage("信号传输完成"); } }); progressTimer->start(100); // 每100ms更新一次 } else { QMessageBox::warning(this, "错误", "音频输出设备未初始化或没有可传输的信号"); } } void CommunicationSystemGUI::onReceiveSignal() { if (audioInput) { // 更新状态 receiveStatusLabel->setText("接收状态: 接收中"); receiveProgressBar->setValue(0); // 准备输入缓冲区 inputBuffer.setData(QByteArray()); inputBuffer.open(QIODevice::WriteOnly); // 开始录音 audioInput->start(&inputBuffer); showStatusMessage("开始接收信号..."); // 设置进度更新定时器 QTimer* progressTimer = new QTimer(this); connect(progressTimer, &QTimer::timeout, [=]() { int elapsed = receiveTimer.elapsed(); int progress = qMin(100, static_cast<int>((elapsed / 5000.0) * 100)); receiveProgressBar->setValue(progress); if (progress >= 100) { progressTimer->stop(); progressTimer->deleteLater(); receiveStatusLabel->setText("接收状态: 完成"); // === 新增:处理接收到的音频数据 === inputBuffer.close(); QByteArray audioData = inputBuffer.data(); if (!audioData.isEmpty()) { // 转换为浮点数组 const float* rawData = reinterpret_cast<const float*>(audioData.constData()); int numSamples = audioData.size() / sizeof(float); // 设置接收器的信号类型和调制类型 receiver.signal_type = transmitter.signal_type; receiver.mod_type = transmitter.mod_type; receiver.sample_rate = transmitter.sample_rate; // 存储到接收器 receiver.sample_count = numSamples; for (int i = 0; i < receiver.sample_count; i++) { receiver.received[i] = rawData[i]; } // 绘制接收信号 plotSignal(receivedWaveformView, receiver.received, receiver.sample_count, "接收信号波形"); showStatusMessage(QString("接收完成,共 %1 个样本").arg(receiver.sample_count)); onDemodulateSignal(); } else { showStatusMessage("未接收到任何数据"); } } }); progressTimer->start(100); receiveTimer.start(); } else { QMessageBox::warning(this, "错误", "音频输入设备未初始化"); } } // 添加处理接收到的音频数据的函数 void CommunicationSystemGUI::processReceivedAudio() { inputBuffer.close(); if (audioData.size() == 0) { showStatusMessage("未接收到任何数据"); return; } // 将接收到的数据转换为浮点数组 const float* rawData = reinterpret_cast<const float*>(audioData.constData()); int numSamples = audioData.size() / sizeof(float); if (numSamples > MAX_SAMPLES) { numSamples = MAX_SAMPLES; showStatusMessage("警告:接收数据超出最大样本数,已截断"); } // 复制到接收器 receiver.sample_count = numSamples; receiver.signal_type = transmitter.signal_type; // 设置信号类型 receiver.mod_type = transmitter.mod_type; // 设置调制类型 for (int i = 0; i < numSamples; i++) { receiver.received[i] = rawData[i]; } // 绘制接收信号 plotSignal(receivedWaveformView, receiver.received, receiver.sample_count, "接收信号波形"); showStatusMessage(QString("接收完成,共 %1 个样本").arg(numSamples)); // 清空数据为下次接收准备 audioData.clear(); } // ================= 辅助函数 ================= void CommunicationSystemGUI::plotSignal(QChartView* chartView, float* data, int count, const QString& title) { // 严格检查输入参数 if (!chartView || !data || count <= 0) { qWarning() << "无效的绘图参数"; return; } // 创建新的series QLineSeries* series = new QLineSeries(); // 计算步长以减少点数 int step = std::max(1, count / 1000); if (step < 1) step = 1; // 添加数据点 for (int i = 0; i < count; i += step) { series->append(i, data[i]); } // 创建新图表 QChart* chart = new QChart(); chart->addSeries(series); chart->setTitle(title); // 创建并设置X轴 QValueAxis* axisX = new QValueAxis(); axisX->setTitleText("采样点"); axisX->setRange(0, count); chart->addAxis(axisX, Qt::AlignBottom); series->attachAxis(axisX); // 创建并设置Y轴 QValueAxis* axisY = new QValueAxis(); axisY->setTitleText("幅度"); // 自动计算Y轴范围 auto minmax = std::minmax_element(data, data + count); float minVal = *minmax.first; float maxVal = *minmax.second; // 确保有合理的范围 if (fabs(maxVal - minVal) < 0.001f) { minVal -= 1.0f; maxVal += 1.0f; } else { // 扩展10%的范围 float range = maxVal - minVal; minVal -= range * 0.1f; maxVal += range * 0.1f; } axisY->setRange(minVal, maxVal); chart->addAxis(axisY, Qt::AlignLeft); series->attachAxis(axisY); // 设置图表到视图 chartView->setChart(chart); chartView->setRenderHint(QPainter::Antialiasing); } void CommunicationSystemGUI::showStatusMessage(const QString& message) { statusBar->showMessage(message, 5000); // 显示5秒 }1.修改修改下QPSK,FSK,AM调制代码,因为调制出来的波形和源信号波形相差太远。2.如果发送的是文本数据,那么还是同样的点击调制方式接收端点击解调吗?如果是这样那么解码的意义在哪里?如果发送端不是点击调制方式,接收端点击解码方式,那么发送端是不是要有一个按键进行编码而不是只有调制波形?

import os import numpy as np import matplotlib.pyplot as plt from PIL import Image import torch import torch.nn as nn import torch.optim as optim from torch.utils.data import DataLoader, Dataset from torchvision import transforms, models from torchvision.datasets import ImageFolder import torch.nn.functional as F from sklearn.metrics import classification_report, confusion_matrix, accuracy_score import seaborn as sns import gradio as gr import warnings # 设置中文字体 plt.rcParams['font.sans-serif'] = ['Simhei'] plt.rcParams['axes.unicode_minus'] = False warnings.filterwarnings("ignore") # 设置随机种子以确保结果可复现 torch.manual_seed(42) np.random.seed(42) # 数据路径设置(使用原始字符串避免转义问题) data_dir = r"D:\桌面\课设\case_1\photo" train_dir = os.path.join(data_dir, "train") val_dir = os.path.join(data_dir, "validation") model_save_path = "plant_disease_model.pth" # 检查是否有GPU可用 device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu") print(f"使用设备: {device}") # 数据集探索函数(增强版) def explore_dataset(): train_dataset = ImageFolder(train_dir) val_dataset = ImageFolder(val_dir) print(f"训练集样本数量: {len(train_dataset)}") print(f"验证集样本数量: {len(val_dataset)}") class_names = train_dataset.classes print(f"类别名称: {class_names}") train_class_counts = {class_names[i]: 0 for i in range(len(class_names))} val_class_counts = {class_names[i]: 0 for i in range(len(class_names))} for img, label in train_dataset: train_class_counts[class_names[label]] += 1 for img, label in val_dataset: val_class_counts[class_names[label]] += 1 print("训练集类别分布:") for class_name, count in train_class_counts.items(): print(f"{class_name}: {count}") print("验证集类别分布:") for class_name, count in val_class_counts.items(): print(f"{class_name}: {count}") # 可视化数据集样本 visualize_dataset_samples(train_dataset, class_names, num_samples=4) # 可视化类别分布 visualize_class_distribution(train_class_counts, val_class_counts, class_names) return class_names, train_class_counts, val_class_counts # 新增:数据集样本可视化函数 def visualize_dataset_samples(dataset, class_names, num_samples=4): """可视化每个类别的样本图片""" plt.figure(figsize=(4 * num_samples, 4 * len(class_names))) for class_idx, class_name in enumerate(class_names): # 获取该类别的所有样本索引 class_indices = [i for i, (_, label) in enumerate(dataset) if label == class_idx] # 随机选择样本 selected_indices = np.random.choice(class_indices, min(num_samples, len(class_indices)), replace=False) for i, sample_idx in enumerate(selected_indices): img, _ = dataset[sample_idx] plt.subplot(len(class_names), num_samples, class_idx * num_samples + i + 1) plt.imshow(img) plt.title(f"{class_name} 样本 {i + 1}") plt.axis('off') plt.tight_layout() plt.savefig('dataset_samples.png', dpi=300, bbox_inches='tight') plt.close() print("已生成数据集样本可视化: dataset_samples.png") # 新增:类别分布可视化函数 def visualize_class_distribution(train_counts, val_counts, class_names): """可视化训练集和验证集的类别分布""" plt.figure(figsize=(12, 5)) # 训练集分布 plt.subplot(1, 2, 1) bars = plt.bar(class_names, [train_counts[cls] for cls in class_names], width=0.4) # 修改柱子宽度 plt.title('训练集类别分布') plt.xlabel('类别') plt.ylabel('样本数量') plt.xticks(rotation=45) # 添加数值标签 for bar in bars: height = bar.get_height() plt.text(bar.get_x() + bar.get_width() / 2., height + 5, f'{int(height)}', ha='center', va='bottom') # 验证集分布 plt.subplot(1, 2, 2) bars = plt.bar(class_names, [val_counts[cls] for cls in class_names], width=0.4) # 修改柱子宽度 plt.title('验证集类别分布') plt.xlabel('类别') plt.ylabel('样本数量') plt.xticks(rotation=45) # 添加数值标签 for bar in bars: height = bar.get_height() plt.text(bar.get_x() + bar.get_width() / 2., height + 2, f'{int(height)}', ha='center', va='bottom') plt.tight_layout() plt.savefig('class_distribution.png', dpi=300, bbox_inches='tight') plt.close() print("已生成类别分布可视化: class_distribution.png") # 数据预处理和增强 def get_data_loaders(batch_size=32): # 定义数据增强和预处理转换 train_transforms = transforms.Compose([ transforms.RandomResizedCrop(224), transforms.RandomHorizontalFlip(), transforms.RandomRotation(15), transforms.ColorJitter(brightness=0.2, contrast=0.2, saturation=0.2), transforms.ToTensor(), transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]) ]) val_transforms = transforms.Compose([ transforms.Resize(256), transforms.CenterCrop(224), transforms.ToTensor(), transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]) ]) # 加载数据集 train_dataset = ImageFolder(train_dir, transform=train_transforms) val_dataset = ImageFolder(val_dir, transform=val_transforms) # 创建数据加载器 train_loader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True, num_workers=4) val_loader = DataLoader(val_dataset, batch_size=batch_size, shuffle=False, num_workers=4) return train_loader, val_loader, train_dataset.classes # 构建模型 - 使用ResNet-18进行迁移学习 def build_model(num_classes=2): # 加载预训练的ResNet-18模型 model = models.resnet18(pretrained=True) # 冻结所有卷积层的参数 for param in model.parameters(): param.requires_grad = False # 修改最后的全连接层以适应我们的分类任务 model.fc = nn.Sequential( nn.Linear(model.fc.in_features, 256), nn.ReLU(), nn.Dropout(0.5), nn.Linear(256, num_classes) ) return model.to(device) # 训练模型(增强版) def train_model(model, train_loader, val_loader, num_epochs=10): criterion = nn.CrossEntropyLoss() optimizer = optim.Adam(model.fc.parameters(), lr=0.001) scheduler = optim.lr_scheduler.ReduceLROnPlateau(optimizer, 'min', patience=3) best_val_acc = 0.0 train_losses = [] val_losses = [] train_accs = [] val_accs = [] learning_rates = [] # 记录学习率变化 for epoch in range(num_epochs): # 训练阶段 model.train() train_loss = 0.0 correct = 0 total = 0 for inputs, labels in train_loader: inputs, labels = inputs.to(device), labels.to(device) optimizer.zero_grad() outputs = model(inputs) loss = criterion(outputs, labels) loss.backward() optimizer.step() train_loss += loss.item() * inputs.size(0) _, predicted = outputs.max(1) total += labels.size(0) correct += predicted.eq(labels).sum().item() epoch_train_loss = train_loss / len(train_loader.dataset) epoch_train_acc = 100.0 * correct / total train_losses.append(epoch_train_loss) train_accs.append(epoch_train_acc) # 验证阶段 model.eval() val_loss = 0.0 correct = 0 total = 0 with torch.no_grad(): for inputs, labels in val_loader: inputs, labels = inputs.to(device), labels.to(device) outputs = model(inputs) loss = criterion(outputs, labels) val_loss += loss.item() * inputs.size(0) _, predicted = outputs.max(1) total += labels.size(0) correct += predicted.eq(labels).sum().item() epoch_val_loss = val_loss / len(val_loader.dataset) epoch_val_acc = 100.0 * correct / total val_losses.append(epoch_val_loss) val_accs.append(epoch_val_acc) # 记录当前学习率 learning_rates.append(optimizer.param_groups[0]['lr']) # 学习率调整 scheduler.step(epoch_val_loss) print(f"Epoch {epoch + 1}/{num_epochs} - " f"Train Loss: {epoch_train_loss:.4f} Acc: {epoch_train_acc:.2f}% - " f"Val Loss: {epoch_val_loss:.4f} Acc: {epoch_val_acc:.2f}% - " f"LR: {learning_rates[-1]:.6f}") # 保存最佳模型 if epoch_val_acc > best_val_acc: best_val_acc = epoch_val_acc torch.save(model.state_dict(), model_save_path) print(f"保存最佳模型,验证集准确率: {best_val_acc:.2f}%") # 绘制训练过程可视化(增强版) visualize_training_process(train_losses, val_losses, train_accs, val_accs, learning_rates) return model, train_losses, val_losses, train_accs, val_accs # 新增:训练过程可视化函数 def visualize_training_process(train_losses, val_losses, train_accs, val_accs, learning_rates): """可视化训练过程中的损失、准确率和学习率变化""" fig = plt.figure(figsize=(15, 10)) # 损失曲线 plt.subplot(2, 2, 1) plt.plot(train_losses, label='训练损失', marker='o') plt.plot(val_losses, label='验证损失', marker='x') plt.xlabel('Epoch') plt.ylabel('Loss') plt.title('训练和验证损失曲线') plt.legend() plt.grid(True, linestyle='--', alpha=0.7) # 准确率曲线 plt.subplot(2, 2, 2) plt.plot(train_accs, label='训练准确率', marker='o') plt.plot(val_accs, label='验证准确率', marker='x') plt.xlabel('Epoch') plt.ylabel('准确率 (%)') plt.title('训练和验证准确率曲线') plt.legend() plt.grid(True, linestyle='--', alpha=0.7) # 学习率变化 plt.subplot(2, 2, 3) plt.plot(learning_rates, marker='o', color='red') plt.xlabel('Epoch') plt.ylabel('学习率') plt.title('学习率变化') plt.grid(True, linestyle='--', alpha=0.7) # 损失和准确率对比(归一化) plt.subplot(2, 2, 4) # 归一化损失 train_losses_norm = (np.array(train_losses) - np.min(train_losses)) / (np.max(train_losses) - np.min(train_losses)) val_losses_norm = (np.array(val_losses) - np.min(val_losses)) / (np.max(val_losses) - np.min(val_losses)) # 归一化准确率(反转以便与损失对比) train_accs_norm = 1 - (np.array(train_accs) / 100) val_accs_norm = 1 - (np.array(val_accs) / 100) plt.plot(train_losses_norm, label='训练损失(归一化)', marker='o') plt.plot(val_losses_norm, label='验证损失(归一化)', marker='x') plt.plot(train_accs_norm, label='训练准确率(归一化)', marker='^', linestyle='--') plt.plot(val_accs_norm, label='验证准确率(归一化)', marker='v', linestyle='--') plt.xlabel('Epoch') plt.title('损失与准确率对比(归一化)') plt.legend() plt.grid(True, linestyle='--', alpha=0.7) plt.tight_layout() plt.savefig('training_metrics_enhanced.png', dpi=300, bbox_inches='tight') plt.close() print("已生成增强版训练过程可视化: training_metrics_enhanced.png") # 评估模型(增强版) def evaluate_model(model, val_loader, class_names): model.eval() all_preds = [] all_labels = [] all_images = [] # 保存原始图像用于可视化 all_probs = [] # 保存预测概率用于可视化 with torch.no_grad(): for inputs, labels in val_loader: inputs = inputs.to(device) outputs = model(inputs) probs = F.softmax(outputs, dim=1) _, preds = torch.max(outputs, 1) all_preds.extend(preds.cpu().numpy()) all_labels.extend(labels.cpu().numpy()) all_probs.extend(probs.cpu().numpy()) # 还原预处理的图像(用于可视化) inv_normalize = transforms.Compose([ transforms.Normalize(mean=[-0.485 / 0.229, -0.456 / 0.224, -0.406 / 0.225], std=[1 / 0.229, 1 / 0.224, 1 / 0.225]), ]) for img in inputs: img = inv_normalize(img).cpu().permute(1, 2, 0).numpy() all_images.append(img) # 计算每个类别的准确率 class_acc = {} for i, class_name in enumerate(class_names): mask = (np.array(all_labels) == i) if np.sum(mask) > 0: class_acc[class_name] = np.mean(np.array(all_preds)[mask] == np.array(all_labels)[mask]) * 100 # 生成分类报告 report = classification_report(all_labels, all_preds, target_names=class_names) print("分类报告:\n", report) # 绘制混淆矩阵 cm = confusion_matrix(all_labels, all_preds) plt.figure(figsize=(8, 6)) sns.heatmap(cm, annot=True, fmt='d', cmap='Blues', xticklabels=class_names, yticklabels=class_names) plt.xlabel('预测标签') plt.ylabel('真实标签') plt.title('混淆矩阵') plt.tight_layout() plt.savefig('confusion_matrix.png', dpi=300, bbox_inches='tight') plt.close() print("已生成混淆矩阵: confusion_matrix.png") # 可视化预测结果(增强版) visualize_prediction_results(all_images, all_labels, all_preds, all_probs, class_names) # 可视化每个类别的准确率 visualize_class_accuracy(class_acc, class_names) return class_acc, report # 新增:预测结果可视化函数(增强版) def visualize_prediction_results(images, labels, preds, probs, class_names, num_samples=5): """可视化模型预测结果,包括正确和错误预测""" # 找出正确和错误预测的索引 correct_indices = np.where(np.array(labels) == np.array(preds))[0] wrong_indices = np.where(np.array(labels) != np.array(preds))[0] # 随机选择样本进行可视化 if len(correct_indices) > 0: selected_correct = np.random.choice(correct_indices, min(num_samples, len(correct_indices)), replace=False) else: selected_correct = [] if len(wrong_indices) > 0: selected_wrong = np.random.choice(wrong_indices, min(num_samples, len(wrong_indices)), replace=False) else: selected_wrong = [] # 可视化正确预测 if len(selected_correct) > 0: plt.figure(figsize=(4 * len(selected_correct), 8)) plt.suptitle("模型正确预测示例", fontsize=16) for i, idx in enumerate(selected_correct): # 图像 plt.subplot(2, len(selected_correct), i + 1) plt.imshow(np.clip(images[idx], 0, 1)) # 确保像素值在0-1之间 plt.title(f"真实: {class_names[labels[idx]]}\n预测: {class_names[preds[idx]]}") plt.axis('off') # 预测概率条形图 plt.subplot(2, len(selected_correct), i + 1 + len(selected_correct)) bars = plt.bar(class_names, probs[idx]) bars[preds[idx]].set_color('g') # 预测类别标绿 plt.title(f"置信度: {probs[idx][preds[idx]] * 100:.1f}%") plt.ylim(0, 1) plt.xticks(rotation=45) plt.tight_layout(rect=[0, 0, 1, 0.96]) plt.savefig('correct_predictions.png', dpi=300, bbox_inches='tight') plt.close() print("已生成正确预测可视化: correct_predictions.png") # 可视化错误预测 if len(selected_wrong) > 0: plt.figure(figsize=(4 * len(selected_wrong), 8)) plt.suptitle("模型错误预测示例", fontsize=16) for i, idx in enumerate(selected_wrong): # 图像 plt.subplot(2, len(selected_wrong), i + 1) plt.imshow(np.clip(images[idx], 0, 1)) # 确保像素值在0-1之间 plt.title(f"真实: {class_names[labels[idx]]}\n预测: {class_names[preds[idx]]}") plt.axis('off') # 预测概率条形图 plt.subplot(2, len(selected_wrong), i + 1 + len(selected_wrong)) bars = plt.bar(class_names, probs[idx]) bars[labels[idx]].set_color('r') # 真实类别标红 bars[preds[idx]].set_color('b') # 预测类别标蓝 plt.title(f"置信度: {probs[idx][preds[idx]] * 100:.1f}%") plt.ylim(0, 1) plt.xticks(rotation=45) plt.tight_layout(rect=[0, 0, 1, 0.96]) plt.savefig('wrong_predictions.png', dpi=300, bbox_inches='tight') plt.close() print("已生成错误预测可视化: wrong_predictions.png") # 新增:每个类别准确率可视化函数 def visualize_class_accuracy(class_acc, class_names): """可视化每个类别的准确率""" plt.figure(figsize=(10, 6)) bars = plt.bar(class_names, [class_acc[cls] for cls in class_names], width=0.4) # 修改柱子宽度 # 颜色编码(准确率>80%为绿色,<70%为红色,其余为蓝色) for i, bar in enumerate(bars): acc = class_acc[class_names[i]] if acc > 80: bar.set_color('g') elif acc < 70: bar.set_color('r') else: bar.set_color('b') plt.title('各类别预测准确率') plt.xlabel('类别') plt.ylabel('准确率 (%)') plt.xticks(rotation=45) plt.axhline(y=70, color='r', linestyle='--', alpha=0.7) # 添加70%阈值线 plt.axhline(y=80, color='g', linestyle='--', alpha=0.7) # 添加80%阈值线 # 添加数值标签 for bar in bars: height = bar.get_height() plt.text(bar.get_x() + bar.get_width() / 2., height + 1, f'{height:.1f}%', ha='center', va='bottom') plt.tight_layout() plt.savefig('class_accuracy.png', dpi=300, bbox_inches='tight') plt.close() print("已生成类别准确率可视化: class_accuracy.png") # 预测函数(增强版) def predict_image(model, image_path, class_names): # 加载并预处理图像 transform = transforms.Compose([ transforms.Resize(256), transforms.CenterCrop(224), transforms.ToTensor(), transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225]) ]) image = Image.open(image_path).convert('RGB') img_tensor = transform(image).unsqueeze(0).to(device) # 预测 model.eval() with torch.no_grad(): outputs = model(img_tensor) probs = F.softmax(outputs, dim=1) _, preds = torch.max(outputs, 1) # 获取预测结果和概率 pred_class = class_names[preds.item()] confidence = probs[0][preds.item()].item() # 返回完整的预测结果(包括所有类别的概率) return pred_class, confidence, probs[0].cpu().numpy() # 病害解决方案 def get_solution(pred_class): solutions = { "Healthy": "当前植物状态良好,建议继续保持常规的养护管理:\n" "1. 定期浇水,保持土壤湿润但避免积水\n" "2. 每月施肥一次,使用均衡的复合肥\n" "3. 定期检查植物,预防病虫害的发生", "Late_blight": "检测到晚疫病,建议采取以下措施:\n" "1. 立即移除并销毁受感染的植物部分,防止病害扩散\n" "2. 使用铜基杀菌剂进行喷雾处理,每7-10天一次,连续2-3次\n" "3. 改善种植环境,增强通风,降低湿度\n" "4. 避免在傍晚浇水,减少叶片湿润时间\n" "5. 下一季种植时,选择抗病品种" } return solutions.get(pred_class, "未找到对应的解决方案") # Gradio前端界面(增强版) def create_gradio_interface(model, class_names): def predict_interface(image): if image is None: return "请上传一张植物叶片图片", "0%", "未找到解决方案", None, None # 预测 pred_class, confidence, all_probs = predict_image(model, image, class_names) solution = get_solution(pred_class) confidence_percent = f"{confidence * 100:.2f}%" # 创建预测概率可视化 plt.figure(figsize=(6, 4)) bars = plt.bar(class_names, all_probs) bars[class_names.index(pred_class)].set_color('g') # 预测类别标绿 plt.title("预测概率分布") plt.xlabel("类别") plt.ylabel("概率") plt.ylim(0, 1) plt.xticks(rotation=45) plt.tight_layout() prob_plot_path = "prediction_probability.png" plt.savefig(prob_plot_path, dpi=300, bbox_inches='tight') plt.close() # 创建原始图像可视化 original_img = Image.open(image).convert('RGB') original_img_path = "original_image.png" original_img.save(original_img_path) return pred_class, confidence_percent, solution, original_img_path, prob_plot_path # 创建Gradio界面 with gr.Blocks(title="植物病害识别系统") as interface: gr.Markdown("# 植物病害识别系统") gr.Markdown("上传植物叶片图片,系统将自动识别是否患有晚疫病") with gr.Row(): with gr.Column(): image_input = gr.Image(type="filepath", label="植物叶片图片") predict_button = gr.Button("预测") with gr.Column(): pred_output = gr.Textbox(label="预测结果") conf_output = gr.Textbox(label="置信度") solution_output = gr.Textbox(label="解决方案", lines=10) with gr.Row(): with gr.Column(): original_img_output = gr.Image(label="原始图像") with gr.Column(): prob_plot_output = gr.Image(label="预测概率分布") predict_button.click( fn=predict_interface, inputs=image_input, outputs=[pred_output, conf_output, solution_output, original_img_output, prob_plot_output] ) gr.Markdown("### 系统说明") gr.Markdown("本系统使用深度学习技术识别植物是否患有晚疫病," "可帮助农民早期发现植物病害并采取相应措施。") return interface # 主函数 def main(): # 探索数据集 print("正在探索数据集...") class_names, train_counts, val_counts = explore_dataset() # 加载数据 print("正在加载数据...") train_loader, val_loader, class_names = get_data_loaders(batch_size=32) # 构建模型 print("正在构建模型...") model = build_model(num_classes=len(class_names)) # 检查是否存在已训练的模型 if os.path.exists(model_save_path): print("加载已训练的模型...") model.load_state_dict(torch.load(model_save_path, map_location=device)) else: # 训练模型 print("开始训练模型...") model, train_losses, val_losses, train_accs, val_accs = train_model( model, train_loader, val_loader, num_epochs=15 ) # 评估模型 print("正在评估模型...") class_acc, report = evaluate_model(model, val_loader, class_names) # 检查每个类别的准确率是否满足要求 for class_name, acc in class_acc.items(): print(f"{class_name} 准确率: {acc:.2f}%") if acc < 70: print(f"警告: {class_name} 准确率低于70%,可能需要进一步优化模型") # 构建Gradio界面 print("正在构建Gradio界面...") interface = create_gradio_interface(model, class_names) # 启动Gradio界面 print("启动植物病害识别系统...") interface.launch() if __name__ == "__main__": main() 但是运行这个代码后显示测试集里的植物都患有晚疫病,给我修改后的完整代码

最新推荐

recommend-type

基于51单片机的智能温控电扇设计.doc

基于51单片机的智能温控电扇设计.doc
recommend-type

C++生成动态库dll项目、及C#调用C++的dll项目,两个项目分开的,配合使用

C++生成动态库dll项目、及C#调用C++的dll项目,两个项目分开的,配合使用
recommend-type

PKID查壳工具最新版发布,轻松识别安卓安装包加壳

根据提供的文件信息,我们可以详细解读以下知识点: ### PKiD(查壳)工具介绍 #### 标题分析 - **PKiD(查壳)**: 这是一个专门用于分析安卓安装包(APK文件)是否被加壳的应用程序。"查壳"是一种用于检测软件是否被保护层(即“壳”)包裹的技术术语。加壳是一种常见的软件保护手段,用于隐藏真实的代码逻辑,防止恶意逆向分析。 - **RAR格式文件**: 文件使用了RAR格式进行压缩,这是WinRAR软件用于文件压缩和解压缩的专有格式。 #### 描述分析 - **ApkScan-PKID查壳工具.zip**: 这指的是一款名为ApkScan的工具,它包含了PKID查壳功能。该工具被打包成ZIP格式,便于用户下载和使用。 - **安卓安装包**: 这是指Android平台的应用程序安装包,通常以APK作为文件扩展名。 - **加壳检测**: PKID查壳工具用于检测APK文件是否被加壳,加壳是一种常见的软件保护技术,用于加密和保护软件免遭逆向工程。 - **脱壳测试**: 如果检测到加壳,脱壳测试将用于尝试去除或绕过保护层,以便进行安全分析、调试或修改程序。 #### 标签分析 - **查壳**: 再次强调了工具的主要功能,即检测APK文件中的加壳情况。 - **最新版**: 表示这个文件是PKID查壳工具的最新版本。 - **PKID**: 这是工具的核心名称,代表着该软件的主要功能和用途。 #### 文件列表分析 - **PKiD(查壳).exe**: 这是一个可执行文件,说明PKID查壳工具是一个独立的应用程序,用户可以通过双击此文件直接运行程序,而无需安装。 ### 技术背景 #### 查壳工具的工作原理 查壳工具通常通过分析APK文件的头部信息、资源文件和代码段来检测加壳。它可能会检查PE文件格式的特定区域(APK基于DEX,但PE检查的概念相似),这些区域在加壳过程中可能会被特定的代码模式、字符串或签名标记。例如,某些壳会在文件头部加入特定的字符串,或者修改方法计数等信息。 #### 加壳技术 加壳技术通常用于防止软件被轻易反编译或逆向工程。它可以阻止潜在的窃取知识产权、绕过付费或防止代码分析等。加壳过程包括加密和压缩原始代码,然后在运行时解压和解密代码以供执行。 #### 脱壳技术 脱壳技术是指绕过或移除软件保护壳的过程。这通常需要对壳的工作原理有深入了解,并且需要一定的逆向工程技能。脱壳过程可能包括识别壳的签名、分析保护机制、修改代码和重定位等步骤。 #### 安全测试与逆向工程 查壳工具对于安全测试和逆向工程人员来说是非常重要的工具。它们可以帮助识别软件是否被加壳,从而决定是否需要进行脱壳操作以便深入分析软件的安全漏洞或版权问题。 #### 使用场景 - **安全测试**: 安全工程师在对移动应用程序进行安全评估时,需要确认是否有必要进行脱壳处理。 - **逆向工程**: 开发人员可能需要逆向工程某个应用,以更好地理解其工作原理或为了兼容性问题进行修改。 - **软件保护**: 对于软件开发者而言,了解常见的加壳和脱壳技术有助于更好地保护自己的应用免遭未授权的篡改和分析。 ### 结论 了解和掌握PKID查壳工具的使用对于任何需要对APK文件进行安全性分析的专业人士来说都是基础且关键的技能。该工具提供了一种快速判断软件是否加壳的方法,而且在发现壳的情况下,还可以推测出壳的来源公司,这对于后续的分析和处理提供了便利。然而,值得注意的是,进行脱壳操作往往需要对原始程序代码进行逆向工程,这在很多情况下可能涉及法律和道德问题,因此在使用此类工具时,必须确保其合法性和符合道德规范。
recommend-type

【心理学火柴人视频制作秘籍】:Coze工作流全攻略,带你从入门到精通

# 1. 心理学火柴人视频制作概述 ## 1.1 创作心理学火柴人视频的动机 心理学火柴人视频作为一种富有创意的视觉传达方式,不仅能够将复杂的心理学理论变得生动和易于理解,而且还能为观众提供轻松愉悦的学习体验。这种视频通常采用简化的角色——火柴人,通过动画的形式演绎心理学相关的内容,使得原本抽象的理论变得具体、形象。 ## 1.2 火柴人视频的普及与应用 随着互联
recommend-type

frame.onload不执行?

<think>在解决`frame.onload`不执行的问题时,我们需要考虑多种可能的原因和解决方案。下面我将详细解释这个问题,并提供相应的代码示例。##可能的原因及解决方案###1.事件绑定时机不正确如果我们在设置`src`属性之后再绑定`onload`事件,可能会错过事件的触发(因为资源可能已经加载完成)。所以,我们应该在设置`src`之前绑定`onload`事件。###2.资源加载失败如果资源加载失败,`onload`事件可能不会触发,而触发`onerror`事件。因此,我们需要同时监听`onerror`事件。###3.iframe内容为空如果我们使用`document.write`来设
recommend-type

一键关闭系统更新的工具介绍

从给定的文件信息中我们可以分析出几个相关的知识点,以下是详细说明: 【标题】“系统禁止更新工具.7z”暗示着这个压缩文件内包含的可能是一款软件工具,其主要功能是阻止或禁止操作系统的更新。这种工具可能针对的是Windows、Linux或者其他操作系统的自动更新功能。一般来说,用户可能出于稳定性考虑,希望控制更新时间,或者是因为特定的软件环境依赖于旧版本的系统兼容性,不希望系统自动更新导致兼容性问题。 【描述】“一健关闭系统更新”说明了该工具的使用方式非常简单直接。用户只需通过简单的操作,比如点击一个按钮或者执行一个命令,就能实现关闭系统自动更新的目的。这种一键式操作符合用户追求的易用性原则,使得不太精通系统操作的用户也能轻松控制更新设置。 【标签】“系统工具”表明这是一个与操作系统紧密相关的辅助工具。系统工具通常包括系统清理、性能优化、磁盘管理等多种功能,而本工具专注于管理系统更新,使其成为系统维护中的一环。 【压缩包子文件的文件名称列表】“系统禁止更新工具”是压缩包内的文件名。由于文件格式为“.7z”,这说明该工具采用了7-Zip压缩格式。7-Zip是一款开源且免费的压缩软件,支持非常高的压缩比,并且能够处理各种压缩文件格式,如ZIP、RAR等。它支持创建密码保护的压缩文件和分卷压缩,这在需要转移大量数据时特别有用。然而在这个上下文中,“系统禁止更新工具”文件名暗示了该压缩包内只包含了一个程序,即专门用于关闭系统更新的工具。 根据标题和描述,我们可以推测该工具可能的实现机制,例如: 1. 修改系统服务的配置:在Windows系统中,可以通过修改Windows Update服务的属性来禁用该服务,从而阻止系统自动下载和安装更新。 2. 修改注册表设置:通过编辑Windows注册表中的某些特定键值,可以关闭系统更新功能。这通常涉及到对HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate和HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate\AU等路径下的设置进行修改。 3. 使用第三方软件:某些第三方工具提供了一键禁用系统更新的功能,通过修改操作系统的相关文件和配置来实现更新的禁用。这包括阻止Windows Update的运行、拦截更新下载等。 该工具的使用可能带来一些后果,比如系统安全风险的增加、系统漏洞得不到及时修复以及可能违反某些软件许可协议。用户在使用这类工具时应该清楚这些潜在的影响,并自行承担相应的风险。 总结来说,该“系统禁止更新工具”可以视为一个针对特定用户需求,尤其是企业用户或个人用户中对系统稳定性有较高要求的人群,提供的一个便捷的解决方案。它通过直接修改系统设置来禁止操作系统自动更新,但用户在使用此工具时需要谨慎,因为这可能会带来系统安全和稳定性的风险。
recommend-type

罗技GHUB驱动21.03.24版自定义设置与性能优化:专家级使用技巧

# 摘要 本文综合介绍了罗技GHUB驱动的安装、自定义设置、性能优化、进阶应用以及故障排除。通过对GHUB软件界面和功能的详细介绍,阐述了硬件识别、宏命令创建以及游戏特有设置的定制。同时,文中探讨了性能调整的理论基础,并提供实践中针对网络延迟和数据传输优化的具体技巧。进一步探讨了高级个性化配置和第三方软件集成的策略,最后针对
recommend-type

RX72M单片机读取差分编码器,输入输出滤波,差分转单端后将5V电压转为3.3v输入给单片机

<think>嗯,我需要帮助用户解决关于RX72M单片机读取差分编码器的问题。用户的需求很明确:如何用RX72M单片机读取差分编码器,实现输入输出滤波,将差分信号转为单端信号,并进行5V到3.3V的电平转换。首先回顾用户之前的几个引用。引用1提到ADC的差分输入模式,这让我想到用户可能需要处理模拟信号,但差分编码器通常是数字信号。用户的主要需求集中在数字信号处理,比如正交编码器的脉冲信号。因此,可能需要参考引用1中的差分输入设置,但注意这里适用的是数字接口而非ADC。引用2关于74HC245和SN74LVC1T45DBVR芯片的内容非常有价值。这两个都是电平转换方案,尤其是SN74LVC1T4
recommend-type

鼎捷易飞ERPV9.0委外进货单批量导入解决方案

根据提供的文件信息,我们可以从标题、描述、标签以及压缩包文件列表中提取以下知识点: 1. 委外进货单批量导入程序及模版格式 标题提到的“委外进货单批量导入程序”指的是一个软件应用,其主要功能是允许用户批量地将委外进货数据导入到ERP系统中。批量导入通常是指在ERP系统中不需要逐条手动输入数据,而是通过预先定义好的模板,一次性将大量数据导入系统。这样的程序对于提高工作效率、减少重复性工作以及避免人为错误具有重要意义。 2. 鼎捷易飞ERPV9.0 描述中提到的“鼎捷易飞ERPV9.0”是一个特定版本的ERP系统,由鼎捷软件公司开发。ERP(Enterprise Resource Planning,企业资源计划)系统是一种用于整合企业内部所有资源信息,实现信息流、物流、资金流、工作流的高度集成和自动化管理的软件。ERPV9.0是该系列产品的版本号,表明该程序和文件模板是为这一特定版本的ERP系统设计。 3. .NET C#源代码 标题中的“.NET C#源代码”表示程序是使用.NET框架和C#语言开发的。.NET是微软公司开发的一个软件框架,用于构建和运行Windows应用程序。C#(读作“C Sharp”)是.NET框架下的一种编程语言,具有面向对象、类型安全和垃圾回收等特点。开发者可能提供了源代码,以便企业用户可以自行修改、调整以满足特定需求。 4. 使用方法和步骤 描述中详细说明了程序的使用方法: - 首先编辑模版格式数据,即将需要导入的数据按照特定的格式要求填写到模板中。 - 然后在程序中选择单别(可能指的是单据类型)和日期等条件。 - 点击“导入数据”按钮,程序将提示用户选择含有数据的模板文件。 - 程序会进行数据校验,以确保数据的正确性。校验规则是特定的,如果用户不需要特定的校验条件,可以在程序中直接删除这部分代码。 - 最后,数据校验无误后,程序可以生成相应的进货单据。 5. 自定义程序和模板 在标签中提到的“易飞ERP委外进货单导入程序”、“委外进货单导入程序”和“易飞ERP自定义程序”表明,所提供的程序可以根据用户的特定需求进行定制。同时,模板格式的使用,也意味着用户可以根据自己的业务需要和ERP系统的要求调整模板内容,以便更好地适应数据导入过程。 6. 压缩包子文件的文件名称列表 提供的压缩包包含两个文件: - “委外进货单批量导入格式.xls”指的是一个Excel格式的文件,这个文件应该是一个符合要求的导入模板,用户需要按照这个模板的格式来准备需要导入的数据。 - “委外入库导入(源代码).zip”是一个包含源代码的压缩文件,其中可能包含程序的完整源代码,也可能仅包含与导入功能相关的部分代码。 7. 文件列表和数据导入流程 通过文件列表可以理解整个数据导入流程: - 用户首先需要准备好数据,并将其按照“委外进货单批量导入格式.xls”模板中的要求填入相应的数据。 - 用户然后使用“委外入库导入(源代码).zip”中的程序,根据程序使用说明进行配置。 - 最后,用户通过程序界面导入整理好的Excel模板文件,程序会自动处理数据导入的过程,并进行必要的数据校验,最终生成所需的进货单据。 以上总结的知识点涵盖了该压缩包文件的内容、用途、技术细节以及操作步骤,为了解和使用该委外进货单批量导入程序提供了充分的信息。
recommend-type

罗技GHUB驱动21.03.24版更新日志详细解读:驱动改进点全掌握

# 摘要 罗技GHUB驱动自发布以来,不断更新以提升兼容性、用户界面体验、性能和功能。本文主要概述了罗技GHUB驱动的更新历程,特别聚焦于21.03.24版的改进点,包括对新硬件的支持、现有设备兼容性的增强、界面设计和交互体验的优化。此外,本文深入探讨了该版本新增与改进的功能及其具体使用方法,并提供了故障排除指南。通过与旧版驱