【问题标题】:OpenCV- Detecting objects excluding shadowsOpenCV-检测不包括阴影的物体
【发布时间】:2018-08-26 06:45:16
【问题描述】:

我正在开发一种从鸟瞰图像中检测汽车的方法。我正在使用 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、YUV 等,BGR 颜色域不适合此类问题,其他颜色约定在此用例中似乎更有利,因为阴影只会改变 这些像素的强度,研究强度是独立通道的那些颜色约定。然后您可以废弃该通道,同时计算剩余两个通道的差异。
  • 切肉刀的想法!谢谢@ZdaR,我会尝试并报告!
  • 它正在工作,我可以通过移除 V 通道来移除阴影!当相机嘈杂或阴影太暗时,暗区的色调值会变得疯狂。所以有时这种方法会失败。
  • 那么另一种方法是训练你自己的 haar 级联,对于车辆检测,你需要使用没有阴影的汽车顶视图图像自己训练一个。

标签: python-3.x opencv image-processing object-detection


【解决方案1】:

您可以将图像格式转换为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()

【讨论】:

  • @nipunasudha 2 年零 5 个月前,我希望你已经找到了答案。
  • 非常感谢@Carson。 HSV 方法是我们在图像处理方面取得的最好成绩,然后我们切换到 YOLO 库。实际上,我们的摄像头并不直接位于停车位上方,因此 YOLO 可以识别边界框。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-07-06
  • 1970-01-01
  • 2014-03-07
  • 1970-01-01
  • 1970-01-01
  • 2013-12-23
  • 1970-01-01
相关资源
最近更新 更多