feat:新增了qt_test;

docs:修改了通讯协议;
fix:修复了一系列bug;
test:已与qt_test测试完毕;
This commit is contained in:
TG 2024-06-17 01:30:00 +08:00
parent 3e620df4c6
commit 4c914af0eb
8 changed files with 122 additions and 73 deletions

View File

@ -299,37 +299,27 @@ class Passion_fruit:
return result
class Spec_predict(object):
def __init__(self, load_from=None, debug_mode=False, class_weight=None):
if load_from is None:
self.model = RandomForestRegressor(n_estimators=100)
else:
self.load(load_from)
self.log = utils.Logger(is_to_file=debug_mode)
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=None):
if path is None:
path = os.path.join(ROOT_DIR, 'models')
model_files = os.listdir(path)
if len(model_files) == 0:
self.log.log("No model found!")
return 1
self.log.log("./ Models Found:")
_ = [self.log.log("├--" + str(model_file)) for model_file in model_files]
file_times = [model_file[6:-2] for model_file in model_files]
latest_model = model_files[int(np.argmax(file_times))]
self.log.log("└--Using the latest model: " + str(latest_model))
path = os.path.join(ROOT_DIR, "models", str(latest_model))
def load(self, path):
if not os.path.isabs(path):
logging.warning('给的是相对路径')
return -1
self.log.log('Path is relative, converting to absolute path.')
path = os.path.abspath(path)
if not os.path.exists(path):
logging.warning('文件不存在')
return -1
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['model']
return 0
self.model = model_dic
self.log.log(f'Model loaded successfully from {path}')
def predict(self, data_x):
'''
@ -337,10 +327,13 @@ class Spec_predict(object):
:param data_x: 波段选择后的数据
:return: 预测结果二值化后的数据0为背景1为黄芪,2为杂质23为杂质14为甘草片5为红芪
'''
data_x = data_x.reshape(1, -1)
selected_bands = [8, 9, 10, 48, 49, 50, 77, 80, 103, 108, 115, 143, 145]
data_x = data_x[:, selected_bands]
data_y = self.model.predict(data_x)
return data_y
# def get_tomato_dimensions(edge_img):
# """
# 根据边缘二值化轮廓图,计算果子的长径、短径和长短径比值。
@ -484,10 +477,10 @@ class Data_processing:
float: 估算的西红柿体积
"""
density = 0.652228972
a = long_axis / 2
b = short_axis /2
a = ((long_axis / 535) * 6.3) / 2
b = ((short_axis /535) * 6.3) / 2
volume = 4 / 3 * np.pi * a * b * b
weigth = volume * density
weigth = round(volume * density)
return weigth
def analyze_tomato(self, img):
"""
@ -522,7 +515,7 @@ class Data_processing:
number_defects, total_pixels = self.analyze_defect(new_bin_img)
# 将处理后的图像转换为 RGB 格式
rp = cv2.cvtColor(org_defect, cv2.COLOR_BGR2RGB)
diameter = (long_axis + short_axis) / 2
diameter = (long_axis + short_axis) / 2
return diameter, green_percentage, number_defects, total_pixels, rp
def analyze_passion_fruit(self, img, hue_value=37, hue_delta=10, value_target=25, value_delta=10):
@ -537,7 +530,6 @@ class Data_processing:
combined_mask = pf.create_mask(hsv_image)
combined_mask = pf.apply_morphology(combined_mask)
max_mask = pf.find_largest_component(combined_mask)
contour_mask = self.contour_process(max_mask)
long_axis, short_axis = self.analyze_ellipse(contour_mask)
weigth = self.weight_estimates(long_axis, short_axis)

View File

