feat:新增百香果rgb部分代码;

refactor:重构部分代码逻辑
This commit is contained in:
GG 2024-06-04 22:51:02 +08:00
parent ea62ca237c
commit 828015c206
20 changed files with 1514 additions and 415 deletions

84
.gitignore vendored Normal file
View File

@ -0,0 +1,84 @@
### JetBrains template
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
# User-specific stuff
.idea/**/workspace.xml
.idea/**/tasks.xml
.idea/**/usage.statistics.xml
.idea/**/dictionaries
.idea/**/shelf
# AWS User-specific
.idea/**/aws.xml
# Generated files
.idea/**/contentModel.xml
# Sensitive or high-churn files
.idea/**/dataSources/
.idea/**/dataSources.ids
.idea/**/dataSources.local.xml
.idea/**/sqlDataSources.xml
.idea/**/dynamic.xml
.idea/**/uiDesigner.xml
.idea/**/dbnavigator.xml
# Gradle
.idea/**/gradle.xml
.idea/**/libraries
# Gradle and Maven with auto-import
# When using Gradle or Maven with auto-import, you should exclude module files,
# since they will be recreated, and may cause churn. Uncomment if using
# auto-import.
# .idea/artifacts
# .idea/compiler.xml
# .idea/jarRepositories.xml
# .idea/modules.xml
# .idea/*.iml
# .idea/modules
# *.iml
# *.ipr
# CMake
cmake-build-*/
# Mongo Explorer plugin
.idea/**/mongoSettings.xml
# File-based project format
*.iws
# IntelliJ
out/
# mpeltonen/sbt-idea plugin
.idea_modules/
# JIRA plugin
atlassian-ide-plugin.xml
# Cursive Clojure plugin
.idea/replstate.xml
# SonarLint plugin
.idea/sonarlint/
# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
fabric.properties
# Editor-based Rest Client
.idea/httpRequests
# Android studio 3.1+ serialized cache file
.idea/caches/build_file_checksums.ser
!/20240529RGBtest3/data/
!/20240529RGBtest3/data/
!/20240410RGBtest1/super-tomato/defect_big.bmp
!/20240410RGBtest1/super-tomato/defect_mask.bmp
!/20240410RGBtest1/defect_big.bmp

1
.idea/.name generated Normal file
View File

@ -0,0 +1 @@
04average.ipynb

3
.idea/misc.xml generated
View File

@ -1,4 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Black">
<option name="sdkName" value="tengg" />
</component>
<component name="ProjectRootManager" version="2" project-jdk-name="tengg" project-jdk-type="Python SDK" />
</project>

6
.idea/other.xml generated Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="PySciProjectComponent">
<option name="PY_INTERACTIVE_PLOTS_SUGGESTED" value="true" />
</component>
</project>

View File

@ -1,7 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="PYTHON_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$" />
<content url="file://$MODULE_DIR$">
<excludeFolder url="file://$MODULE_DIR$/.idea/copilot/chatSessions" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>

View File

