supermachine--tomato-passio.../20240529RGBtest3/tg/passion_fruit_rgb.py

256 lines
10 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# -*- 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()