OpenCV-检测不包括阴影的对象

OpenCV- Detecting objects excluding shadows

我正在开发一种从鸟瞰图检测汽车的方法。我正在使用 scikit 包来计算空停车场图像和有车的停车场的差异以检测异物。然后我画了汽车周围的最小面积矩形。

没有阴影时效果很好。

空停车场图片(请忽略栗色车)

没有阴影

有阴影(问题)

有车影的时候,也在最小面积矩形内。如何从矩形中排除阴影?

这是我的来源

import numpy as np
from skimage.measure import compare_ssim
import imutils
from cv2 import cv2

# construct the argument parse and parse the arguments
# load the two input images
imageA = cv2.imread('empty-lot.png')
imageB = cv2.imread('two-car-lot-shadow.png')

# convert the images to grayscale
grayA = cv2.cvtColor(imageA, cv2.COLOR_BGR2GRAY)
grayB = cv2.cvtColor(imageB, cv2.COLOR_BGR2GRAY)

# compute the Structural Similarity Index (SSIM) between the two
# images, ensuring that the difference image is returned
(score, diff) = compare_ssim(grayA, grayB, full=True, gaussian_weights=True, sigma=4)
diff = (diff * 255).astype("uint8")
print("SSIM: {}".format(score))

# threshold the difference image, followed by finding contours to
# obtain the regions of the two input images that differ
thresh = cv2.threshold(diff, 0, 255, cv2.THRESH_BINARY_INV | cv2.THRESH_OTSU)[1]
cnts = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = cnts[0] if imutils.is_cv2() else cnts[1]

# loop over the contours
for c in cnts:
    # compute the min area rect of the contour and area
    rect = cv2.minAreaRect(c)
    box = cv2.boxPoints(rect)
    area = cv2.contourArea(c)
    # remove small contour areas
    if area < 10000: continue
    # convert all coordinates floating point values to int
    box = np.int0(box)
    # draw a green rectangle
    cv2.drawContours(imageB, [box], 0, (0, 255, 0), 2)

# show the output images
cv2.imshow("Modified", imageB)
cv2.imshow("Thresh", thresh)
k = cv2.waitKey() & 0xFF
if k == 27:
    exit()

您可以将图片格式转换为HSV格式,去除阴影很容易。

import ctypes
import numpy as np
import cv2
from pathlib import Path
from typing import List, Union, Callable


def main():
    img_bgr: np.ndarray = cv2.imread(str(Path('parking.jpg')))
    img_hsv: np.ndarray = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2HSV)
    mask = cv2.inRange(src=img_hsv, lowerb=np.array([0, 64, 153]), upperb=np.array([179, 255, 255]))
    img_hsv_modify: np.ndarray = cv2.bitwise_and(img_bgr, img_bgr, mask=mask)
    # show_img(img_hsv_modify)

    img_mask_gray = cv2.cvtColor(img_hsv_modify, cv2.COLOR_BGR2GRAY)
    threshold_val, img_bit = cv2.threshold(img_mask_gray, 0, 255, cv2.THRESH_BINARY)  # type: float, np.ndarray
    contours, hierarchy = cv2.findContours(img_bit, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)

    img_bgr_copy = np.copy(img_bgr)
    for cnt in filter(lambda c: cv2.contourArea(c) > 10000, contours):
        box = cv2.boxPoints(cv2.minAreaRect(cnt))
        cv2.drawContours(img_bgr_copy, [np.int0(box)], -1, color=(0, 255, 0), thickness=2)

    show_img([img_bgr, img_hsv_modify, cv2.cvtColor(img_bit, cv2.COLOR_GRAY2BGR), img_bgr_copy],
             note=['original', 'hsv modify', 'bit', 'result'])
    show_img(img_bgr_copy)


if __name__ == '__main__':
    main()