@ -44,7 +44,7 @@ def process_data(cmd: str, images: list, spec: any, detector: Spec_predict) -> b
max_total_defect_area = max(max_total_defect_area, total_pixels)
if i == 1:
rp_result = rp
gp = green_percentage
gp = round(green_percentage)
elif cmd == 'PF':
# 百香果
@ -64,23 +64,27 @@ def process_data(cmd: str, images: list, spec: any, detector: Spec_predict) -> b
diameter = round(sum(diameter_axis_list) / 3)
if cmd == 'TO':
response = pipe.send_data(cmd=cmd, diameter=diameter, green_percentage=gp,
brix = 0
weigth = 0
response = pipe.send_data(cmd=cmd, brix=brix, diameter=diameter, green_percentage=gp, weigth=weigth,
defect_num=max_defect_num, total_defect_area=max_total_defect_area, rp=rp_result)
return response
elif cmd == 'PF':
green_percentage = 0
brix = detector.predict(spec)
response = pipe.send_data(cmd=cmd, brix=brix, diameter=diameter, weigth=weigth,
response = pipe.send_data(cmd=cmd, brix=brix, green_percentage=green_percentage, diameter=diameter, weigth=weigth,
defect_num=max_defect_num, total_defect_area=max_total_defect_area, rp=rp_result)
return response
return response
def main(is_debug=False):
file_handler = logging.FileHandler(os.path.join(ROOT_DIR, 'tomato.log'))
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] - %(levellevel)s - %(message)s',
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(ROOT_DIR/'20240529RGBtest3'/'models'/'passion_fruit.joblib')
detector = Spec_predict(ROOT_DIR/'models'/'passion_fruit_2.joblib')
while True:
images = []
@ -89,8 +93,14 @@ def main(is_debug=False):
for _ in range(5):
data = pipe.receive_rgb_data(rgb_receive)
cmd, img = pipe.parse_img(data)
# print(cmd, img.shape)
images.append(img)
# print(len(images))
if cmd not in ['TO', 'PF']:
logging.error(f'错误指令,指令为{cmd}')
continue
@ -99,6 +109,7 @@ def main(is_debug=False):
if cmd == 'PF':
spec_data = pipe.receive_spec_data(spec_receive)
_, spec = pipe.parse_spec(spec_data)
# print(spec.shape)
response = process_data(cmd, images, spec, detector)
if response:

View File

@ -13,6 +13,9 @@ import win32file
import struct
from PIL import Image
import io
import numpy as np
import cv2
class MainWindow(QMainWindow):
def __init__(self):
@ -69,12 +72,22 @@ class MainWindow(QMainWindow):
spec_files = [os.path.join(image_dir, f) for f in os.listdir(image_dir) if f.endswith('.raw')][:1]
for image_path in rgb_files:
with open(image_path, 'rb') as f:
img_data = f.read()
img = cv2.imread(image_path, cv2.IMREAD_COLOR)
img = np.asarray(img, dtype=np.uint8)
try:
win32file.WriteFile(self.rgb_send, len(img_data).to_bytes(4, byteorder='big'))
win32file.WriteFile(self.rgb_send, img_data)
# 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 = '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}")
@ -84,10 +97,20 @@ class MainWindow(QMainWindow):
spec_data = f.read()
try:
win32file.WriteFile(self.spec_send, len(spec_data).to_bytes(4, byteorder='big'))
print(f"发送的光谱数据长度: {len(spec_data)}")
win32file.WriteFile(self.spec_send, spec_data)
print(f'发送的光谱数据长度: {len(spec_data)}')
# 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 = '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}")
@ -96,17 +119,30 @@ class MainWindow(QMainWindow):
def receive_result(self):
try:
# 读取结果数据
long_axis = int.from_bytes(win32file.ReadFile(self.rgb_receive, 2)[1], byteorder='big')
short_axis = int.from_bytes(win32file.ReadFile(self.rgb_receive, 2)[1], byteorder='big')
defect_num = int.from_bytes(win32file.ReadFile(self.rgb_receive, 2)[1], byteorder='big')
total_defect_area = int.from_bytes(win32file.ReadFile(self.rgb_receive, 4)[1], byteorder='big')
len_img = int.from_bytes(win32file.ReadFile(self.rgb_receive, 4)[1], byteorder='big')
img_data = win32file.ReadFile(self.rgb_receive, len_img)[1]
# 读取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')
green_percentage = int.from_bytes(data[4:5], byteorder='big')
diameter = int.from_bytes(data[5:7], byteorder='big')
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')
heigth = int.from_bytes(data[14:16], byteorder='big')
width = int.from_bytes(data[16:18], byteorder='big')
rp = data[18:]
print(heigth, width)
img = np.frombuffer(rp, dtype=np.uint8).reshape(heigth, width, -1)
print(f"接收到的结果数据: {cmd_result}, {brix}, {green_percentage}, {diameter}, {weight}, {defect_num}, {total_defect_area}, {img.shape}")
print(f"长径: {long_axis}, 短径: {short_axis}, 缺陷个数: {defect_num}, 缺陷面积: {total_defect_area}")
# 显示结果图像
image = Image.open(io.BytesIO(img_data))
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)
@ -122,6 +158,13 @@ class MainWindow(QMainWindow):
self.send_image_group(selected_directory)
if __name__ == "__main__":
'''
1. 创建Qt应用程序
2. 创建主窗口
3. 显示主窗口
4. 打开文件对话框
5. 进入Qt事件循环
'''
app = QApplication(sys.argv)
main_window = MainWindow()
main_window.show()