@ -214,7 +214,7 @@ def extract_max_connected_area(image, lower_hsv, upper_hsv):
def main():
parser = argparse.ArgumentParser(description='Process some integers.')
parser.add_argument('--dir_path', type=str, default=r'D:\project\Tomato\20240410RGBtest2\datatest',
parser.add_argument('--dir_path', type=str, default=r'D:\project\supermachine--tomato-passion_fruit\20240419RGBtest2\data',
help='the directory path of images')
parser.add_argument('--threshold_s_l', type=int, default=180,
help='the threshold for s_l')
@ -224,7 +224,7 @@ def main():
args = parser.parse_args()
for img_file in os.listdir(args.dir_path):
if img_file.endswith('.png'):
if img_file.endswith('.bmp'):
img_path = os.path.join(args.dir_path, img_file)
s_l = extract_s_l(img_path)
otsu_thresholded = otsu_threshold(s_l)

View File

@ -211,7 +211,7 @@ def extract_max_connected_area(image_path, lower_hsv, upper_hsv):
def main():
parser = argparse.ArgumentParser(description='Process some integers.')
parser.add_argument('--dir_path', type=str, default=r'D:\project\Tomato\20240410RGBtest2\data',
parser.add_argument('--dir_path', type=str, default=r'D:\project\supermachine--tomato-passion_fruit\20240419RGBtest2\data',
help='the directory path of images')
parser.add_argument('--threshold_s_l', type=int, default=180,
help='the threshold for s_l')

View File

@ -20,7 +20,10 @@ ASCII字符比如`类型1`为' '(空格)`类型2`为' '(空格)`类型3
- **RGB图像数据包' (空格)''(空格) ''I''M'**`数据1`~`数据i`包含了图像的行数rows(高度)、列数cols(宽度)、以及图像的RGB数据
$i-4=rows \times cols \times 3$具体如下:
$$
i-4=rows \times cols \times 3
$$
具体如下:
| 行数1 | 行数2 | 列数1 | 列数2 | 图像数据1 | ... | 图像数据(i-4) |
| :--------: | :-------: | :--------: | :-------: | :-------: | :--: | :-----------: |
@ -28,7 +31,11 @@ ASCII字符比如`类型1`为' '(空格)`类型2`为' '(空格)`类型3
**返回结果数据包' (空格)''R''I''M'**`数据1`~`数据i`包含了长径long、短径short、缺陷个数num、缺陷面积area、结果图像的行数rows(高度)、列数cols(宽度)、以及结果图像的RGB数据
$i-14=rows \times cols \times 3$具体如下:
$$
i-14=rows \times cols \times 3
$$
具体如下:
| 长径1 | 长径2 | 短径1 | 短径2 | 缺陷个数1 | 缺陷个数2 | 缺陷面积1 | 缺陷面积2 | 缺陷面积3 | 缺陷面积4 | 行数1 | 行数2 | 列数1 | 列数2 | 图像数据1 | 图像数据(i-14) |
| :-: | :-: | :-: | :-: | :-: | :-: | :-: | :-: | :-: | :-: | :-: | :-: | :-: | :-: | :-: | :-: |
@ -36,7 +43,10 @@ ASCII字符比如`类型1`为' '(空格)`类型2`为' '(空格)`类型3
- **光谱数据包' (空格)''(空格) ''S''P'**`数据1`~`数据i`包含了光谱数据的行数rows(高度)、列数cols(宽度)、谱段数bands、以及图像的光谱数据
$i-6=rows \times cols \times bands \times 4$具体如下:
$$
i-6=rows \times cols \times bands \times 4
$$
具体如下:
| 行数1 | 行数2 | 列数1 | 列数2 | 谱段1 | 谱段2 | 图像数据1 | ... | 图像数据(i-6) |
| :--------: | :-------: | :--------: | :-------: | :---------: | :--------: | :-------: | :--: | :-----------: |

View File

@ -0,0 +1,224 @@
# -*- coding: utf-8 -*-
# @Time : 2024/6/4 21:34
# @Author : GG
# @File : classifer.py
# @Software: PyCharm
import cv2
import numpy as np
class Tomato:
def __init__(self):
''' 初始化 Tomato 类。'''
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, threshold=190):
'''
通过阈值处理识别图像中的反射区域
:param image: 输入的单通道图像
:param threshold: 用于二值化的阈值
:return: 二值化后的图像高于阈值的部分为白色其余为黑色
'''
_, reflection = cv2.threshold(image, 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, 1.5), 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 fill_holes(self, bin_img):
'''
使用 floodFill 算法填充图像中的孔洞
: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_inv)
return img_filled
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

125
20240529RGBtest3/main.py Normal file
View File

@ -0,0 +1,125 @@
# -*- coding: utf-8 -*-
# @Time : 2024/4/20 18:45
# @Author : TG
# @File : main.py
# @Software: PyCharm
# -*- coding: utf-8 -*-
# @Time : 2024/4/12 15:04
# @Author : TG
# @File : main.py
# @Software: PyCharm
import socket
import sys
import numpy as np
import cv2
import root_dir
import time
import os
from root_dir import ROOT_DIR
import logging
from utils import create_pipes, receive_rgb_data, send_data, receive_spec_data, analyze_tomato
from collections import deque
import time
import io
from PIL import Image
import threading
import queue
def process_data(img: any) -> tuple:
"""
处理指令
:param cmd: 指令类型
:param data: 指令内容
:param connected_sock: socket
:param detector: 模型
:return: 是否处理成功
"""
# if cmd == 'IM':
long_axis, short_axis, number_defects, total_pixels, rp = analyze_tomato(img)
return long_axis, short_axis, number_defects, total_pixels, rp
## 20240423代码
def main(is_debug=False):
file_handler = logging.FileHandler(os.path.join(ROOT_DIR, 'report.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] - %(levelname)s - %(message)s',
handlers=[file_handler, console_handler],
level=logging.DEBUG)
rgb_receive_name = r'\\.\pipe\rgb_receive'
rgb_send_name = r'\\.\pipe\rgb_send'
spec_receive_name = r'\\.\pipe\spec_receive'
rgb_receive, rgb_send, spec_receive = create_pipes(rgb_receive_name, rgb_send_name, spec_receive_name)
# data_size = 15040566
while True:
long_axis_list = []
short_axis_list = []
defect_num_sum = 0
total_defect_area_sum = 0
rp = None
start_time = time.time()
for i in range(5):
# start_time = time.time()
img_data = receive_rgb_data(rgb_receive)
image = Image.open(io.BytesIO(img_data))
img = np.array(image)
print(img.shape)
# end_time = time.time()
# elapsed_time = end_time - start_time
# print(f'接收时间:{elapsed_time}秒')
long_axis, short_axis, number_defects, total_pixels, rp = process_data(img=img)
# print(long_axis, short_axis, number_defects, type(total_pixels), rp.shape)
if i <= 2:
long_axis_list.append(long_axis)
short_axis_list.append(short_axis)
if i == 1:
rp_result = rp
defect_num_sum += number_defects
total_defect_area_sum += total_pixels
long_axis = round(sum(long_axis_list) / 3)
short_axis = round(sum(short_axis_list) / 3)
# print(type(long_axis), type(short_axis), type(defect_num_sum), type(total_defect_area_sum), type(rp_result))
spec_data = receive_spec_data(spec_receive)
print(f'光谱数据接收长度:', len(spec_data))
response = send_data(pipe_send=rgb_send, long_axis=long_axis, short_axis=short_axis,
defect_num=defect_num_sum, total_defect_area=total_defect_area_sum, rp=rp_result)
end_time = time.time()
elapsed_time = (end_time - start_time) * 1000
print(f'总时间:{elapsed_time}毫秒')
print(long_axis, short_axis, defect_num_sum, total_defect_area_sum, rp_result.shape)
if __name__ == '__main__':
# 2个pipe管道
# 接收到图片 n_rows * n_cols * 3 uint8
# 发送long_axis, short_axis, defect_num_sum, total_defect_area_sum, rp_result
main(is_debug=False)

View File

@ -0,0 +1,79 @@
{
"cells": [
{
"cell_type": "code",
"id": "initial_id",
"metadata": {
"collapsed": true,
"ExecuteTime": {
"end_time": "2024-06-03T08:44:12.688855Z",
"start_time": "2024-06-03T08:44:07.661963Z"
}
},
"source": [
"import cv2\n",
"import numpy as np\n",
"\n",
"# 读取彩色图像,使用原始字符串处理文件路径\n",
"image = cv2.imread(r\"D:\\project\\supermachine--tomato-passion_fruit\\20240529RGBtest3\\data\\bad\\36.bmp\")\n",
"\n",
"# 将RGB图像转换到HSV颜色空间\n",
"hsv_image = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)\n",
"\n",
"# 将RGB图像转换到Lab颜色空间\n",
"lab_image = cv2.cvtColor(image, cv2.COLOR_BGR2LAB)\n",
"\n",
"# 提取S分量\n",
"s_channel = hsv_image[:,:,1]\n",
"\n",
"# 提取L分量\n",
"l_channel = lab_image[:,:,0]\n",
"\n",
"# 计算S+L图像\n",
"sl_image = cv2.addWeighted(s_channel, 0.5, l_channel, 0.5, 0)\n",
"\n",
"# 使用Otsu阈值分割\n",
"_, otsu_segmentation = cv2.threshold(sl_image, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)\n",
"\n",
"# 显示原始图像和分割结果\n",
"cv2.imshow(\"Original Image\", image)\n",
"cv2.imshow(\"Otsu Segmentation\", otsu_segmentation)\n",
"cv2.waitKey(0)\n",
"cv2.destroyAllWindows()\n",
"#存图\n",
"# cv2.imwrite(r\"D:\\project\\supermachine--tomato-passion_fruit\\20240529RGBtest3\\33_otsu.bmp\", otsu_segmentation)\n"
],
"outputs": [],
"execution_count": 5
},
{
"metadata": {},
"cell_type": "code",
"outputs": [],
"execution_count": null,
"source": "",
"id": "29d27b11f43683db"
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 2
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython2",
"version": "2.7.6"
}
},
"nbformat": 4,
"nbformat_minor": 5
}

292
20240529RGBtest3/test.py Normal file
View File

@ -0,0 +1,292 @@
import cv2
import numpy as np
import os
import argparse
# from svm import predict_image_array, load_model
#提取西红柿使用S+L的图像
def extract_s_l(image_path):
image = cv2.imread(image_path)
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(image_path, threshold=190):
# 读取图像
image = cv2.imread(image_path, cv2.IMREAD_GRAYSCALE)
# 应用阈值分割
_, reflection = cv2.threshold(image, threshold, 255, cv2.THRESH_BINARY)
return reflection
def otsu_threshold(image):
# 将图像转换为灰度图像
# gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# 使用Otsu阈值分割
_, binary = cv2.threshold(image, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
return binary
# 提取花萼使用G-R的图像
def extract_g_r(image):
# image = cv2.imread(image_path)
g_channel = image[:,:,1]
r_channel = image[:,:,2]
result = cv2.subtract(cv2.multiply(g_channel, 1.5), r_channel)
return result
#提取西红柿使用R-B的图像
def extract_r_b(image_path):
image = cv2.imread(image_path)
r_channel = image[:,:,2]
b_channel = image[:,:,0]
result = cv2.subtract(r_channel, b_channel)
return result
def extract_r_g(image_path):
image = cv2.imread(image_path)
r_channel = image[:,:,2]
g_channel = image[:,:,1]
result = cv2.subtract(r_channel, g_channel)
return result
def threshold_segmentation(image, threshold, color=255):
_, result = cv2.threshold(image, threshold, color, cv2.THRESH_BINARY)
return result
def bitwise_operation(image1, image2, operation='and'):
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(bin_img):
# 使用connectedComponentsWithStats函数找到连通区域
num_labels, labels, stats, _ = cv2.connectedComponentsWithStats(bin_img, connectivity=8)
# 找到最大的连通区域(除了背景)
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(bin_img, kernel_size=(5, 5)):
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, kernel_size)
closed_img = cv2.morphologyEx(bin_img, cv2.MORPH_CLOSE, kernel)
return closed_img
def open_operation(bin_img, kernel_size=(5, 5)):
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(original_img, bin_img):
bin_img_processed = close_operation(bin_img, kernel_size=(15, 15))
# cv2.imshow('Close Operation', bin_img_processed)
# bin_img_processed = open_operation(bin_img_processed, kernel_size=(19, 19))
# cv2.imshow('Open Operation', bin_img_processed)
# 现在使用处理后的bin_img_processed查找轮廓
contours, _ = cv2.findContours(bin_img_processed, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# 如果没有找到轮廓,直接返回原图
if not contours:
return original_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(original_img, bin_img):
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 fill_holes(bin_img):
# 复制 bin_img 到 img_filled
img_filled = bin_img.copy()
# 获取图像的高度和宽度
height, width = bin_img.shape
# 创建一个掩码,比输入图像大两个像素点
mask = np.zeros((height + 2, width + 2), np.uint8)
# 使用 floodFill 函数填充黑色区域
cv2.floodFill(img_filled, mask, (0, 0), 255)
# 反转填充后的图像
img_filled_d = cv2.bitwise_not(img_filled)
# 使用 bitwise_or 操作合并原图像和填充后的图像
img_filled = cv2.bitwise_or(bin_img, img_filled)
# 裁剪 img_filled 和 img_filled_d 到与 bin_img 相同的大小
# img_filled = img_filled[:height, :width]
img_filled_d = img_filled_d[:height, :width]
return img_filled, img_filled_d
def bitwise_and_rgb_with_binary(rgb_img, bin_img):
# 将二值图像转换为三通道图像
bin_img_3channel = cv2.cvtColor(bin_img, cv2.COLOR_GRAY2BGR)
# 使用 bitwise_and 操作合并 RGB 图像和二值图像
result = cv2.bitwise_and(rgb_img, bin_img_3channel)
return result
def extract_max_connected_area(image_path, lower_hsv, upper_hsv):
# 读取图像
image = cv2.imread(image_path)
# 将图像从BGR转换到HSV
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
# 复制 new_bin_img 到 img_filled
img_filled = new_bin_img.copy()
# 获取图像的高度和宽度
height, width = new_bin_img.shape
# 创建一个掩码,比输入图像大两个像素点
mask = np.zeros((height + 2, width + 2), np.uint8)
# 使用 floodFill 函数填充黑色区域
cv2.floodFill(img_filled, mask, (0, 0), 255)
# 反转填充后的图像
img_filled_inv = cv2.bitwise_not(img_filled)
# 使用 bitwise_or 操作合并原图像和填充后的图像
img_filled = cv2.bitwise_or(new_bin_img, img_filled_inv)
return img_filled
def main():
parser = argparse.ArgumentParser(description='Process some integers.')
parser.add_argument('--dir_path', type=str, default=r'D:\project\supermachine--tomato-passion_fruit\20240529RGBtest3\data\broken',
help='the directory path of images')
parser.add_argument('--threshold_s_l', type=int, default=180,
help='the threshold for s_l')
parser.add_argument('--threshold_r_b', type=int, default=15,
help='the threshold for r_b')
args = parser.parse_args()
for img_file in os.listdir(args.dir_path):
if img_file.endswith('.bmp'):
img_path = os.path.join(args.dir_path, img_file)
s_l = extract_s_l(img_path)
otsu_thresholded = otsu_threshold(s_l)
img_fore = bitwise_and_rgb_with_binary(cv2.imread(img_path), otsu_thresholded)
img_fore_defect = extract_g_r(img_fore)
img_fore_defect = threshold_segmentation(img_fore_defect, args.threshold_r_b)
# cv2.imshow('img_fore_defect', img_fore_defect)
thresholded_s_l = threshold_segmentation(s_l, args.threshold_s_l)
new_bin_img = largest_connected_component(thresholded_s_l)
# zhongggggg = cv2.bitwise_or(new_bin_img, cv2.imread('defect_mask.bmp', cv2.IMREAD_GRAYSCALE))
# cv2.imshow('zhongggggg', zhongggggg)
new_otsu_bin_img = largest_connected_component(otsu_thresholded)
filled_img, defect = fill_holes(new_bin_img)
defect = bitwise_and_rgb_with_binary(cv2.imread(img_path), defect)
cv2.imshow('defect', defect)
edge, mask = draw_tomato_edge(cv2.imread(img_path), new_bin_img)
org_defect = bitwise_and_rgb_with_binary(edge, new_bin_img)
fore = bitwise_and_rgb_with_binary(cv2.imread(img_path), mask)
fore_g_r_t = threshold_segmentation(extract_g_r(fore), 20)
fore_g_r_t_ture = bitwise_and_rgb_with_binary(cv2.imread(img_path), fore_g_r_t)
cv2.imwrite('defect_big.bmp', fore_g_r_t_ture)
res = cv2.bitwise_or(new_bin_img, fore_g_r_t)
white = find_reflection(img_path)
# SVM预测
# 加载模型
# model, scaler = load_model('/Users/xs/PycharmProjects/super-tomato/svm_green.joblib')
# 对图像进行预测
# predicted_mask = predict_image_array(image, model, scaler)
cv2.imshow('white', white)
cv2.imshow('fore', fore)
cv2.imshow('fore_g_r_t', fore_g_r_t)
cv2.imshow('mask', mask)
print('mask', mask.shape)
print('filled', filled_img.shape)
print('largest', new_bin_img.shape)
print('rp', org_defect.shape)
cv2.imshow('res', res)
# 显示原始图像
original_img = cv2.imread(img_path)
cv2.imshow('Original', original_img)
cv2.imshow('thresholded_s_l', thresholded_s_l)
cv2.imshow('Largest Connected Component', new_bin_img)
cv2.imshow('Filled', filled_img)
cv2.imshow('Defect', defect)
cv2.imshow('Org_defect', org_defect)
cv2.imshow('otsu_thresholded', new_otsu_bin_img)
#显示轮廓
cv2.imshow('Edge', edge)
# 等待用户按下任意键
cv2.waitKey(0)
# 关闭所有窗口
cv2.destroyAllWindows()
if __name__ == '__main__':
main()

280
20240529RGBtest3/utils.py Normal file
View File

@ -0,0 +1,280 @@
# -*- coding: utf-8 -*-
# @Time : 2024/4/20 18:24
# @Author : TG
# @File : utils.py
# @Software: PyCharm
import time
import logging
import numpy as np
import shutil
import cv2
import os
from scipy.ndimage.measurements import label, find_objects
import win32pipe
import win32file
import io
from PIL import Image
import select
import msvcrt
from classifer import Tomato
def receive_rgb_data(pipe):
try:
# 读取图片数据
len_img = win32file.ReadFile(pipe, 4, None)
data_size = int.from_bytes(len_img[1], byteorder='big')
result, img_data = win32file.ReadFile(pipe, data_size, None)
return img_data
except Exception as e:
print(f"数据接收失败,错误原因: {e}")
return None
def receive_spec_data(pipe):
try:
# 读取图片数据长度
len_spec = win32file.ReadFile(pipe, 4, None)
if len_spec is None:
# 未能读取到数据长度,返回"0"
return "0"
data_size = int.from_bytes(len_spec[1], byteorder='big')
if data_size == 0:
# 接收到空数据,返回"0"
return "0"
# 读取图片数据
result, spec_data = win32file.ReadFile(pipe, data_size, None)
return spec_data
except Exception as e:
print(f"数据接收失败,错误原因: {e}")
return '0'
def create_pipes(rgb_receive_name, rgb_send_name, spec_receive_name):
while True:
try:
# 打开或创建命名管道
rgb_receive = win32pipe.CreateNamedPipe(
rgb_receive_name,
win32pipe.PIPE_ACCESS_INBOUND,
win32pipe.PIPE_TYPE_BYTE | win32pipe.PIPE_WAIT,
1, 80000000, 80000000, 0, None
)
rgb_send = win32pipe.CreateNamedPipe(
rgb_send_name,
win32pipe.PIPE_ACCESS_OUTBOUND, # 修改为输出模式
win32pipe.PIPE_TYPE_BYTE | win32pipe.PIPE_WAIT,
1, 80000000, 80000000, 0, None
)
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管道创建成功等待连接...")
# 等待发送端连接
win32pipe.ConnectNamedPipe(rgb_receive, None)
print("rgb_receive connected.")
# 等待发送端连接
win32pipe.ConnectNamedPipe(rgb_send, None)
print("rgb_send connected.")
win32pipe.ConnectNamedPipe(rgb_receive, None)
print("spec_receive connected.")
return rgb_receive, rgb_send, spec_receive
except Exception as e:
print(f"管道创建连接失败,失败原因: {e}")
print("等待5秒后重试...")
time.sleep(5)
continue
def send_data(pipe_send, long_axis, short_axis, defect_num, total_defect_area, rp):
# start_time = time.time()
#
rp1 = Image.fromarray(rp.astype(np.uint8))
# cv2.imwrite('rp1.bmp', rp1)
# 将 Image 对象保存到 BytesIO 流中
img_bytes = io.BytesIO()
rp1.save(img_bytes, format='BMP')
img_bytes = img_bytes.getvalue()
# width = rp.shape[0]
# height = rp.shape[1]
# print(width, height)
# img_bytes = rp.tobytes()
# length = len(img_bytes) + 18
# print(length)
# length = length.to_bytes(4, byteorder='big')
# width = width.to_bytes(2, byteorder='big')
# height = height.to_bytes(2, byteorder='big')
print(f'原始长度:', len(rp.tobytes()))
print(f'发送长度:', len(img_bytes))
long_axis = long_axis.to_bytes(2, byteorder='big')
short_axis = short_axis.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) + 4).to_bytes(4, byteorder='big')
# cmd_type = 'RIM'
# result = result.encode('ascii')
# send_message = b'\xaa' + length + (' ' + cmd_type).upper().encode('ascii') + long_axis + short_axis + defect_num + total_defect_area + width + height + img_bytes + b'\xff\xff\xbb'
# send_message = long_axis + short_axis + defect_num + total_defect_area + img_bytes
send_message = long_axis + short_axis + defect_num + total_defect_area + length + img_bytes
# print(long_axis)
# print(short_axis)
# print(defect_num)
# print(total_defect_area)
# print(width)
# print(height)
try:
win32file.WriteFile(pipe_send, send_message)
time.sleep(0.01)
print('发送成功')
# print(len(send_message))
except Exception as e:
logging.error(f'发送完成指令失败,错误类型:{e}')
return False
# end_time = time.time()
# print(f'发送时间:{end_time - start_time}秒')
return True
def mkdir_if_not_exist(dir_name, is_delete=False):
"""
创建文件夹
:param dir_name: 文件夹
:param is_delete: 是否删除
:return: 是否成功
"""
try:
if is_delete:
if os.path.exists(dir_name):
shutil.rmtree(dir_name)
print('[Info] 文件夹 "%s" 存在, 删除文件夹.' % dir_name)
if not os.path.exists(dir_name):
os.makedirs(dir_name)
print('[Info] 文件夹 "%s" 不存在, 创建文件夹.' % dir_name)
return True
except Exception as e:
print('[Exception] %s' % e)
return False
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.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)
def get_tomato_dimensions(edge_img):
"""
根据边缘二值化轮廓图,计算果子的长径短径和长短径比值
使用最小外接矩形和最小外接圆两种方法
参数:
edge_img (numpy.ndarray): 边缘二值化轮廓图,背景为黑色,番茄区域为白色
返回:
tuple: (长径, 短径, 长短径比值)
"""
if edge_img is None or edge_img.any() == 0:
return (0, 0)
# 最小外接矩形
rect = cv2.minAreaRect(cv2.findContours(edge_img, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)[0][0])
major_axis, minor_axis = rect[1]
# aspect_ratio = max(major_axis, minor_axis) / min(major_axis, minor_axis)
# # 最小外接圆
# (x, y), radius = cv2.minEnclosingCircle(
# cv2.findContours(edge_img, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)[0][0])
# diameter = 2 * radius
# aspect_ratio_circle = 1.0
return (max(major_axis, minor_axis), min(major_axis, minor_axis))
def get_defect_info(defect_img):
"""
根据区域缺陷二值化轮廓图,计算缺陷区域的个数和总面积
参数:
defect_img (numpy.ndarray): 番茄区域缺陷二值化轮廓图,背景为黑色,番茄区域为白色,缺陷区域为黑色连通域
返回:
tuple: (缺陷区域个数, 缺陷区域像素面积缺陷像素总面积)
"""
# 检查输入是否为空
if defect_img is None or defect_img.any() == 0:
return (0, 0)
nb_components, output, stats, centroids = cv2.connectedComponentsWithStats(defect_img, connectivity=4)
max_area = max(stats[i, cv2.CC_STAT_AREA] for i in range(1, nb_components))
areas = []
for i in range(1, nb_components):
area = stats[i, cv2.CC_STAT_AREA]
if area != max_area:
areas.append(area)
number_defects = len(areas)
total_pixels = sum(areas)
return number_defects, total_pixels
def analyze_tomato(img):
"""
分析给定图像提取和返回西红柿的长径短径缺陷数量和缺陷总面积并返回处理后的图像
使用 Tomoto 类的图像处理方法以及自定义的尺寸和缺陷信息获取函数
参数:
img (numpy.ndarray): 输入的 BGR 图像
返回:
tuple: (长径, 短径, 缺陷区域个数, 缺陷区域总像素, 处理后的图像)
"""
tomato = Tomato() # 创建 Tomato 类的实例
# 设置 S-L 通道阈值并处理图像
threshold_s_l = 180
s_l = tomato.extract_s_l(img)
thresholded_s_l = tomato.threshold_segmentation(s_l, threshold_s_l)
new_bin_img = tomato.largest_connected_component(thresholded_s_l)
# 绘制西红柿边缘并获取缺陷信息
edge, mask = tomato.draw_tomato_edge(img, new_bin_img)
org_defect = tomato.bitwise_and_rgb_with_binary(edge, new_bin_img)
# 获取西红柿的尺寸信息
long_axis, short_axis = get_tomato_dimensions(mask)
# 获取缺陷信息
number_defects, total_pixels = get_defect_info(new_bin_img)
# 将处理后的图像转换为 RGB 格式
rp = cv2.cvtColor(org_defect, cv2.COLOR_BGR2RGB)
return long_axis, short_axis, number_defects, total_pixels, rp

View File

@ -0,0 +1,64 @@
import numpy as np
import matplotlib.pyplot as plt
from sklearn.ensemble import RandomForestRegressor, GradientBoostingRegressor
from sklearn.svm import SVR
from sklearn.neighbors import KNeighborsRegressor
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_squared_error
from spec_read import all_spectral_data
def prepare_data(data):
"""Reshape data and select specified spectral bands."""
reshaped_data = data.reshape(100, -1)
selected_bands = [1, 2, 3, 58, 59, 60, 106, 107, 108, 112, 113, 114, 142, 146, 200, 201, 202]
return reshaped_data[:, selected_bands]
def split_data(X, y, test_size=0.20, random_state=1):
"""Split data into training and test sets."""
return train_test_split(X, y, test_size=test_size, random_state=random_state)
def evaluate_model(model, X_test, y_test):
"""Evaluate the model and return MSE and predictions."""
y_pred = model.predict(X_test)
mse = mean_squared_error(y_test, y_pred)
return mse, y_pred
def print_predictions(y_test, y_pred, model_name):
"""Print actual and predicted values."""
print(f"Test Set Predictions for {model_name}:")
for i, (real, pred) in enumerate(zip(y_test, y_pred)):
print(f"Sample {i + 1}: True Value = {real:.2f}, Predicted Value = {pred:.2f}")
def main():
sweetness_acidity = np.array([
16.2, 16.1, 17, 16.9, 16.8, 17.8, 18.1, 17.2, 17, 17.2, 17.1, 17.2,
17.2, 17.2, 18.1, 17, 17.6, 17.4, 17.1, 17.1, 16.9, 17.6, 17.3, 16.3,
16.5, 18.7, 17.6, 16.2, 16.8, 17.2, 16.8, 17.3, 16, 16.6, 16.7, 16.7,
17.3, 16.3, 16.8, 17.4, 17.3, 16.3, 16.1, 17.2, 18.6, 16.8, 16.1, 17.2,
18.3, 16.5, 16.6, 17, 17, 17.8, 16.4, 18, 17.7, 17, 18.3, 16.8, 17.5,
17.7, 18.5, 18, 17.7, 17, 18.3, 18.1, 17.4, 17.7, 17.8, 16.3, 17.1, 16.8,
17.2, 17.5, 16.6, 17.7, 17.1, 17.7, 19.4, 20.3, 17.3, 15.8, 18, 17.7,
17.2, 15.2, 18, 18.4, 18.3, 15.7, 17.2, 18.6, 15.6, 17, 16.9, 17.4, 17.8,
16.5
])
X = prepare_data(all_spectral_data)
X_train, X_test, y_train, y_test = split_data(X, sweetness_acidity)
models = {
"RandomForest": RandomForestRegressor(n_estimators=100),
"GradientBoosting": GradientBoostingRegressor(n_estimators=100),
"SVR": SVR(kernel='rbf', C=100, gamma=0.1, epsilon=.1),
"KNeighbors": KNeighborsRegressor(n_neighbors=5)
}
for model_name, model in models.items():
model.fit(X_train, y_train)
mse, y_pred = evaluate_model(model, X_test, y_test)
print(f"Model: {model_name}")
print(f"Mean Squared Error on the test set: {mse}")
print_predictions(y_test, y_pred, model_name)
print("\n" + "-"*50 + "\n")
if __name__ == "__main__":
main()

View File

@ -0,0 +1,89 @@
import cv2
import numpy as np
import matplotlib.pyplot as plt
import os
def create_mask(hsv_image, hue_value, hue_delta, value_target, value_delta):
# 创建H通道阈值掩码
lower_hue = np.array([hue_value - hue_delta, 0, 0])
upper_hue = np.array([hue_value + 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, value_target - value_delta])
lower_value_2 = np.array([0, 0, value_target + 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)
cv2.imshow('value_mask_1', value_mask_1)
value_mask_2 = cv2.inRange(hsv_image, lower_value_2, upper_value_2)
cv2.imshow('value_mask_2', value_mask_2)
value_mask = cv2.bitwise_and(value_mask_1, value_mask_2)
cv2.imshow('value_mask', value_mask)
# 等待用户按下任意键
cv2.waitKey(0)
# 关闭所有窗口
cv2.destroyAllWindows()
# 合并H通道和V通道掩码
return cv2.bitwise_and(hue_mask, value_mask)
def apply_morphology(mask):
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5, 5))
return cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernel)
def find_largest_component(mask):
num_labels, labels, stats, centroids = cv2.connectedComponentsWithStats(mask, 4, cv2.CV_32S)
if num_labels < 2:
return None # No significant components found
max_label = 1 + np.argmax(stats[1:, cv2.CC_STAT_AREA]) # Skip background
return (labels == max_label).astype(np.uint8) * 255
def process_image(image_path, hue_value=37, hue_delta=10, value_target=25, value_delta=10):
image = cv2.imread(image_path)
if image is None:
print(f"Error: Image at {image_path} could not be read.")
return None
hsv_image = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
combined_mask = create_mask(hsv_image, hue_value, hue_delta, value_target, value_delta)
combined_mask = apply_morphology(combined_mask)
max_mask = find_largest_component(combined_mask)
cv2.imshow('max_mask', max_mask)
# 等待用户按下任意键
cv2.waitKey(0)
# 关闭所有窗口
cv2.destroyAllWindows()
if max_mask is None:
print(f"No significant components found in {image_path}.")
return None
result_image = cv2.bitwise_and(image, image, mask=max_mask)
result_image[max_mask == 0] = [255, 255, 255] # Set background to white
return result_image
def save_image(image, output_path):
cv2.imwrite(output_path, image)
def process_images_in_folder(input_folder, output_folder):
if not os.path.exists(output_folder):
os.makedirs(output_folder)
for filename in os.listdir(input_folder):
if filename.lower().endswith(".bmp"):
image_path = os.path.join(input_folder, filename)
result_image = process_image(image_path)
if result_image is not None:
output_path = os.path.join(output_folder, filename)
save_image(result_image, output_path)
print(f"Processed and saved {filename} to {output_folder}.")
# 主函数调用
input_folder = r'D:\project\supermachine--tomato-passion_fruit\20240529RGBtest3\data\passion_fruit_img' # 替换为你的输入文件夹路径
output_folder = r'D:\project\supermachine--tomato-passion_fruit\20240529RGBtest3\data\01' # 替换为你的输出文件夹路径
process_images_in_folder(input_folder, output_folder)

View File

@ -0,0 +1,71 @@
import numpy as np
import matplotlib.pyplot as plt
from sklearn.ensemble import RandomForestRegressor
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_squared_error
from spec_read import all_spectral_data
def prepare_data(data):
"""Calculate the average spectral values for each fruit across all pixels."""
return np.mean(data, axis=(1, 2))
def train_model(X, y):
"""Train a RandomForest model."""
rf = RandomForestRegressor(n_estimators=100)
rf.fit(X, y)
return rf
def split_data(X, y, test_size=0.20, random_state=42):
"""Split data into training and test sets."""
return train_test_split(X, y, test_size=test_size, random_state=random_state)
def evaluate_model(model, X_test, y_test):
"""Evaluate the model and return MSE and predictions."""
y_pred = model.predict(X_test)
mse = mean_squared_error(y_test, y_pred)
return mse, y_pred
def print_predictions(y_test, y_pred):
"""Print actual and predicted values."""
print("Test Set Predictions:")
for i, (real, pred) in enumerate(zip(y_test, y_pred)):
print(f"Sample {i + 1}: True Value = {real:.2f}, Predicted Value = {pred:.2f}")
def plot_spectra(X, y):
"""Plot the average spectra for all samples and annotate with sweetness_acidity values."""
plt.figure(figsize=(10, 6))
for i in range(X.shape[0]):
plt.plot(X[i], label=f'Sample {i+1}')
plt.annotate(f'{y[i]:.1f}', xy=(len(X[i])-1, X[i][-1]), xytext=(5, 0),
textcoords='offset points', ha='left', va='center')
plt.xlabel('Wavelength Index')
plt.ylabel('Average Spectral Value')
plt.title('Average Spectral Curves for All Samples')
plt.legend(loc='upper right', bbox_to_anchor=(1.1, 1.05))
plt.show()
def main():
sweetness_acidity = np.array([
16.2, 16.1, 17, 16.9, 16.8, 17.8, 18.1, 17.2, 17, 17.2, 17.1, 17.2,
17.2, 17.2, 18.1, 17, 17.6, 17.4, 17.1, 17.1, 16.9, 17.6, 17.3, 16.3,
16.5, 18.7, 17.6, 16.2, 16.8, 17.2, 16.8, 17.3, 16, 16.6, 16.7, 16.7,
17.3, 16.3, 16.8, 17.4, 17.3, 16.3, 16.1, 17.2, 18.6, 16.8, 16.1, 17.2,
18.3, 16.5, 16.6, 17, 17, 17.8, 16.4, 18, 17.7, 17, 18.3, 16.8, 17.5,
17.7, 18.5, 18, 17.7, 17, 18.3, 18.1, 17.4, 17.7, 17.8, 16.3, 17.1, 16.8,
17.2, 17.5, 16.6, 17.7, 17.1, 17.7, 19.4, 20.3, 17.3, 15.8, 18, 17.7,
17.2, 15.2, 18, 18.4, 18.3, 15.7, 17.2, 18.6, 15.6, 17, 16.9, 17.4, 17.8,
16.5
])
X = prepare_data(all_spectral_data)
plot_spectra(X, sweetness_acidity) # 绘制光谱曲线并添加标注
X_train, X_test, y_train, y_test = split_data(X, sweetness_acidity)
rf_model = train_model(X_train, y_train)
mse, y_pred = evaluate_model(rf_model, X_test, y_test)
print("Transformed data shape:", X_train.shape)
print("Mean Squared Error on the test set:", mse)
print_predictions(y_test, y_pred)
if __name__ == "__main__":
main()

View File

@ -0,0 +1,70 @@
import cv2
import numpy as np
import matplotlib.pyplot as plt
def dual_threshold_and_max_component(image_path, hue_value=37, hue_delta=10, value_target=30, value_delta=10):
# 读取图像
image = cv2.imread(image_path)
# 检查图像是否读取成功
if image is None:
print("Error: Image could not be read.")
return
# 将图像从BGR转换到HSV色彩空间
hsv_image = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
# 创建H通道阈值掩码
lower_hue = np.array([hue_value - hue_delta, 0, 0])
upper_hue = np.array([hue_value + 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, value_target - value_delta])
lower_value_2 = np.array([0, 0, value_target + 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_2 = cv2.inRange(hsv_image, lower_value_2, upper_value_2)
value_mask = cv2.bitwise_or(value_mask_1, value_mask_2)
# 合并H通道和V通道掩码
combined_mask = cv2.bitwise_and(hue_mask, value_mask)
# 形态学操作 - 开运算,去除小的粘连
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5, 5))
combined_mask = cv2.morphologyEx(combined_mask, cv2.MORPH_OPEN, kernel)
# 连通域分析
num_labels, labels, stats, centroids = cv2.connectedComponentsWithStats(combined_mask, 4, cv2.CV_32S)
# 找出最大的连通区域(除了背景)
max_label = 1 + np.argmax(stats[1:, cv2.CC_STAT_AREA]) # 跳过背景
max_mask = (labels == max_label).astype(np.uint8) * 255
# 使用掩码生成结果图像
result_image = cv2.bitwise_and(image, image, mask=max_mask)
# 设置背景为白色
result_image[max_mask == 0] = [255, 255, 255]
# 将结果图像从BGR转换到RGB以便正确显示
result_image_rgb = cv2.cvtColor(result_image, cv2.COLOR_BGR2RGB)
# 使用matplotlib显示原始图像和结果图像
plt.figure(figsize=(10, 5))
plt.subplot(1, 2, 1)
plt.imshow(cv2.cvtColor(image, cv2.COLOR_BGR2RGB))
plt.title('Original Image')
plt.axis('off')
plt.subplot(1, 2, 2)
plt.imshow(result_image_rgb)
plt.title('Largest Connected Component on White Background')
plt.axis('off')
plt.show()
# 使用函数
image_path = r'D:\project\supermachine--tomato-passion_fruit\20240529RGBtest3\data\passion_fruit_img\50.bmp' # 替换为你的图片路径
dual_threshold_and_max_component(image_path)

View File

@ -0,0 +1,70 @@
import numpy as np
import os
def read_spectral_data(hdr_path, raw_path):
# Read HDR file for image dimensions information
with open(hdr_path, 'r', encoding='latin1') as hdr_file:
lines = hdr_file.readlines()
height = width = bands = 0
for line in lines:
if line.startswith('lines'):
height = int(line.split()[-1])
elif line.startswith('samples'):
width = int(line.split()[-1])
elif line.startswith('bands'):
bands = int(line.split()[-1])
# Read spectral data from RAW file
raw_image = np.fromfile(raw_path, dtype='uint16')
# Initialize the image with the actual read dimensions
formatImage = np.zeros((height, width, bands))
for row in range(height):
for dim in range(bands):
formatImage[row, :, dim] = raw_image[(dim + row * bands) * width:(dim + 1 + row * bands) * width]
# Ensure the image is 30x30x224 by cropping or padding
target_height, target_width, target_bands = 30, 30, 224
# Crop or pad height
if height > target_height:
formatImage = formatImage[:target_height, :, :]
elif height < target_height:
pad_height = target_height - height
formatImage = np.pad(formatImage, ((0, pad_height), (0, 0), (0, 0)), mode='constant', constant_values=0)
# Crop or pad width
if width > target_width:
formatImage = formatImage[:, :target_width, :]
elif width < target_width:
pad_width = target_width - width
formatImage = np.pad(formatImage, ((0, 0), (0, pad_width), (0, 0)), mode='constant', constant_values=0)
# Crop or pad bands if necessary (usually bands should not change)
if bands > target_bands:
formatImage = formatImage[:, :, :target_bands]
elif bands < target_bands:
pad_bands = target_bands - bands
formatImage = np.pad(formatImage, ((0, 0), (0, 0), (0, pad_bands)), mode='constant', constant_values=0)
return formatImage
# Specify the directory containing the HDR and RAW files
directory = '/Users/xs/PycharmProjects/super-tomato/baixiangguo/光谱数据3030/'
# Initialize a list to hold all the spectral data arrays
all_spectral_data = []
# Loop through each data set (assuming there are 40 datasets)
for i in range(1, 101):
hdr_path = os.path.join(directory, f'{i}.HDR')
raw_path = os.path.join(directory, f'{i}')
# Read data
spectral_data = read_spectral_data(hdr_path, raw_path)
all_spectral_data.append(spectral_data)
# Stack all data into a single numpy array
all_spectral_data = np.stack(all_spectral_data)
print(all_spectral_data.shape) # This should print (40, 30, 30, 224)

View File

@ -11,7 +11,6 @@
"start_time": "2024-03-18T12:58:35.489144Z"
}
},
"outputs": [],
"source": [
"import numpy as np\n",
"import cv2\n",
@ -19,73 +18,12 @@
"import numpy as np\n",
"import cv2 as cv\n",
"import os"
]
],
"outputs": []
},
{
"cell_type": "code",
"execution_count": 4,
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"<class 'str'>\n",
"<class 'numpy.ndarray'>\n",
"(39911424,)\n",
"(87, 2048, 224)\n",
"<class 'numpy.ndarray'>\n",
"[[[1686. 1654. 1654. ... 1654. 1622. 1654.]\n",
" [1689. 1673. 1673. ... 1689. 1689. 1673.]\n",
" [1673. 1657. 1657. ... 1673. 1753. 1609.]\n",
" ...\n",
" [1609. 1689. 1721. ... 1657. 1705. 1577.]\n",
" [1718. 1702. 1654. ... 1574. 1638. 1686.]\n",
" [1686. 1670. 1686. ... 1590. 1686. 1670.]]\n",
"\n",
" [[1654. 1670. 1718. ... 1686. 1654. 1734.]\n",
" [1721. 1657. 1641. ... 1689. 1657. 1721.]\n",
" [1641. 1625. 1705. ... 1705. 1689. 1609.]\n",
" ...\n",
" [1625. 1673. 1689. ... 1673. 1673. 1641.]\n",
" [1686. 1622. 1654. ... 1686. 1654. 1606.]\n",
" [1734. 1654. 1670. ... 1510. 1654. 1654.]]\n",
"\n",
" [[1718. 1702. 1670. ... 1622. 1670. 1686.]\n",
" [1657. 1641. 1641. ... 1705. 1689. 1657.]\n",
" [1673. 1673. 1689. ... 1673. 1625. 1657.]\n",
" ...\n",
" [1625. 1673. 1705. ... 1625. 1609. 1625.]\n",
" [1702. 1686. 1654. ... 1638. 1670. 1654.]\n",
" [1638. 1702. 1686. ... 1670. 1702. 1638.]]\n",
"\n",
" ...\n",
"\n",
" [[1638. 1606. 1686. ... 1654. 1670. 1638.]\n",
" [1721. 1673. 1673. ... 1689. 1641. 1625.]\n",
" [1641. 1641. 1673. ... 1657. 1641. 1625.]\n",
" ...\n",
" [1625. 1609. 1689. ... 1657. 1657. 1673.]\n",
" [1638. 1606. 1702. ... 1622. 1622. 1654.]\n",
" [1574. 1654. 1622. ... 1718. 1670. 1622.]]\n",
"\n",
" [[1590. 1686. 1638. ... 1654. 1654. 1638.]\n",
" [1641. 1657. 1609. ... 1673. 1657. 1641.]\n",
" [1721. 1657. 1641. ... 1657. 1673. 1577.]\n",
" ...\n",
" [1657. 1673. 1641. ... 1593. 1609. 1609.]\n",
" [1622. 1670. 1670. ... 1686. 1734. 1622.]\n",
" [1670. 1590. 1670. ... 1638. 1590. 1638.]]\n",
"\n",
" [[1654. 1654. 1638. ... 1606. 1606. 1558.]\n",
" [1689. 1657. 1689. ... 1657. 1625. 1673.]\n",
" [1657. 1561. 1657. ... 1657. 1673. 1609.]\n",
" ...\n",
" [1657. 1625. 1689. ... 1657. 1609. 1625.]\n",
" [1638. 1654. 1654. ... 1622. 1606. 1590.]\n",
" [1590. 1686. 1606. ... 1574. 1686. 1686.]]]\n"
]
}
],
"source": [
"##合成真彩色图像\n",
"#读取raw文件\n",
@ -126,21 +64,11 @@
"start_time": "2024-03-18T13:06:04.869067Z"
}
},
"id": "3443037608cc60f6"
"id": "3443037608cc60f6",
"outputs": []
},
{
"cell_type": "code",
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"<class 'str'>\n",
"<class 'numpy.ndarray'>\n",
"(50462720,)\n"
]
}
],
"source": [
"##合成真彩色图像\n",
"#读取raw文件\n",
@ -192,97 +120,12 @@
}
},
"id": "283f370b2401221b",
"execution_count": 5
"execution_count": 5,
"outputs": []
},
{
"cell_type": "code",
"execution_count": 5,
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"(928, 1200, 3)\n",
"<class 'numpy.ndarray'>\n",
"(848, 1200, 3)\n",
"<class 'numpy.ndarray'>\n",
"(902, 1200, 3)\n",
"<class 'numpy.ndarray'>\n",
"(795, 1200, 3)\n",
"<class 'numpy.ndarray'>\n",
"(783, 1200, 3)\n",
"<class 'numpy.ndarray'>\n",
"(708, 1200, 3)\n",
"<class 'numpy.ndarray'>\n",
"(747, 1200, 3)\n",
"<class 'numpy.ndarray'>\n",
"(630, 1200, 3)\n",
"<class 'numpy.ndarray'>\n",
"(709, 1200, 3)\n",
"<class 'numpy.ndarray'>\n",
"(699, 1200, 3)\n",
"<class 'numpy.ndarray'>\n",
"(796, 1200, 3)\n",
"<class 'numpy.ndarray'>\n",
"(865, 1200, 3)\n",
"<class 'numpy.ndarray'>\n",
"(791, 1200, 3)\n",
"<class 'numpy.ndarray'>\n",
"(723, 1200, 3)\n",
"<class 'numpy.ndarray'>\n",
"(797, 1200, 3)\n",
"<class 'numpy.ndarray'>\n",
"(760, 1200, 3)\n",
"<class 'numpy.ndarray'>\n",
"(701, 1200, 3)\n",
"<class 'numpy.ndarray'>\n",
"(770, 1200, 3)\n",
"<class 'numpy.ndarray'>\n",
"(637, 1200, 3)\n",
"<class 'numpy.ndarray'>\n",
"(729, 1200, 3)\n",
"<class 'numpy.ndarray'>\n",
"(762, 1200, 3)\n",
"<class 'numpy.ndarray'>\n",
"(732, 1200, 3)\n",
"<class 'numpy.ndarray'>\n",
"(839, 1200, 3)\n",
"<class 'numpy.ndarray'>\n",
"(760, 1200, 3)\n",
"<class 'numpy.ndarray'>\n",
"(711, 1200, 3)\n",
"<class 'numpy.ndarray'>\n",
"(736, 1200, 3)\n",
"<class 'numpy.ndarray'>\n",
"(758, 1200, 3)\n",
"<class 'numpy.ndarray'>\n",
"(772, 1200, 3)\n",
"<class 'numpy.ndarray'>\n",
"(744, 1200, 3)\n",
"<class 'numpy.ndarray'>\n",
"(775, 1200, 3)\n",
"<class 'numpy.ndarray'>\n",
"(821, 1200, 3)\n",
"<class 'numpy.ndarray'>\n",
"(775, 1200, 3)\n",
"<class 'numpy.ndarray'>\n",
"(820, 1200, 3)\n",
"<class 'numpy.ndarray'>\n",
"(772, 1200, 3)\n",
"<class 'numpy.ndarray'>\n",
"(772, 1200, 3)\n",
"<class 'numpy.ndarray'>\n",
"(747, 1200, 3)\n",
"<class 'numpy.ndarray'>\n",
"(770, 1200, 3)\n",
"<class 'numpy.ndarray'>\n",
"(645, 1200, 3)\n",
"<class 'numpy.ndarray'>\n",
"(756, 1200, 3)\n",
"<class 'numpy.ndarray'>\n"
]
}
],
"source": [
"##循环合成真彩色图像\n",
"# 指定文件夹路径\n",
@ -357,27 +200,28 @@
"start_time": "2023-11-24T02:50:24.958389400Z"
}
},
"id": "15a3889f7bf1917f"
"id": "15a3889f7bf1917f",
"outputs": []
},
{
"cell_type": "code",
"execution_count": null,
"outputs": [],
"source": [],
"metadata": {
"collapsed": false
},
"id": "17b929b3de3fa2ed"
"id": "17b929b3de3fa2ed",
"outputs": []
},
{
"cell_type": "code",
"execution_count": null,
"outputs": [],
"source": [],
"metadata": {
"collapsed": false
},
"id": "e00c8e929ee57b60"
"id": "e00c8e929ee57b60",
"outputs": []
}
],
"metadata": {

File diff suppressed because one or more lines are too long