mirror of
https://github.com/NanjingForestryUniversity/supermachine--tomato-passion_fruit.git
synced 2025-11-08 22:34:00 +00:00
feat:新增百香果绿色占比计算;tg文件夹下为百香果绿色占比各个结果图实验程序
This commit is contained in:
parent
7c191d9d42
commit
68fd790823
@ -317,6 +317,40 @@ class Passion_fruit:
|
|||||||
return np.zeros_like(rgb_img)
|
return np.zeros_like(rgb_img)
|
||||||
return result
|
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):
|
class Spec_predict(object):
|
||||||
def __init__(self, load_from=None, debug_mode=False):
|
def __init__(self, load_from=None, debug_mode=False):
|
||||||
@ -390,35 +424,28 @@ class Data_processing:
|
|||||||
return np.zeros_like(image_array) if image_array is not None else np.zeros((100, 100), dtype=np.uint8)
|
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)
|
image_filtered = cv2.medianBlur(image_array, 5)
|
||||||
|
|
||||||
# 形态学闭操作
|
# 形态学闭操作
|
||||||
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (15, 15))
|
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (15, 15))
|
||||||
image_closed = cv2.morphologyEx(image_filtered, cv2.MORPH_CLOSE, kernel)
|
image_closed = cv2.morphologyEx(image_filtered, cv2.MORPH_CLOSE, kernel)
|
||||||
|
|
||||||
# 查找轮廓
|
# 查找轮廓
|
||||||
contours, _ = cv2.findContours(image_closed, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
|
contours, _ = cv2.findContours(image_closed, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
|
||||||
|
|
||||||
# 创建空白图像以绘制轮廓
|
# 创建空白图像以绘制轮廓
|
||||||
image_contours = np.zeros_like(image_array)
|
image_contours = np.zeros_like(image_array)
|
||||||
|
|
||||||
# 进行多边形拟合并填充轮廓
|
# 进行多边形拟合并填充轮廓
|
||||||
for contour in contours:
|
for contour in contours:
|
||||||
epsilon = 0.001 * cv2.arcLength(contour, True)
|
epsilon = 0.001 * cv2.arcLength(contour, True)
|
||||||
approx = cv2.approxPolyDP(contour, epsilon, True)
|
approx = cv2.approxPolyDP(contour, epsilon, True)
|
||||||
if cv2.contourArea(approx) > 100: # 仅处理较大的轮廓
|
if cv2.contourArea(approx) > 100: # 仅处理较大的轮廓
|
||||||
cv2.drawContours(image_contours, [approx], -1, (255, 255, 255), -1)
|
cv2.drawContours(image_contours, [approx], -1, (255, 255, 255), -1)
|
||||||
|
|
||||||
return image_contours
|
return image_contours
|
||||||
|
|
||||||
def analyze_ellipse(self, image_array):
|
def analyze_ellipse(self, image_array):
|
||||||
# 查找白色区域的轮廓
|
# 查找白色区域的轮廓
|
||||||
_, binary_image = cv2.threshold(image_array, 127, 255, cv2.THRESH_BINARY)
|
_, binary_image = cv2.threshold(image_array, 127, 255, cv2.THRESH_BINARY)
|
||||||
contours, _ = cv2.findContours(binary_image, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
|
contours, _ = cv2.findContours(binary_image, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
|
||||||
|
|
||||||
# 初始化变量用于存储最大轮廓的长径和短径
|
# 初始化变量用于存储最大轮廓的长径和短径
|
||||||
major_axis = 0
|
major_axis = 0
|
||||||
minor_axis = 0
|
minor_axis = 0
|
||||||
|
|
||||||
# 对每个找到的轮廓,找出可以包围它的最小椭圆,并计算长径和短径
|
# 对每个找到的轮廓,找出可以包围它的最小椭圆,并计算长径和短径
|
||||||
for contour in contours:
|
for contour in contours:
|
||||||
if len(contour) >= 5: # 至少需要5个点来拟合椭圆
|
if len(contour) >= 5: # 至少需要5个点来拟合椭圆
|
||||||
@ -586,10 +613,13 @@ class Data_processing:
|
|||||||
combined_mask = pf.create_mask(hsv_image)
|
combined_mask = pf.create_mask(hsv_image)
|
||||||
combined_mask = pf.apply_morphology(combined_mask)
|
combined_mask = pf.apply_morphology(combined_mask)
|
||||||
max_mask = pf.find_largest_component(combined_mask)
|
max_mask = pf.find_largest_component(combined_mask)
|
||||||
|
|
||||||
filled_img, defect = self.fill_holes(max_mask)
|
filled_img, defect = self.fill_holes(max_mask)
|
||||||
|
|
||||||
contour_mask = self.contour_process(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)
|
||||||
|
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)
|
long_axis, short_axis = self.analyze_ellipse(contour_mask)
|
||||||
#重量单位为g,加上了一点随机数
|
#重量单位为g,加上了一点随机数
|
||||||
weight_real = self.weight_estimates(long_axis, short_axis)
|
weight_real = self.weight_estimates(long_axis, short_axis)
|
||||||
@ -608,12 +638,13 @@ class Data_processing:
|
|||||||
# print(f'直径:{diameter}')
|
# print(f'直径:{diameter}')
|
||||||
if diameter < 2.5:
|
if diameter < 2.5:
|
||||||
diameter = 0
|
diameter = 0
|
||||||
|
green_percentage = 0
|
||||||
weight = 0
|
weight = 0
|
||||||
number_defects = 0
|
number_defects = 0
|
||||||
total_pixels = 0
|
total_pixels = 0
|
||||||
rp = cv2.cvtColor(np.ones((setting.n_rgb_rows, setting.n_rgb_cols, setting.n_rgb_bands),
|
rp = cv2.cvtColor(np.ones((setting.n_rgb_rows, setting.n_rgb_cols, setting.n_rgb_bands),
|
||||||
dtype=np.uint8), cv2.COLOR_BGR2RGB)
|
dtype=np.uint8), cv2.COLOR_BGR2RGB)
|
||||||
return diameter, weight, number_defects, total_pixels, rp
|
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:
|
def process_data(seif, cmd: str, images: list, spec: any, pipe: Pipe, detector: Spec_predict) -> bool:
|
||||||
"""
|
"""
|
||||||
@ -644,7 +675,7 @@ class Data_processing:
|
|||||||
|
|
||||||
elif cmd == 'PF':
|
elif cmd == 'PF':
|
||||||
# 百香果
|
# 百香果
|
||||||
diameter, weight, number_defects, total_pixels, rp = seif.analyze_passion_fruit(img)
|
diameter, green_percentage, weight, number_defects, total_pixels, rp = seif.analyze_passion_fruit(img)
|
||||||
if i <= 2:
|
if i <= 2:
|
||||||
diameter_axis_list.append(diameter)
|
diameter_axis_list.append(diameter)
|
||||||
max_defect_num = max(max_defect_num, number_defects)
|
max_defect_num = max(max_defect_num, number_defects)
|
||||||
@ -652,6 +683,7 @@ class Data_processing:
|
|||||||
if i == 1:
|
if i == 1:
|
||||||
rp_result = rp
|
rp_result = rp
|
||||||
weight = weight
|
weight = weight
|
||||||
|
gp = round(green_percentage, 2)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
logging.error(f'错误指令,指令为{cmd}')
|
logging.error(f'错误指令,指令为{cmd}')
|
||||||
@ -668,14 +700,12 @@ class Data_processing:
|
|||||||
defect_num=max_defect_num, total_defect_area=max_total_defect_area, rp=rp_result)
|
defect_num=max_defect_num, total_defect_area=max_total_defect_area, rp=rp_result)
|
||||||
return response
|
return response
|
||||||
elif cmd == 'PF':
|
elif cmd == 'PF':
|
||||||
green_percentage = 0
|
|
||||||
brix = detector.predict(spec)
|
brix = detector.predict(spec)
|
||||||
if diameter == 0:
|
if diameter == 0:
|
||||||
brix = 0
|
brix = 0
|
||||||
# print(f'预测的brix值为:{brix}; 预测的直径为:{diameter}; 预测的重量为:{weight}; 预测的绿色比例为:{green_percentage};'
|
# print(f'预测的brix值为:{brix}; 预测的直径为:{diameter}; 预测的重量为:{weight}; 预测的绿色比例为:{green_percentage};'
|
||||||
# f' 预测的缺陷数量为:{max_defect_num}; 预测的总缺陷面积为:{max_total_defect_area};')
|
# f' 预测的缺陷数量为:{max_defect_num}; 预测的总缺陷面积为:{max_total_defect_area};')
|
||||||
response = pipe.send_data(cmd=cmd, brix=brix, green_percentage=green_percentage, diameter=diameter,
|
response = pipe.send_data(cmd=cmd, brix=brix, green_percentage=gp, diameter=diameter, weight=weight,
|
||||||
weight=weight,
|
|
||||||
defect_num=max_defect_num, total_defect_area=max_total_defect_area, rp=rp_result)
|
defect_num=max_defect_num, total_defect_area=max_total_defect_area, rp=rp_result)
|
||||||
return response
|
return response
|
||||||
|
|
||||||
|
|||||||
@ -32,6 +32,14 @@ class Config:
|
|||||||
value_target = 25
|
value_target = 25
|
||||||
value_delta = 10
|
value_delta = 10
|
||||||
|
|
||||||
|
#提取绿色像素参数
|
||||||
|
low_H = 0
|
||||||
|
low_S = 100
|
||||||
|
low_V = 0
|
||||||
|
high_H = 60
|
||||||
|
high_S = 180
|
||||||
|
high_V = 60
|
||||||
|
|
||||||
#spec_predict
|
#spec_predict
|
||||||
#筛选谱段并未使用,在qt取数据时已经筛选
|
#筛选谱段并未使用,在qt取数据时已经筛选
|
||||||
selected_bands = [8, 9, 10, 48, 49, 50, 77, 80, 103, 108, 115, 143, 145]
|
selected_bands = [8, 9, 10, 48, 49, 50, 77, 80, 103, 108, 115, 143, 145]
|
||||||
|
|||||||
53
20240529RGBtest3/tg/config.py
Normal file
53
20240529RGBtest3/tg/config.py
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
# -*- 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
|
||||||
|
|
||||||
|
#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
|
||||||
|
|
||||||
256
20240529RGBtest3/tg/passion_fruit_rgb.py
Normal file
256
20240529RGBtest3/tg/passion_fruit_rgb.py
Normal file
@ -0,0 +1,256 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# @Time : 2024/6/26 下午5:31
|
||||||
|
# @Author : TG
|
||||||
|
# @File : passion_fruit_rgb.py
|
||||||
|
# @Software: PyCharm
|
||||||
|
import os
|
||||||
|
import cv2
|
||||||
|
import numpy as np
|
||||||
|
import argparse
|
||||||
|
import logging
|
||||||
|
from config import Config as setting
|
||||||
|
|
||||||
|
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.warning("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.warning("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.warning("二值图像为空或全黑,返回一个全黑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 fill_holes(bin_img):
|
||||||
|
|
||||||
|
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(image_array):
|
||||||
|
# 检查图像是否为空或全黑
|
||||||
|
if image_array is None or image_array.size == 0 or np.all(image_array == 0):
|
||||||
|
logging.warning("输入的图像为空或全黑,返回一个全黑图像。")
|
||||||
|
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 extract_green_pixels_cv(image):
|
||||||
|
"""
|
||||||
|
使用 OpenCV 提取图像中的绿色像素,并可选择保存结果图像。
|
||||||
|
|
||||||
|
参数:
|
||||||
|
image_path (str): 输入图像的文件路径。
|
||||||
|
save_path (str, optional): 输出图像的保存路径,若提供此参数,则保存提取的绿色像素图像。
|
||||||
|
|
||||||
|
返回:
|
||||||
|
输出图像,绿色像素为白色,其他像素为黑色。
|
||||||
|
"""
|
||||||
|
image_rgb = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
|
||||||
|
# Define the HSV range for green
|
||||||
|
lower_green = np.array([0, 100, 0])
|
||||||
|
upper_green = np.array([60, 180, 60])
|
||||||
|
# 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(defect, mask):
|
||||||
|
"""
|
||||||
|
比较两幅图像的像素值,如果相同则赋值为0,不同则赋值为255。
|
||||||
|
参数:
|
||||||
|
defect_path (str): 第一幅图像的路径。
|
||||||
|
mask_path (str): 第二幅图像的路径。
|
||||||
|
save_path (str, optional): 结果图像的保存路径。
|
||||||
|
返回:
|
||||||
|
numpy.ndarray: 处理后的图像数组。
|
||||||
|
"""
|
||||||
|
# 确保图像是二值图像
|
||||||
|
_, 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
|
||||||
|
|
||||||
|
|
||||||
|
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\tg\test',
|
||||||
|
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()
|
||||||
|
pf = Passion_fruit()
|
||||||
|
|
||||||
|
for img_file in os.listdir(args.dir_path):
|
||||||
|
if img_file.endswith('.bmp'):
|
||||||
|
img_path = os.path.join(args.dir_path, img_file)
|
||||||
|
img = cv2.imread(img_path)
|
||||||
|
cv2.imshow('img', img)
|
||||||
|
hsv_image = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
|
||||||
|
cv2.imshow('hsv', hsv_image)
|
||||||
|
combined_mask = pf.create_mask(hsv_image)
|
||||||
|
cv2.imshow('combined_mask1', combined_mask)
|
||||||
|
combined_mask = pf.apply_morphology(combined_mask)
|
||||||
|
cv2.imshow('combined_mask2', combined_mask)
|
||||||
|
max_mask = pf.find_largest_component(combined_mask)
|
||||||
|
cv2.imshow('max_mask', max_mask)
|
||||||
|
|
||||||
|
filled_img, defect = fill_holes(max_mask)
|
||||||
|
cv2.imshow('filled_img', filled_img)
|
||||||
|
cv2.imshow('defect', defect)
|
||||||
|
|
||||||
|
contour_mask = contour_process(max_mask)
|
||||||
|
cv2.imshow('contour_mask', contour_mask)
|
||||||
|
|
||||||
|
fore = pf.bitwise_and_rgb_with_binary(img, contour_mask)
|
||||||
|
cv2.imshow('fore', fore)
|
||||||
|
|
||||||
|
mask = extract_green_pixels_cv(fore)
|
||||||
|
cv2.imshow('mask', mask)
|
||||||
|
|
||||||
|
green_img = pixel_comparison(defect, mask)
|
||||||
|
cv2.imshow('green_img', green_img)
|
||||||
|
|
||||||
|
green_percentage = np.sum(green_img == 255) / np.sum(contour_mask == 255)
|
||||||
|
green_percentage = round(green_percentage, 2)
|
||||||
|
|
||||||
|
print(np.sum(green_img == 255))
|
||||||
|
print(np.sum(contour_mask == 255))
|
||||||
|
print(green_percentage)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
edge = pf.draw_contours_on_image(img, contour_mask)
|
||||||
|
cv2.imshow('edge', edge)
|
||||||
|
org_defect = pf.bitwise_and_rgb_with_binary(edge, max_mask)
|
||||||
|
cv2.imshow('org_defect', org_defect)
|
||||||
|
|
||||||
|
|
||||||
|
cv2.waitKey(0)
|
||||||
|
cv2.destroyAllWindows()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
61
20240529RGBtest3/tg/t.py
Normal file
61
20240529RGBtest3/tg/t.py
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# @Time : 2024/6/26 下午6:15
|
||||||
|
# @Author : TG
|
||||||
|
# @File : t.py
|
||||||
|
# @Software: PyCharm
|
||||||
|
import cv2
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
|
||||||
|
def nothing(x):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
# Create a window
|
||||||
|
cv2.namedWindow('Green Pixels Selector')
|
||||||
|
|
||||||
|
# Create trackbars for color change
|
||||||
|
cv2.createTrackbar('Lower Hue', 'Green Pixels Selector', 0, 255, nothing)
|
||||||
|
cv2.createTrackbar('Lower Sat', 'Green Pixels Selector', 100, 255, nothing)
|
||||||
|
cv2.createTrackbar('Lower Val', 'Green Pixels Selector', 0, 255, nothing)
|
||||||
|
cv2.createTrackbar('Upper Hue', 'Green Pixels Selector', 60, 255, nothing)
|
||||||
|
cv2.createTrackbar('Upper Sat', 'Green Pixels Selector', 180, 255, nothing)
|
||||||
|
cv2.createTrackbar('Upper Val', 'Green Pixels Selector', 60, 255, nothing)
|
||||||
|
|
||||||
|
# Load image
|
||||||
|
image = cv2.imread(r'D:\project\supermachine--tomato-passion_fruit\20240529RGBtest3\tg\test\23.bmp')
|
||||||
|
image_rgb = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
|
||||||
|
|
||||||
|
while (True):
|
||||||
|
# Get current positions of the trackbars
|
||||||
|
lh = cv2.getTrackbarPos('Lower Hue', 'Green Pixels Selector')
|
||||||
|
ls = cv2.getTrackbarPos('Lower Sat', 'Green Pixels Selector')
|
||||||
|
lv = cv2.getTrackbarPos('Lower Val', 'Green Pixels Selector')
|
||||||
|
uh = cv2.getTrackbarPos('Upper Hue', 'Green Pixels Selector')
|
||||||
|
us = cv2.getTrackbarPos('Upper Sat', 'Green Pixels Selector')
|
||||||
|
uv = cv2.getTrackbarPos('Upper Val', 'Green Pixels Selector')
|
||||||
|
|
||||||
|
# Define the HSV range for green
|
||||||
|
lower_green = np.array([lh, ls, lv])
|
||||||
|
upper_green = np.array([uh, us, uv])
|
||||||
|
|
||||||
|
# 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)
|
||||||
|
|
||||||
|
# Display the resulting frame
|
||||||
|
cv2.imshow('Green Pixels Selector', res_bgr)
|
||||||
|
|
||||||
|
if cv2.waitKey(1) & 0xFF == ord('q'):
|
||||||
|
break
|
||||||
|
|
||||||
|
# When everything done, release the window
|
||||||
|
cv2.destroyAllWindows()
|
||||||
@ -209,8 +209,7 @@ class Pipe:
|
|||||||
defect_num + total_defect_area + height + width + img_bytes)
|
defect_num + total_defect_area + height + width + img_bytes)
|
||||||
elif cmd == 'PF':
|
elif cmd == 'PF':
|
||||||
brix = int(brix * 1000).to_bytes(2, byteorder='big')
|
brix = int(brix * 1000).to_bytes(2, byteorder='big')
|
||||||
gp = 0
|
gp = int(green_percentage * 100).to_bytes(1, byteorder='big')
|
||||||
gp = gp.to_bytes(1, byteorder='big')
|
|
||||||
weight = weight.to_bytes(1, byteorder='big')
|
weight = weight.to_bytes(1, byteorder='big')
|
||||||
send_message = (length + cmd_re + brix + gp + diameter + weight +
|
send_message = (length + cmd_re + brix + gp + diameter + weight +
|
||||||
defect_num + total_defect_area + height + width + img_bytes)
|
defect_num + total_defect_area + height + width + img_bytes)
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user