View File

@ -67,13 +67,13 @@ class Pipe:
time.sleep(5)
continue
def receive_rgb_data(self):
def receive_rgb_data(self, rgb_receive):
try:
# 读取图片数据长度
len_img = win32file.ReadFile(self.rgb_receive, 4, None)
len_img = win32file.ReadFile(rgb_receive, 4, None)
data_size = int.from_bytes(len_img[1], byteorder='big')
# 读取实际图片数据
result, data = win32file.ReadFile(self.rgb_receive, data_size, None)
result, data = win32file.ReadFile(rgb_receive, data_size, None)
# 检查读取操作是否成功
if result != 0:
print(f"读取失败,错误代码: {result}")
@ -84,13 +84,13 @@ class Pipe:
print(f"数据接收失败,错误原因: {e}")
return None
def receive_spec_data(self):
def receive_spec_data(self, spec_receive):
try:
# 读取光谱数据长度
len_spec = win32file.ReadFile(self.spec_receive, 4, None)
len_spec = win32file.ReadFile(spec_receive, 4, None)
data_size = int.from_bytes(len_spec[1], byteorder='big')
# 读取光谱数据
result, spec_data = win32file.ReadFile(self.spec_receive, data_size, None)
result, spec_data = win32file.ReadFile(spec_receive, data_size, None)
# 检查读取操作是否成功
if result != 0:
print(f"读取失败,错误代码: {result}")
@ -128,7 +128,7 @@ class Pipe:
except AssertionError:
logging.error('图像指令IM转换失败数据长度错误')
return '', None
img = np.frombuffer(img, dtype=np.uint8).reshape((n_rows, n_cols, -1))
img = np.frombuffer(img, dtype=np.uint8).reshape(n_rows, n_cols, -1)
return cmd, img
def parse_spec(self, data: bytes) -> (str, any):
@ -153,12 +153,12 @@ class Pipe:
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 * 4 == len(spec)
assert n_rows * n_cols * n_bands * 2 == len(spec)
except AssertionError:
logging.error('图像指令转换失败,数据长度错误')
return '', None
spec = spec.reshape((n_rows, n_bands, -1)).transpose(0, 2, 1)
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, weigth, diameter, defect_num, total_defect_area, rp):
@ -193,17 +193,17 @@ class Pipe:
diameter = diameter.to_bytes(2, byteorder='big')
defect_num = defect_num.to_bytes(2, byteorder='big')
total_defect_area = int(total_defect_area).to_bytes(4, byteorder='big')
length = len(img_bytes) + 15
length = len(img_bytes) + 18
length = length.to_bytes(4, byteorder='big')
if cmd == 'TO':
brix = 0
brix = brix.to_bytes(1, byteorder='big')
brix = brix.to_bytes(2, byteorder='big')
gp = green_percentage.to_bytes(1, byteorder='big')
weigth = 0
weigth = weigth.to_bytes(1, byteorder='big')
send_message = length + cmd_re + brix + gp + diameter + weigth + defect_num + total_defect_area + height + width + img_bytes
elif cmd == 'PF':
brix = brix.to_bytes(1, byteorder='big')
brix = int(brix.item() * 1000).to_bytes(2, byteorder='big')
gp = 0
gp = gp.to_bytes(1, byteorder='big')
weigth = weigth.to_bytes(1, byteorder='big')
@ -212,6 +212,7 @@ class Pipe:
win32file.WriteFile(self.rgb_send, send_message)
time.sleep(0.01)
print('发送成功')
print(len(send_message), len(img_bytes))
# print(len(send_message))
except Exception as e:
logging.error(f'发送完成指令失败,错误类型:{e}')

View File