你可能会问我怎么知道下限上限。 (请看扩展码:control_bar_hsv.py

mask = cv2.inRange(src=img_hsv, lowerb=np.array([0, 64, 153]), upperb=np.array([179, 255, 255]))

结果:

所有图片:

分机号

show_img:

def show_img(img_list: Union[np.ndarray, List[np.ndarray]], combine_fun: Callable = np.vstack,
             window_name='demo', window_size=(ctypes.windll.user32.GetSystemMetrics(0) // 2, ctypes.windll.user32.GetSystemMetrics(1) // 2),
             delay_time=0, note: Union[str, List[str]] = None, **options):
    if isinstance(img_list, np.ndarray):
        img_list = [img_list]

    if isinstance(note, str):
        print(note)

    cv2.namedWindow(window_name, cv2.WINDOW_NORMAL)
    if window_size:
        w, h = window_size
        cv2.resizeWindow(window_name, w, h)

    result_list = []
    for idx, img in enumerate(img_list):
        img = np.copy(img)
        if note and isinstance(note, list) and idx < len(note):
            cv2.putText(img, note[idx], org=options.get('org', (50, 50)),
                        fontFace=options.get('fontFace', cv2.FONT_HERSHEY_SIMPLEX),
                        fontScale=options.get('fontScale', 2), color=(0, 255, 255), thickness=4)
        result_list.append(img)
    cv2.imshow(window_name, combine_fun(result_list))
    cv2.waitKey(delay_time)

control_bar_hsv.py

使用起来很简单,只需复制粘贴然后告诉它你的图片路径在哪里,就完成了。

如果你想创建其他control_bar,也许ControlBarBase会帮助你写更少的代码。

"""
control_bar_hsv.py
"""

import cv2
from pathlib import Path
import numpy as np
import ctypes
import functools
from typing import Tuple, Callable


class ControlBarBase:
    __slots__ = ('img_bgr',)
    WAIT_TIME = 500  # milliseconds
    CONTROL_PANEL_NAME = 'control_panel'
    IMAGE_PANEL_NAME = 'image'
    SCREEN_SIZE: Tuple[int, int] = None

    def __init__(self, img_path: Path):
        self.img_bgr: np.ndarray = cv2.imread(str(img_path))
        self.init_window()
        self.init_track_bar()

    def init_window(self):
        for name in (self.CONTROL_PANEL_NAME, self.IMAGE_PANEL_NAME):
            cv2.namedWindow(name, cv2.WINDOW_NORMAL)
            if self.SCREEN_SIZE:
                screen_width, screen_height = self.SCREEN_SIZE
                cv2.resizeWindow(name, int(screen_width), int(screen_height))

    def init_track_bar(self):
        """
        self.build_track_bar(label_name, range_lim=(0, 255), default_value=0, callback)
        """
        raise NotImplementedError('subclasses of _ControlBarBase must provide a init_track_bar() method')

    def render(self):
        raise NotImplementedError('subclasses of _ControlBarBase must provide a render() method.')

    def build_track_bar(self, label_name: str,
                        range_lim: Tuple[int, int], default_value: int, callback: Callable = lambda x: ...):
        min_val, max_val = range_lim
        cv2.createTrackbar(label_name, self.CONTROL_PANEL_NAME, min_val, max_val, callback)
        cv2.setTrackbarPos(label_name, self.CONTROL_PANEL_NAME, default_value)

    def get_trackbar_pos(self, widget_name: str):
        return cv2.getTrackbarPos(widget_name, self.CONTROL_PANEL_NAME)

    def run(self):
        while 1:
            img, callback_func = self.render()
            cv2.imshow(self.IMAGE_PANEL_NAME, img)
            if (cv2.waitKey(self.WAIT_TIME) & 0xFF == ord('q') or
                    cv2.getWindowProperty(self.IMAGE_PANEL_NAME, 0) == -1 or
                    cv2.getWindowProperty(self.CONTROL_PANEL_NAME, 0) == -1):
                callback_func()
                break
        cv2.destroyAllWindows()


class ControlBarHSV(ControlBarBase):
    __slots__ = ()
    WAIT_TIME = 500
    SCREEN_SIZE = ctypes.windll.user32.GetSystemMetrics(0) / 2, ctypes.windll.user32.GetSystemMetrics(1) / 2

    def init_track_bar(self):
        self.build_track_bar('HMin', range_lim=(0, 179), default_value=0)
        self.build_track_bar('SMin', (0, 255), 0)
        self.build_track_bar('VMin', (0, 255), 0)

        self.build_track_bar('HMax', (0, 179), 179)
        self.build_track_bar('SMax', (0, 255), 255)
        self.build_track_bar('VMax', (0, 255), 255)

    def render(self):
        # get current positions of all trackbars
        h_min = self.get_trackbar_pos('HMin')
        s_min = self.get_trackbar_pos('SMin')
        v_min = self.get_trackbar_pos('VMin')

        h_max = self.get_trackbar_pos('HMax')
        s_max = self.get_trackbar_pos('SMax')
        v_max = self.get_trackbar_pos('VMax')

        # Set minimum and max HSV values to display
        lower = np.array([h_min, s_min, v_min])
        upper = np.array([h_max, s_max, v_max])

        # Create HSV Image and threshold into a range.
        hsv = cv2.cvtColor(self.img_bgr, cv2.COLOR_BGR2HSV)
        mask = cv2.inRange(hsv, lower, upper)
        img_output: np.ndarray = cv2.bitwise_and(self.img_bgr, self.img_bgr, mask=mask)

        @functools.wraps(self.render)
        def cb_func():
            return print(f'cv2.inRange(src=hsv, lowerb=np.array([{h_min}, {s_min}, {v_min}]), upperb=np.array([{h_max}, {s_max}, {v_max}]))')

        return img_output, cb_func


if __name__ == '__main__':
    from argparse import ArgumentParser

    arg_parser = ArgumentParser()
    arg_parser.add_argument("src_path", type=Path, help="source image path")
    args = arg_parser.parse_args()
    obj = ControlBarHSV(args.src_path)  # ControlBarHSV(Path('parking.jpg'))
    obj.run()