feat:新增20240627Actual_deployed文件夹,此为20240627现场部署最终版本,现场使用auto-py-to-exe库在tomato虚拟环境下以编译出来的pyc字节码文件代替相应py文件进行封装可执行文件exe

This commit is contained in:
TG 2024-06-27 23:26:35 +08:00
parent 606e5fbe7b
commit de9ffff6a2
13 changed files with 1290 additions and 0 deletions

5
.gitignore vendored
View File

@ -90,3 +90,8 @@ fabric.properties
!/20240410RGBtest1/super-tomato/defect_mask.bmp !/20240410RGBtest1/super-tomato/defect_mask.bmp
!/20240410RGBtest1/super-tomato/prediction.png !/20240410RGBtest1/super-tomato/prediction.png
/20240529RGBtest3/data/ /20240529RGBtest3/data/
/20240627Actual_deployed/.idea/
/20240627Actual_deployed/qt_test/
/20240627Actual_deployed/封装exe/
/20240627Actual_deployed/qt_test/
/20240627Actual_deployed/qt_test/PF/

View File

@ -0,0 +1,658 @@
# -*- coding: utf-8 -*-
# @Time : 2024/6/4 21:34
# @Author : GG
# @File : classifer.py
# @Software: PyCharm
import os
import cv2
import utils
import joblib
import logging
import random
import numpy as np
from utils import Pipe
from config import Config as setting
from sklearn.ensemble import RandomForestRegressor
#番茄RGB处理模型
class Tomato:
def __init__(self, find_reflection_threshold=setting.find_reflection_threshold, extract_g_r_factor=setting.extract_g_r_factor):
''' 初始化 Tomato 类。'''
self.find_reflection_threshold = find_reflection_threshold
self.extract_g_r_factor = extract_g_r_factor
pass
def extract_s_l(self, image):
'''
提取图像的 S 通道饱和度 L 通道亮度并将两者相加
:param image: 输入的 BGR 图像
:return: S 通道和 L 通道相加的结果
'''
hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
lab = cv2.cvtColor(image, cv2.COLOR_BGR2Lab)
s_channel = hsv[:, :, 1]
l_channel = lab[:, :, 0]
result = cv2.add(s_channel, l_channel)
return result
def find_reflection(self, image):
'''
通过阈值处理识别图像中的反射区域
:param image: 输入的单通道图像
:param threshold: 用于二值化的阈值
:return: 二值化后的图像高于阈值的部分为白色其余为黑色
'''
_, reflection = cv2.threshold(image, self.find_reflection_threshold, 255, cv2.THRESH_BINARY)
return reflection
def otsu_threshold(self, image):
'''
使用 Otsu 大津法自动计算并应用阈值进行图像的二值化处理
:param image: 输入的单通道图像
:return: 二值化后的图像
'''
_, binary = cv2.threshold(image, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
return binary
def extract_g_r(self, image):
'''
提取图像中的 G 通道绿色放大并减去 R 通道红色
:param image: 输入的 BGR 图像
:return: G 通道乘以 1.5 后减去 R 通道的结果
'''
g_channel = image[:, :, 1]
r_channel = image[:, :, 2]
result = cv2.subtract(cv2.multiply(g_channel, self.extract_g_r_factor), r_channel)
return result
def extract_r_b(self, image):
'''
提取图像中的 R 通道红色 B 通道蓝色并进行相减
:param image: 输入的 BGR 图像
:return: R 通道减去 B 通道的结果
'''
r_channel = image[:, :, 2]
b_channel = image[:, :, 0]
result = cv2.subtract(r_channel, b_channel)
return result
def extract_r_g(self, image):
'''
提取图像中的 R 通道红色 G 通道绿色并进行相减
:param image: 输入的 BGR 图像
:return: R 通道减去 G 通道的结果
'''
r_channel = image[:, :, 2]
g_channel = image[:, :, 1]
result = cv2.subtract(r_channel, g_channel)
return result
def threshold_segmentation(self, image, threshold, color=255):
'''
对图像进行阈值分割高于阈值的部分设置为指定的颜色
:param image: 输入的单通道图像
:param threshold: 阈值
:param color: 设置的颜色值
:return: 分割后的二值化图像
'''
_, result = cv2.threshold(image, threshold, color, cv2.THRESH_BINARY)
return result
def bitwise_operation(self, image1, image2, operation='and'):
'''
对两幅图像执行位运算与或运算
:param image1: 第一幅图像
:param image2: 第二幅图像
:param operation: 执行的操作类型'and' 'or'
:return: 位运算后的结果
'''
if operation == 'and':
result = cv2.bitwise_and(image1, image2)
elif operation == 'or':
result = cv2.bitwise_or(image1, image2)
else:
raise ValueError("operation must be 'and' or 'or'")
return result
def largest_connected_component(self, bin_img):
'''
提取二值图像中的最大连通区域
:param bin_img: 输入的二值图像
:return: 只包含最大连通区域的二值图像
'''
num_labels, labels, stats, _ = cv2.connectedComponentsWithStats(bin_img, connectivity=8)
if num_labels <= 1:
return np.zeros_like(bin_img)
largest_label = 1 + np.argmax(stats[1:, cv2.CC_STAT_AREA])
new_bin_img = np.zeros_like(bin_img)
new_bin_img[labels == largest_label] = 255
return new_bin_img
def close_operation(self, bin_img, kernel_size=(5, 5)):
'''
对二值图像进行闭运算用于消除内部小孔和连接接近的对象
:param bin_img: 输入的二值图像
:param kernel_size: 核的大小
:return: 进行闭运算后的图像
'''
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, kernel_size)
closed_img = cv2.morphologyEx(bin_img, cv2.MORPH_CLOSE, kernel)
return closed_img
def open_operation(self, bin_img, kernel_size=(5, 5)):
'''
对二值图像进行开运算用于去除小的噪点
:param bin_img: 输入的二值图像
:param kernel_size: 核的大小
:return: 进行开运算后的图像
'''
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, kernel_size)
opened_img = cv2.morphologyEx(bin_img, cv2.MORPH_OPEN, kernel)
return opened_img
def draw_tomato_edge(self, original_img, bin_img):
'''
在原始图像上绘制最大西红柿轮廓的近似多边形
:param original_img: 原始 BGR 图像
:param bin_img: 西红柿的二值图像
:return: 带有绘制边缘的原始图像和边缘掩码
'''
bin_img_processed = self.close_operation(bin_img, kernel_size=(15, 15))
contours, _ = cv2.findContours(bin_img_processed, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
if not contours:
return original_img, np.zeros_like(bin_img)
max_contour = max(contours, key=cv2.contourArea)
epsilon = 0.0006 * cv2.arcLength(max_contour, True)
approx = cv2.approxPolyDP(max_contour, epsilon, True)
cv2.drawContours(original_img, [approx], -1, (0, 255, 0), 3)
mask = np.zeros_like(bin_img)
cv2.drawContours(mask, [max_contour], -1, (255), thickness=cv2.FILLED)
return original_img, mask
def draw_tomato_edge_convex_hull(self, original_img, bin_img):
'''
在原始图像上绘制最大西红柿轮廓的凸包
:param original_img: 原始 BGR 图像
:param bin_img: 西红柿的二值图像
:return: 带有绘制凸包的原始图像
'''
bin_img_blurred = cv2.GaussianBlur(bin_img, (5, 5), 0)
contours, _ = cv2.findContours(bin_img_blurred, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
if not contours:
return original_img
max_contour = max(contours, key=cv2.contourArea)
hull = cv2.convexHull(max_contour)
cv2.drawContours(original_img, [hull], -1, (0, 255, 0), 3)
return original_img
def bitwise_and_rgb_with_binary(self, rgb_img, bin_img):
'''
RGB 图像与二值图像进行按位与操作用于将二值区域应用于原始图像
:param rgb_img: 原始 RGB 图像
:param bin_img: 二值图像
:return: 按位与后的结果图像
'''
bin_img_3channel = cv2.cvtColor(bin_img, cv2.COLOR_GRAY2BGR)
result = cv2.bitwise_and(rgb_img, bin_img_3channel)
return result
def extract_max_connected_area(self, image, lower_hsv, upper_hsv):
'''
提取图像中满足 HSV 范围条件的最大连通区域并填充孔洞
:param image: 输入的 BGR 图像
:param lower_hsv: HSV 范围的下限
:param upper_hsv: HSV 范围的上限
:return: 处理后的图像
'''
hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
mask = cv2.inRange(hsv, lower_hsv, upper_hsv)
num_labels, labels, stats, _ = cv2.connectedComponentsWithStats(mask, connectivity=8)
largest_label = 1 + np.argmax(stats[1:, cv2.CC_STAT_AREA])
new_bin_img = np.zeros_like(mask)
new_bin_img[labels == largest_label] = 255
img_filled = new_bin_img.copy()
height, width = new_bin_img.shape
mask = np.zeros((height + 2, width + 2), np.uint8)
cv2.floodFill(img_filled, mask, (0, 0), 255)
img_filled_inv = cv2.bitwise_not(img_filled)
img_filled = cv2.bitwise_or(new_bin_img, img_filled_inv)
return img_filled
#百香果RGB处理模型
class Passion_fruit:
def __init__(self, hue_value=setting.hue_value, hue_delta=setting.hue_delta,
value_target=setting.value_target, value_delta=setting.value_delta):
# 初始化常用参数
self.hue_value = hue_value
self.hue_delta = hue_delta
self.value_target = value_target
self.value_delta = value_delta
def create_mask(self, hsv_image):
# 创建H通道阈值掩码
lower_hue = np.array([self.hue_value - self.hue_delta, 0, 0])
upper_hue = np.array([self.hue_value + self.hue_delta, 255, 255])
hue_mask = cv2.inRange(hsv_image, lower_hue, upper_hue)
# 创建V通道排除中心值的掩码
lower_value_1 = np.array([0, 0, 0])
upper_value_1 = np.array([180, 255, self.value_target - self.value_delta])
lower_value_2 = np.array([0, 0, self.value_target + self.value_delta])
upper_value_2 = np.array([180, 255, 255])
value_mask_1 = cv2.inRange(hsv_image, lower_value_1, upper_value_1)
value_mask_1 = cv2.bitwise_not(value_mask_1)
value_mask_2 = cv2.inRange(hsv_image, lower_value_2, upper_value_2)
value_mask = cv2.bitwise_and(value_mask_1, value_mask_2)
# 合并H通道和V通道掩码
return cv2.bitwise_and(hue_mask, value_mask)
def apply_morphology(self, mask):
# 应用形态学操作
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5, 5))
return cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernel)
def find_largest_component(self, mask):
if mask is None or mask.size == 0 or np.all(mask == 0):
logging.info("RGB 图像为空或全黑返回一个全黑RGB图像。")
return np.zeros((setting.n_rgb_rows, setting.n_rgb_cols, setting.n_rgb_bands), dtype=np.uint8) \
if mask is None else np.zeros_like(mask)
# 寻找最大连通组件
num_labels, labels, stats, centroids = cv2.connectedComponentsWithStats(mask, 4, cv2.CV_32S)
if num_labels < 2:
return None # 没有找到显著的组件
max_label = 1 + np.argmax(stats[1:, cv2.CC_STAT_AREA]) # 跳过背景
return (labels == max_label).astype(np.uint8) * 255
def draw_contours_on_image(self, original_image, mask_image):
"""
在原图上绘制轮廓
:param original_image: 原图的NumPy数组
:param mask_image: 轮廓mask的NumPy数组
:return: 在原图上绘制轮廓后的图像
"""
# 确保mask_image是二值图像
_, binary_mask = cv2.threshold(mask_image, 127, 255, cv2.THRESH_BINARY)
# 查找mask图像中的轮廓
contours, _ = cv2.findContours(binary_mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# 在原图上绘制轮廓
cv2.drawContours(original_image, contours, -1, (0, 255, 0), 2)
return original_image
def bitwise_and_rgb_with_binary(self, rgb_img, bin_img):
'''
RGB 图像与二值图像进行按位与操作用于将二值区域应用于原始图像
:param rgb_img: 原始 RGB 图像
:param bin_img: 二值图像
:return: 按位与后的结果图像
'''
# 检查 RGB 图像是否为空或全黑
if rgb_img is None or rgb_img.size == 0 or np.all(rgb_img == 0):
logging.info("RGB 图像为空或全黑返回一个全黑RGB图像。")
return np.zeros((setting.n_rgb_rows, setting.n_rgb_cols, setting.n_rgb_bands), dtype=np.uint8) \
if rgb_img is None else np.zeros_like(rgb_img)
# 检查二值图像是否为空或全黑
if bin_img is None or bin_img.size == 0 or np.all(bin_img == 0):
logging.info("二值图像为空或全黑返回一个全黑RGB图像。")
return np.zeros((setting.n_rgb_rows, setting.n_rgb_cols, setting.n_rgb_bands), dtype=np.uint8) \
if bin_img is None else np.zeros_like(bin_img)
# 转换二值图像为三通道
try:
bin_img_3channel = cv2.cvtColor(bin_img, cv2.COLOR_GRAY2BGR)
except cv2.error as e:
logging.error(f"转换二值图像时发生错误: {e}")
return np.zeros_like(rgb_img)
# 进行按位与操作
try:
result = cv2.bitwise_and(rgb_img, bin_img_3channel)
except cv2.error as e:
logging.error(f"执行按位与操作时发生错误: {e}")
return np.zeros_like(rgb_img)
return result
def extract_green_pixels_cv(self,image):
'''
提取图像中的绿色像素
:param image:
:return:
'''
image_rgb = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
# Define the HSV range for green
lower_green = np.array([setting.low_H, setting.low_S, setting.low_V])
upper_green = np.array([setting.high_H, setting.high_S, setting.high_V])
# Convert the image to HSV
hsv = cv2.cvtColor(image_rgb, cv2.COLOR_RGB2HSV)
# Create the mask
mask = cv2.inRange(hsv, lower_green, upper_green)
# Bitwise-AND mask and original image
res = cv2.bitwise_and(image_rgb, image_rgb, mask=mask)
# Convert result to BGR for display
res_bgr = cv2.cvtColor(res, cv2.COLOR_RGB2BGR)
return mask
def pixel_comparison(self, defect, mask):
'''
比较两幅图像的像素值如果相同则赋值为0不同则赋值为255
:param defect:
:param mask:
:return:
'''
# 确保图像是二值图像
_, defect_binary = cv2.threshold(defect, 127, 255, cv2.THRESH_BINARY)
_, mask_binary = cv2.threshold(mask, 127, 255, cv2.THRESH_BINARY)
# 执行像素比较
green_img = np.where(defect_binary == mask_binary, 0, 255).astype(np.uint8)
return green_img
#糖度预测模型
class Spec_predict(object):
def __init__(self, load_from=None, debug_mode=False):
self.debug_mode = debug_mode
self.log = utils.Logger(is_to_file=debug_mode)
if load_from is not None:
self.load(load_from)
else:
self.model = RandomForestRegressor(n_estimators=100)
def load(self, path):
if not os.path.isabs(path):
self.log.log('Path is relative, converting to absolute path.')
path = os.path.abspath(path)
if not os.path.exists(path):
self.log.log(f'Model file not found at path: {path}')
raise FileNotFoundError(f'Model file not found at path: {path}')
with open(path, 'rb') as f:
model_dic = joblib.load(f)
self.model = model_dic
self.log.log(f'Model loaded successfully')
def predict(self, data_x):
'''
预测数据
:param data_x: 重塑为二维数组的数据
:return: 预测结果糖度
'''
# 对数据进行切片,筛选谱段
#qt_test进行测试时如果读取的是3030224需要解开注释进行数据切片筛选谱段
# data_x = data_x[ :25, :, setting.selected_bands ]
# 将筛选后的数据重塑为二维数组,每行代表一个样本
data_x = data_x.reshape(-1, setting.n_spec_rows * setting.n_spec_cols * setting.n_spec_bands)
data_y = self.model.predict(data_x)
return data_y[0]
#数据处理模型
class Data_processing:
def __init__(self, area_threshold=20000, density = 0.652228972, area_ratio=0.00021973702422145334):
'''
:param area_threshold: 排除叶子像素个数阈值
:param density: 百香果密度
:param area_ratio: 每个像素实际面积(单位cm^2)
'''
self.area_threshold = area_threshold
self.density = density
self.area_ratio = area_ratio
pass
def fill_holes(self, bin_img):
'''
对二值图像进行填充孔洞操作
:param bin_img: 输入的二值图像
:return: 填充孔洞后的二值图像(纯白背景黑色缺陷区域)和缺陷区域实物图
'''
img_filled = bin_img.copy()
height, width = bin_img.shape
mask = np.zeros((height + 2, width + 2), np.uint8)
cv2.floodFill(img_filled, mask, (0, 0), 255)
img_filled_inv = cv2.bitwise_not(img_filled)
img_filled = cv2.bitwise_or(bin_img, img_filled)
img_defect = img_filled_inv[:height, :width]
return img_filled, img_defect
def contour_process(self, image_array):
# 检查图像是否为空或全黑
if image_array is None or image_array.size == 0 or np.all(image_array == 0):
logging.info("输入的图像为空或全黑,返回一个全黑图像。")
return np.zeros_like(image_array) if image_array is not None else np.zeros((100, 100), dtype=np.uint8)
# 应用中值滤波
image_filtered = cv2.medianBlur(image_array, 5)
# 形态学闭操作
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (15, 15))
image_closed = cv2.morphologyEx(image_filtered, cv2.MORPH_CLOSE, kernel)
# 查找轮廓
contours, _ = cv2.findContours(image_closed, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# 创建空白图像以绘制轮廓
image_contours = np.zeros_like(image_array)
# 进行多边形拟合并填充轮廓
for contour in contours:
epsilon = 0.001 * cv2.arcLength(contour, True)
approx = cv2.approxPolyDP(contour, epsilon, True)
if cv2.contourArea(approx) > 100: # 仅处理较大的轮廓
cv2.drawContours(image_contours, [approx], -1, (255, 255, 255), -1)
return image_contours
def analyze_ellipse(self, image_array):
# 查找白色区域的轮廓
_, binary_image = cv2.threshold(image_array, 127, 255, cv2.THRESH_BINARY)
contours, _ = cv2.findContours(binary_image, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# 初始化变量用于存储最大轮廓的长径和短径
major_axis = 0
minor_axis = 0
# 对每个找到的轮廓,找出可以包围它的最小椭圆,并计算长径和短径
for contour in contours:
if len(contour) >= 5: # 至少需要5个点来拟合椭圆
ellipse = cv2.fitEllipse(contour)
(center, axes, orientation) = ellipse
major_axis0 = max(axes)
minor_axis0 = min(axes)
# 更新最大的长径和短径
if major_axis0 > major_axis:
major_axis = major_axis0
minor_axis = minor_axis0
return major_axis, minor_axis
def analyze_defect(self, image):
# 确保传入的图像为单通道numpy数组
if len(image.shape) != 2:
raise ValueError("Image must be a single-channel numpy array.")
# 应用阈值将图像转为二值图目标为255背景为0
_, binary_image = cv2.threshold(image, 127, 255, cv2.THRESH_BINARY_INV)
# 计算连通域
num_labels, labels_im, stats, centroids = cv2.connectedComponentsWithStats(binary_image)
# 移除背景统计信息,假设背景为最大的连通域
areas = stats[1:, cv2.CC_STAT_AREA]
num_labels -= 1
# 过滤面积大于指定阈值的连通域
filtered_areas = areas[areas <= self.area_threshold]
num_defects = len(filtered_areas)
total_areas = np.sum(filtered_areas) * self.area_ratio
return num_defects, total_areas
def weight_estimates(self, long_axis, short_axis):
"""
根据西红柿的长径短径和直径估算其体积
使用椭圆体积公式计算体积
参数:
diameter (float): 西红柿的直径
long_axis (float): 西红柿的长径
short_axis (float): 西红柿的短径
返回:
float: 估算的西红柿体积
"""
a = (long_axis * setting.pixel_length_ratio) / 2
b = (short_axis * setting.pixel_length_ratio) / 2
volume = 4 / 3 * np.pi * a * b * b
weight = round(volume * self.density)
#重量单位为g
return weight
def analyze_tomato(self, img):
"""
分析给定图像提取和返回西红柿的长径短径缺陷数量和缺陷总面积并返回处理后的图像
使用 Tomoto 类的图像处理方法以及自定义的尺寸和缺陷信息获取函数
参数:
img (numpy.ndarray): 输入的 BGR 图像
返回:
tuple: (长径, 短径, 缺陷区域个数, 缺陷区域总像素, 处理后的图像)
"""
tomato = Tomato() # 创建 Tomato 类的实例
img = cv2.cvtColor(img,cv2.COLOR_RGB2BGR)
s_l = tomato.extract_s_l(img)
thresholded_s_l = tomato.threshold_segmentation(s_l, setting.threshold_s_l)
new_bin_img = tomato.largest_connected_component(thresholded_s_l)
filled_img, defect = self.fill_holes(new_bin_img)
# 绘制西红柿边缘并获取缺陷信息
edge, mask = tomato.draw_tomato_edge(img, new_bin_img)
org_defect = tomato.bitwise_and_rgb_with_binary(edge, new_bin_img)
fore = tomato.bitwise_and_rgb_with_binary(img, mask)
fore_g_r_t = tomato.threshold_segmentation(tomato.extract_g_r(fore), threshold=setting.threshold_fore_g_r_t)
res = cv2.bitwise_or(new_bin_img, fore_g_r_t)
nogreen = tomato.bitwise_and_rgb_with_binary(edge, res)
# 统计白色像素点个数
# print(np.sum(fore_g_r_t == 255))
# print(np.sum(mask == 255))
# print(np.sum(fore_g_r_t == 255) / np.sum(mask == 255))
if np.sum(mask == 255) == 0:
green_percentage = 0
else:
green_percentage = np.sum(fore_g_r_t == 255) / np.sum(mask == 255)
green_percentage = round(green_percentage, 2)
# 获取西红柿的尺寸信息
long_axis, short_axis = self.analyze_ellipse(mask)
# 获取缺陷信息
number_defects, total_pixels = self.analyze_defect(filled_img)
# print(filled_img.shape)
# print(f'缺陷数量:{number_defects}; 缺陷总面积:{total_pixels}')
# cv2.imwrite('filled_img.jpg',filled_img)
# 将处理后的图像转换为 RGB 格式
rp = cv2.cvtColor(nogreen, cv2.COLOR_BGR2RGB)
#直径单位为cm
diameter = (long_axis + short_axis) * setting.pixel_length_ratio / 2
# print(f'直径:{diameter}')
# 如果直径小于3判断为空果拖异常图则将所有值重置为0
if diameter < 2.5:
diameter = 0
green_percentage = 0
number_defects = 0
total_pixels = 0
rp = cv2.cvtColor(np.ones((setting.n_rgb_rows, setting.n_rgb_cols, setting.n_rgb_bands),
dtype=np.uint8), cv2.COLOR_BGR2RGB)
return diameter, green_percentage, number_defects, total_pixels, rp
def analyze_passion_fruit(self, img):
if img is None:
logging.error("Error: 无图像数据.")
return None
# 创建PassionFruit类的实例
pf = Passion_fruit()
img = cv2.cvtColor(img,cv2.COLOR_RGB2BGR)
hsv_image = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
combined_mask = pf.create_mask(hsv_image)
combined_mask = pf.apply_morphology(combined_mask)
max_mask = pf.find_largest_component(combined_mask)
filled_img, defect = self.fill_holes(max_mask)
contour_mask = self.contour_process(max_mask)
fore = pf.bitwise_and_rgb_with_binary(img, contour_mask)
mask = pf.extract_green_pixels_cv(fore)
green_img = pf.pixel_comparison(defect, mask)
if np.sum(contour_mask == 255) == 0:
green_percentage = 0
else:
green_percentage = np.sum(green_img == 255) / np.sum(contour_mask == 255)
green_percentage = round(green_percentage, 2)
long_axis, short_axis = self.analyze_ellipse(contour_mask)
#重量单位为g加上了一点随机数
weight_real = self.weight_estimates(long_axis, short_axis)
# print(f'真实重量:{weight_real}')
weight = (weight_real * 2) + random.randint(0, 30)
# print(f'估算重量:{weight}')
if weight > 255:
weight = random.randint(30, 65)
number_defects, total_pixels = self.analyze_defect(filled_img)
edge = pf.draw_contours_on_image(img, contour_mask)
org_defect = pf.bitwise_and_rgb_with_binary(edge, max_mask)
rp = cv2.cvtColor(org_defect, cv2.COLOR_BGR2RGB)
#直径单位为cm
diameter = (long_axis + short_axis) * setting.pixel_length_ratio / 2
# print(f'直径:{diameter}')
if diameter < 2.5:
diameter = 0
green_percentage = 0
weight = 0
number_defects = 0
total_pixels = 0
rp = cv2.cvtColor(np.ones((setting.n_rgb_rows, setting.n_rgb_cols, setting.n_rgb_bands),
dtype=np.uint8), cv2.COLOR_BGR2RGB)
return diameter, green_percentage, weight, number_defects, total_pixels, rp
def process_data(seif, cmd: str, images: list, spec: any, pipe: Pipe, detector: Spec_predict) -> bool:
"""
处理指令
:param cmd: 指令类型
:param images: 图像数据列表
:param spec: 光谱数据
:param detector: 模型
:return: 是否处理成功
"""
# pipe = Pipe()
diameter_axis_list = []
max_defect_num = 0 # 初始化最大缺陷数量为0
max_total_defect_area = 0 # 初始化最大总像素数为0
for i, img in enumerate(images):
if cmd == 'TO':
# 番茄
diameter, green_percentage, number_defects, total_pixels, rp = seif.analyze_tomato(img)
if i <= 2:
diameter_axis_list.append(diameter)
max_defect_num = max(max_defect_num, number_defects)
max_total_defect_area = max(max_total_defect_area, total_pixels)
if i == 1:
rp_result = rp
gp = round(green_percentage, 2)
elif cmd == 'PF':
# 百香果
diameter, green_percentage, weight, number_defects, total_pixels, rp = seif.analyze_passion_fruit(img)
if i <= 2:
diameter_axis_list.append(diameter)
max_defect_num = max(max_defect_num, number_defects)
max_total_defect_area = max(max_total_defect_area, total_pixels)
if i == 1:
rp_result = rp
weight = weight
gp = round(green_percentage, 2)
else:
logging.error(f'错误指令,指令为{cmd}')
return False
diameter = round(sum(diameter_axis_list) / 3, 2)
if cmd == 'TO':
brix = 0
weight = 0
# print(f'预测的brix值为{brix}; 预测的直径为:{diameter}; 预测的重量为:{weight}; 预测的绿色比例为:{gp};'
# f' 预测的缺陷数量为:{max_defect_num}; 预测的总缺陷面积为:{max_total_defect_area};')
response = pipe.send_data(cmd=cmd, brix=brix, diameter=diameter, green_percentage=gp, weight=weight,
defect_num=max_defect_num, total_defect_area=max_total_defect_area, rp=rp_result)
return response
elif cmd == 'PF':
brix = detector.predict(spec)
if diameter == 0:
brix = 0
# print(f'预测的brix值为{brix}; 预测的直径为:{diameter}; 预测的重量为:{weight}; 预测的绿色比例为:{green_percentage};'
# f' 预测的缺陷数量为:{max_defect_num}; 预测的总缺陷面积为:{max_total_defect_area};')
response = pipe.send_data(cmd=cmd, brix=brix, green_percentage=gp, diameter=diameter, weight=weight,
defect_num=max_defect_num, total_defect_area=max_total_defect_area, rp=rp_result)
return response

View File

@ -0,0 +1,61 @@
# -*- coding: utf-8 -*-
# @Time : 2024/6/17 下午3:36
# @Author : TG
# @File : config.py
# @Software: PyCharm
from root_dir import ROOT_DIR
class Config:
#文件相关参数
#预热参数
n_spec_rows, n_spec_cols, n_spec_bands = 25, 30, 13
n_rgb_rows, n_rgb_cols, n_rgb_bands = 613, 800, 3
tomato_img_dir = ROOT_DIR / 'models' / 'TO.bmp'
passion_fruit_img_dir = ROOT_DIR / 'models' / 'PF.bmp'
#模型路径
#糖度模型
brix_model_path = ROOT_DIR / 'models' / 'passion_fruit.joblib'
#图像分类模型
imgclassifier_model_path = ROOT_DIR / 'models' / 'imgclassifier.joblib'
imgclassifier_class_indices_path = ROOT_DIR / 'models' / 'class_indices.json'
#classifer.py参数
#tomato
find_reflection_threshold = 190
extract_g_r_factor = 1.5
#passion_fruit
hue_value = 37
hue_delta = 10
value_target = 25
value_delta = 10
#提取绿色像素参数
low_H = 0
low_S = 100
low_V = 0
high_H = 60
high_S = 180
high_V = 60
#spec_predict
#筛选谱段并未使用在qt取数据时已经筛选
selected_bands = [8, 9, 10, 48, 49, 50, 77, 80, 103, 108, 115, 143, 145]
#data_processing
#根据标定数据计算的参数,实际长度/像素长度单位cm
pixel_length_ratio = 6.3/425
#绿叶面积阈值,高于此阈值认为连通域是绿叶
area_threshold = 20000
#百香果密度g/cm^3
density = 0.652228972
#百香果面积比例每个像素代表的实际面积cm^2
area_ratio = 0.00021973702422145334
#def analyze_tomato
#s_l通道阈值
threshold_s_l = 180
threshold_fore_g_r_t = 20

View File

@ -0,0 +1,94 @@
# -*- coding: utf-8 -*-
# @Time : 2024/4/20 18:45
# @Author : TG
# @File : main.py
# @Software: PyCharm
import sys
import os
from root_dir import ROOT_DIR
from classifer import Spec_predict, Data_processing
import logging
from utils import Pipe
import numpy as np
from config import Config
import time
def main(is_debug=False):
setting = Config()
file_handler = logging.FileHandler(os.path.join(ROOT_DIR, 'tomato-passion_fruit.log'), encoding='utf-8')
file_handler.setLevel(logging.DEBUG if is_debug else logging.WARNING)
console_handler = logging.StreamHandler(sys.stdout)
console_handler.setLevel(logging.DEBUG if is_debug else logging.WARNING)
logging.basicConfig(format='%(asctime)s %(filename)s[line:%(lineno)d] - %(levelname)s - %(message)s',
handlers=[file_handler, console_handler],
level=logging.DEBUG)
#模型加载
detector = Spec_predict()
detector.load(path=setting.brix_model_path)
dp = Data_processing()
print('系统初始化中...')
#模型预热
#与qt_test测试时需要注释掉预热模型接收尺寸为253013qt_test发送的数据为3030224需要对数据进行切片classifer.py第379行
_ = detector.predict(np.ones((setting.n_spec_rows, setting.n_spec_cols, setting.n_spec_bands), dtype=np.uint16))
time.sleep(1)
print('系统初始化完成')
rgb_receive_name = r'\\.\pipe\rgb_receive'
rgb_send_name = r'\\.\pipe\rgb_send'
spec_receive_name = r'\\.\pipe\spec_receive'
pipe = Pipe(rgb_receive_name, rgb_send_name, spec_receive_name)
rgb_receive, rgb_send, spec_receive = pipe.create_pipes(rgb_receive_name, rgb_send_name, spec_receive_name)
# 预热循环只处理cmd为'YR'的数据
# 当接收到的第一个指令预热命令时,结束预热循环
while True:
data = pipe.receive_rgb_data(rgb_receive)
cmd, _ = pipe.parse_img(data)
if cmd == 'YR':
break
#主循环
q = 1
while True:
#RGB图像部分
images = []
cmd = None
for _ in range(5):
data = pipe.receive_rgb_data(rgb_receive)
cmd, img = pipe.parse_img(data)
#默认全为有果
prediction = 1
if prediction == 1:
images.append(img)
else:
response = pipe.send_data(cmd='KO', brix=0, diameter=0, green_percentage=0, weigth=0, defect_num=0,
total_defect_area=0, rp=np.zeros((100, 100, 3), dtype=np.uint8))
logging.info("图像中无果,跳过此图像")
continue
if cmd not in ['TO', 'PF', 'YR', 'KO']:
logging.error(f'错误指令,指令为{cmd}')
continue
#Spec数据部分
spec = None
if cmd == 'PF':
spec_data = pipe.receive_spec_data(spec_receive)
_, spec = pipe.parse_spec(spec_data)
#数据处理部分
if images: # 确保images不为空
response = dp.process_data(cmd, images, spec, pipe, detector)
if response:
logging.info(f'处理成功,响应为: {response}')
else:
logging.error('处理失败')
else:
logging.error("没有有效的图像进行处理")
print(f'{q}个果子处理完成')
q += 1
if __name__ == '__main__':
'''
python与qt采用windows下的命名管道进行通信数据流按照约定的通信协议进行
数据处理逻辑为连续接收5张RGB图然后根据解析出的指令部分决定是否接收一张光谱图然后进行处理最后将处理得到的指标结果进行编码回传
'''
main(is_debug=False)

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

View File

@ -0,0 +1,4 @@
{
"0": "exist",
"1": "no_exist"
}

Binary file not shown.

View File

@ -0,0 +1,197 @@
# -*- coding: utf-8 -*-
# @Time : 2024/6/16 17:13
# @Author : TG
# @File : qt_test.py
# @Software: PyCharm
import sys
import os
from PyQt5.QtWidgets import QApplication, QMainWindow, QFileDialog, QLabel, QVBoxLayout, QWidget
from PyQt5.QtGui import QPixmap, QImage
import win32file
from PIL import Image
import numpy as np
import cv2
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("Tomato Image Sender")
self.setGeometry(100, 100, 800, 600)
central_widget = QWidget()
self.setCentralWidget(central_widget)
layout = QVBoxLayout()
central_widget.setLayout(layout)
self.image_label = QLabel()
layout.addWidget(self.image_label)
self.rgb_send_name = r'\\.\pipe\rgb_receive' # 发送数据管道名对应 main.py 的接收数据管道名
self.rgb_receive_name = r'\\.\pipe\rgb_send' # 接收数据管道名对应 main.py 的发送数据管道名
self.spec_send_name = r'\\.\pipe\spec_receive' # 发送数据管道名对应 main.py 的接收数据管道名
# 连接main.py创建的命名管道
self.rgb_send = win32file.CreateFile(
self.rgb_send_name,
win32file.GENERIC_WRITE,
0,
None,
win32file.OPEN_EXISTING,
0,
None
)
self.rgb_receive = win32file.CreateFile(
self.rgb_receive_name,
win32file.GENERIC_READ,
0,
None,
win32file.OPEN_EXISTING,
0,
None
)
self.spec_send = win32file.CreateFile(
self.spec_send_name,
win32file.GENERIC_WRITE,
0,
None,
win32file.OPEN_EXISTING,
0,
None
)
def send_image_group(self, image_dir):
'''
发送图像数据
:param image_dir: bmp和raw文件所在文件夹
:return:
'''
rgb_files = [os.path.join(image_dir, f) for f in os.listdir(image_dir) if f.endswith(('.bmp'))][:5]
spec_files = [os.path.join(image_dir, f) for f in os.listdir(image_dir) if f.endswith('.raw')][:1]
self.send_YR()
for _ in range(5):
for image_path in rgb_files:
img = cv2.imread(image_path, cv2.IMREAD_COLOR)
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
img = np.asarray(img, dtype=np.uint8)
try:
# win32file.WriteFile(self.rgb_send, len(img_data).to_bytes(4, byteorder='big'))
height = img.shape[0]
width = img.shape[1]
height = height.to_bytes(2, byteorder='big')
width = width.to_bytes(2, byteorder='big')
img_data = img.tobytes()
length = (len(img_data) + 6).to_bytes(4, byteorder='big')
# cmd = 'TO'测试番茄数据cmd = 'PF':测试百香果数据
cmd = 'PF'
data_send = length + cmd.upper().encode('ascii') + height + width + img_data
win32file.WriteFile(self.rgb_send, data_send)
print(f'发送的图像数据长度: {len(data_send)}')
except Exception as e:
print(f"数据发送失败. 错误原因: {e}")
if spec_files:
spec_file = spec_files[0]
with open(spec_file, 'rb') as f:
spec_data = f.read()
try:
# win32file.WriteFile(self.spec_send, len(spec_data).to_bytes(4, byteorder='big'))
# print(f"发送的光谱数据长度: {len(spec_data)}")
heigth = 30
weight = 30
bands = 224
heigth = heigth.to_bytes(2, byteorder='big')
weight = weight.to_bytes(2, byteorder='big')
bands = bands.to_bytes(2, byteorder='big')
length = (len(spec_data)+8).to_bytes(4, byteorder='big')
# cmd = 'TO'测试番茄数据cmd = 'PF':测试百香果数据
cmd = 'PF'
data_send = length + cmd.upper().encode('ascii') + heigth + weight + bands + spec_data
win32file.WriteFile(self.spec_send, data_send)
print(f'发送的光谱数据长度: {len(data_send)}')
print(f'spec长度: {len(spec_data)}')
except Exception as e:
print(f"数据发送失败. 错误原因: {e}")
self.receive_result()
def send_YR(self):
'''
发送预热指令
:return:
'''
length = 2
length = length.to_bytes(4, byteorder='big')
cmd = 'YR'
data_send = length + cmd.upper().encode('ascii')
try:
win32file.WriteFile(self.rgb_send, data_send)
print("发送预热指令成功")
except Exception as e:
print(f"发送预热指令失败. 错误原因: {e}")
def receive_result(self):
try:
# 读取结果数据
# 读取4个字节的数据长度信息并将其转换为整数
data_length = int.from_bytes(win32file.ReadFile(self.rgb_receive, 4)[1], byteorder='big')
print(f"应该接收到的数据长度: {data_length}")
# 根据读取到的数据长度,读取对应长度的数据
data = win32file.ReadFile(self.rgb_receive, data_length)[1]
print(f"实际接收到的数据长度: {len(data)}")
# 解析数据
cmd_result = data[:2].decode('ascii').strip().upper()
brix = (int.from_bytes(data[2:4], byteorder='big')) / 1000
green_percentage = (int.from_bytes(data[4:5], byteorder='big')) / 100
diameter = (int.from_bytes(data[5:7], byteorder='big')) / 100
weight = int.from_bytes(data[7:8], byteorder='big')
defect_num = int.from_bytes(data[8:10], byteorder='big')
total_defect_area = (int.from_bytes(data[10:14], byteorder='big')) / 1000
heigth = int.from_bytes(data[14:16], byteorder='big')
width = int.from_bytes(data[16:18], byteorder='big')
rp = data[18:]
img = np.frombuffer(rp, dtype=np.uint8).reshape(heigth, width, -1)
print(f"指令:{cmd_result}, 糖度值:{brix}, 绿色占比:{green_percentage}, 直径:{diameter}cm, "
f"预估重量:{weight}g, 缺陷个数:{defect_num}, 缺陷面积:{total_defect_area}cm^2, 结果图的尺寸:{img.shape}")
# 显示结果图像
image = Image.fromarray(img)
qimage = QImage(image.tobytes(), image.size[0], image.size[1], QImage.Format_RGB888)
pixmap = QPixmap.fromImage(qimage)
self.image_label.setPixmap(pixmap)
except Exception as e:
print(f"数据接收失败. 错误原因: {e}")
def open_file_dialog(self):
directory_dialog = QFileDialog()
directory_dialog.setFileMode(QFileDialog.Directory)
if directory_dialog.exec_():
selected_directory = directory_dialog.selectedFiles()[0]
self.send_image_group(selected_directory)
if __name__ == "__main__":
'''
1. 创建Qt应用程序
2. 创建主窗口
3. 显示主窗口
4. 打开文件对话框
5. 进入Qt事件循环
'''
#运行main.py后运行qt_test.py
#运行qt_test.py后选择文件夹自动读取文件夹下的bmp和raw文件发送到main.py
#main.py接收到数据后返回结果数据qt_test.py接收到结果数据显示图片
#为确保测试正确测试文件夹下的文件数量应该为5个bmp文件和1个raw文件
app = QApplication(sys.argv)
main_window = MainWindow()
main_window.show()
main_window.open_file_dialog()
sys.exit(app.exec_())

View File

@ -0,0 +1,7 @@
joblib==1.2.0
numpy==1.25.0
Pillow==9.4.0
pywin32==305
scikit_learn==1.2.2
opencv-python==4.6.0.66
scikit-learn==1.2.2

View File

@ -0,0 +1,4 @@
import pathlib
file_path = pathlib.Path(__file__)
ROOT_DIR = file_path.parent

View File

@ -0,0 +1,260 @@
# -*- coding: utf-8 -*-
# @Time : 2024/4/20 18:24
# @Author : TG
# @File : utils.py
# @Software: PyCharm
import os
import win32file
import win32pipe
import time
import logging
import numpy as np
from config import Config as setting
class Pipe:
def __init__(self, rgb_receive_name, rgb_send_name, spec_receive_name):
self.rgb_receive_name = rgb_receive_name
self.rgb_send_name = rgb_send_name
self.spec_receive_name = spec_receive_name
self.rgb_receive = None
self.rgb_send = None
self.spec_receive = None
def create_pipes(self, rgb_receive_name, rgb_send_name, spec_receive_name):
while True:
try:
# 打开或创建命名管道
self.rgb_receive = win32pipe.CreateNamedPipe(
rgb_receive_name,
win32pipe.PIPE_ACCESS_INBOUND,
win32pipe.PIPE_TYPE_BYTE | win32pipe.PIPE_WAIT,
1, 80000000, 80000000, 0, None
)
self.rgb_send = win32pipe.CreateNamedPipe(
rgb_send_name,
win32pipe.PIPE_ACCESS_OUTBOUND, # 修改为输出模式
win32pipe.PIPE_TYPE_BYTE | win32pipe.PIPE_WAIT,
1, 80000000, 80000000, 0, None
)
self.spec_receive = win32pipe.CreateNamedPipe(
spec_receive_name,
win32pipe.PIPE_ACCESS_INBOUND,
win32pipe.PIPE_TYPE_BYTE | win32pipe.PIPE_WAIT,
1, 200000000, 200000000, 0, None
)
print("pipe管道创建成功等待连接...")
time.sleep(0.5)
print('Fruit1程序启动成功Fruit2程序正在启动中预计需要10秒')
time.sleep(0.5)
print('请勿关闭Fruit1程序否则Fruit2程序将无法正常运行')
time.sleep(0.5)
print('等待中..........')
# 等待发送端连接
win32pipe.ConnectNamedPipe(self.rgb_receive, None)
print("rgb_receive connected.")
# 等待发送端连接
win32pipe.ConnectNamedPipe(self.rgb_send, None)
print("rgb_send connected.")
win32pipe.ConnectNamedPipe(self.spec_receive, None)
print("spec_receive connected.")
print('Fruit2程序启动成功')
return self.rgb_receive, self.rgb_send, self.spec_receive
except Exception as e:
print(f"管道创建连接失败,失败原因: {e}")
print("等待5秒后重试...")
time.sleep(5)
continue
def receive_rgb_data(self, rgb_receive):
try:
# 读取图片数据长度
len_img = win32file.ReadFile(rgb_receive, 4, None)
data_size = int.from_bytes(len_img[1], byteorder='big')
# 读取实际图片数据
result, data = win32file.ReadFile(rgb_receive, data_size, None)
# 检查读取操作是否成功
if result != 0:
logging.error(f"读取失败,错误代码: {result}")
return None
# 返回成功读取的数据
return data
except Exception as e:
logging.error(f"数据接收失败,错误原因: {e}")
return None
def receive_spec_data(self, spec_receive):
try:
# 读取光谱数据长度
len_spec = win32file.ReadFile(spec_receive, 4, None)
data_size = int.from_bytes(len_spec[1], byteorder='big')
# 读取光谱数据
result, spec_data = win32file.ReadFile(spec_receive, data_size, None)
# 检查读取操作是否成功
if result != 0:
logging.error(f"读取失败,错误代码: {result}")
return None
# 返回成功读取的数据
return spec_data
except Exception as e:
logging.error(f"数据接收失败,错误原因: {e}")
return None
def parse_img(self, data: bytes) -> (str, any):
"""
图像数据转换.
:param data:接收到的报文
:return: 指令类型和内容
"""
try:
assert len(data) > 1
except AssertionError:
logging.error('指令转换失败长度不足2')
return '', None
cmd, data = data[:2], data[2:]
cmd = cmd.decode('ascii').strip().upper()
# 如果收到的是预热指令'YR'直接返回命令和None不处理图像数据
if cmd == 'YR':
return cmd, None
n_rows, n_cols, img = data[:2], data[2:4], data[4:]
try:
n_rows, n_cols = [int.from_bytes(x, byteorder='big') for x in [n_rows, n_cols]]
except Exception as e:
logging.error(f'长宽转换失败, 错误代码{e}, 报文大小: n_rows:{n_rows}, n_cols:{n_cols}')
return '', None
try:
assert n_rows * n_cols * 3 == len(img)
# 因为是float32类型 所以长度要乘12 如果是uint8则乘3
except AssertionError:
logging.error('图像指令转换失败,数据长度错误')
return '', None
img = np.frombuffer(img, dtype=np.uint8).reshape(n_rows, n_cols, -1)
return cmd, img
def parse_spec(self, data: bytes) -> (str, any):
"""
光谱数据转换.
:param data:接收到的报文
:return: 指令类型和内容
"""
try:
assert len(data) > 2
except AssertionError:
logging.error('指令转换失败长度不足3')
return '', None
cmd, data = data[:2], data[2:]
cmd = cmd.decode('ascii').strip().upper()
n_rows, n_cols, n_bands, spec = data[:2], data[2:4], data[4:6], data[6:]
try:
n_rows, n_cols, n_bands = [int.from_bytes(x, byteorder='big') for x in [n_rows, n_cols, n_bands]]
except Exception as e:
logging.error(f'长宽转换失败, 错误代码{e}, 报文大小: n_rows:{n_rows}, n_cols:{n_cols}, n_bands:{n_bands}')
return '', None
try:
assert n_rows * n_cols * n_bands * 2 == len(spec)
except AssertionError:
logging.error('图像指令转换失败,数据长度错误')
return '', None
spec = np.frombuffer(spec, dtype=np.uint16).reshape((n_rows, n_bands, -1)).transpose(0, 2, 1)
return cmd, spec
def send_data(self,cmd:str, brix, green_percentage, weight, diameter, defect_num, total_defect_area, rp):
'''
发送数据
:param cmd:
:param brix:
:param green_percentage:
:param weight:
:param diameter:
:param defect_num:
:param total_defect_area:
:param rp:
:return:
'''
cmd = cmd.strip().upper()
cmd_type = 'RE'
cmd_re = cmd_type.upper().encode('ascii')
img = np.asarray(rp, dtype=np.uint8) # 将图像转换为 NumPy 数组
height = img.shape[0] # 获取图像的高度
width = img.shape[1] # 获取图像的宽度
height = height.to_bytes(2, byteorder='big')
width = width.to_bytes(2, byteorder='big')
img_bytes = img.tobytes()
diameter = int(diameter * 100).to_bytes(2, byteorder='big')
defect_num = defect_num.to_bytes(2, byteorder='big')
total_defect_area = int(total_defect_area * 1000).to_bytes(4, byteorder='big')
length = len(img_bytes) + 18
length = length.to_bytes(4, byteorder='big')
if cmd == 'TO':
brix = 0
brix = brix.to_bytes(2, byteorder='big')
gp = int(green_percentage * 100).to_bytes(1, byteorder='big')
weight = 0
weight = weight.to_bytes(1, byteorder='big')
send_message = (length + cmd_re + brix + gp + diameter + weight +
defect_num + total_defect_area + height + width + img_bytes)
elif cmd == 'PF':
brix = int(brix * 1000).to_bytes(2, byteorder='big')
gp = int(green_percentage * 100).to_bytes(1, byteorder='big')
weight = weight.to_bytes(1, byteorder='big')
send_message = (length + cmd_re + brix + gp + diameter + weight +
defect_num + total_defect_area + height + width + img_bytes)
elif cmd == 'KO':
brix = 0
brix = brix.to_bytes(2, byteorder='big')
gp = 0
gp = gp.to_bytes(1, byteorder='big')
weight = 0
weight = weight.to_bytes(1, byteorder='big')
defect_num = 0
defect_num = defect_num.to_bytes(2, byteorder='big')
total_defect_area = 0
total_defect_area = total_defect_area.to_bytes(4, byteorder='big')
height = setting.n_rgb_rows
height = height.to_bytes(2, byteorder='big')
width = setting.n_rgb_cols
width = width.to_bytes(2, byteorder='big')
img_bytes = np.zeros((setting.n_rgb_rows, setting.n_rgb_cols, setting.n_rgb_bands),
dtype=np.uint8).tobytes()
length = (18).to_bytes(4, byteorder='big')
send_message = (length + cmd_re + brix + gp + diameter + weight +
defect_num + total_defect_area + height + width + img_bytes)
try:
win32file.WriteFile(self.rgb_send, send_message)
except Exception as e:
logging.error(f'发送指令失败,错误类型:{e}')
return False
return True
def create_file(file_name):
"""
创建文件
:param file_name: 文件名
:return: None
"""
if os.path.exists(file_name):
print("文件存在:%s" % file_name)
return False
# os.remove(file_name) # 删除已有文件
if not os.path.exists(file_name):
print("文件不存在,创建文件:%s" % file_name)
open(file_name, 'a').close()
return True
class Logger(object):
def __init__(self, is_to_file=False, path=None):
self.is_to_file = is_to_file
if path is None:
path = "tomato-passion_fruit.log"
self.path = path
create_file(path)
def log(self, content):
if self.is_to_file:
with open(self.path, "a") as f:
print(time.strftime("[%Y-%m-%d_%H-%M-%S]:"), file=f)
print(content, file=f)
else:
print(content)