@ -55,7 +55,7 @@ def main():
for model_name, model in models.items():
model.fit(X_train, y_train)
if model_name == "RandomForest":
joblib.dump(model, '../models/random_forest_model_2.joblib')
joblib.dump(model, r'D:\project\supermachine--tomato-passion_fruit\20240529RGBtest3\models\passion_fruit_2.joblib')
mse, y_pred = evaluate_model(model, X_test, y_test)
print(f"Model: {model_name}")

View File

@ -60,10 +60,10 @@ def predict(model, data):
def main():
# 加载模型
model = load_model('../models/random_forest_model_2.joblib')
model = load_model(r'D:\project\supermachine--tomato-passion_fruit\20240529RGBtest3\models\passion_fruit.joblib')
# 读取数据
directory = '/Users/xs/PycharmProjects/super-tomato/baixiangguo/光谱数据3030/'
directory = r'D:\project\supermachine--tomato-passion_fruit\20240529RGBtest3\xs\光谱数据3030'
all_spectral_data = []
for i in range(1, 101):
hdr_path = os.path.join(directory, f'{i}.HDR')
@ -71,9 +71,11 @@ def main():
spectral_data = read_spectral_data(hdr_path, raw_path)
all_spectral_data.append(spectral_data)
all_spectral_data = np.stack(all_spectral_data)
print(all_spectral_data.shape)
# 预处理数据
data_prepared = prepare_data(all_spectral_data)
print(data_prepared.shape)
# 预测数据
predictions = predict(model, data_prepared)

View File

@ -51,7 +51,7 @@ def read_spectral_data(hdr_path, raw_path):
# Specify the directory containing the HDR and RAW files
directory = '/Users/xs/PycharmProjects/super-tomato/baixiangguo/光谱数据3030/'
directory = r'D:\project\supermachine--tomato-passion_fruit\20240529RGBtest3\xs\光谱数据3030'
# Initialize a list to hold all the spectral data arrays
all_spectral_data = []

View File

@ -52,11 +52,11 @@ $$
**返回结果数据包:'R''E'**`数据1`~`数据i`包含了糖度值Brix、颜色占比color、直径long、预估重量weight、缺陷个数num、缺陷面积area、结果图像的行数rows(高度)、列数cols(宽度)、以及结果图像的RGB数据组合方式为**糖度值+颜色占比+直径+预估重量+缺陷个数+缺陷面积+高度+宽度+RGB数据**
$$
i-15=rows \times cols \times 3
i-16=rows \times cols \times 3
$$
`数据1`~`数据i`的分布具体如下:
| 糖度值 | 颜色占比 | 直径2 | 直径1 | 预估重量 | 缺陷个数1 | 缺陷个数2 | 缺陷面积1 | 缺陷面积2 | 缺陷面积3 | 缺陷面积4 | 行数1 | 行数2 | 列数1 | 列数2 | 图像数据1 | ... | 图像数据(i-16) |
| :-: | :-: | :-: | :-: | :-: | :-: | :-: | :-: | :-: | :-: | :-: | :-: | :-: | :-: | :-: | --- | --- | --- |
| Brix[7:0] | color[7:0] | long[15:8] | long[7:0] | weight[7:0] | num[15:8] | num[7:0] | area[31:24] | area[23:16] | area[15:8] | area[7:0] | rows[15:8] | rows[7:0] | cols[15:8] | cols[7:0] | | ... | |
| 糖度值2 | 糖度值1 | 颜色占比 | 直径2 | 直径1 | 预估重量 | 缺陷个数1 | 缺陷个数2 | 缺陷面积1 | 缺陷面积2 | 缺陷面积3 | 缺陷面积4 | 行数1 | 行数2 | 列数1 | 列数2 | 图像数据1 | ... | 图像数据(i-16) |
| :-: | :-: | :-: | :-: | :-: | :-: | :-: | :-: | :-: | :-: | :-: | :-: | :-: | :-: | :-: | --- | --- | --- | --- |
| Brix[15:8] | Brix[7:0] | color[7:0] | long[15:8] | long[7:0] | weight[7:0] | num[15:8] | num[7:0] | area[31:24] | area[23:16] | area[15:8] | area[7:0] | rows[15:8] | rows[7:0] | cols[15:8] | cols[7:0] | | ... | |