diff --git "a/01-\351\200\211\346\213\251ROI\345\214\272\345\237\237/README.md" "b/01-\351\200\211\346\213\251ROI\345\214\272\345\237\237/README.md" new file mode 100644 index 0000000..e69de29 diff --git "a/01-\351\200\211\346\213\251ROI\345\214\272\345\237\237/demo-pic.png" "b/01-\351\200\211\346\213\251ROI\345\214\272\345\237\237/demo-pic.png" new file mode 100644 index 0000000..d9792f2 Binary files /dev/null and "b/01-\351\200\211\346\213\251ROI\345\214\272\345\237\237/demo-pic.png" differ diff --git "a/01-\351\200\211\346\213\251ROI\345\214\272\345\237\237/image_roi.png" "b/01-\351\200\211\346\213\251ROI\345\214\272\345\237\237/image_roi.png" new file mode 100644 index 0000000..ec0162e Binary files /dev/null and "b/01-\351\200\211\346\213\251ROI\345\214\272\345\237\237/image_roi.png" differ diff --git "a/01-\351\200\211\346\213\251ROI\345\214\272\345\237\237/select_roi.py" "b/01-\351\200\211\346\213\251ROI\345\214\272\345\237\237/select_roi.py" new file mode 100644 index 0000000..2afc86c --- /dev/null +++ "b/01-\351\200\211\346\213\251ROI\345\214\272\345\237\237/select_roi.py" @@ -0,0 +1,39 @@ +# -*- coding: utf-8 -*- +''' +选取ROI区域 +回车或者空格确认选择 +c键 撤销选择 +''' +import numpy as np +import cv2 +import sys + + +# 文件路径 +# img_path = 'blue-color-block.png' +img_path = sys.argv[1] + +# 读入图片 +img = cv2.imread(img_path) +# 创建一个窗口 +cv2.namedWindow("image", flags= cv2.WINDOW_NORMAL | cv2.WINDOW_FREERATIO) +cv2.imshow("image", img) +# 是否显示网格 +showCrosshair = True + +# 如果为Ture的话 , 则鼠标的其实位置就作为了roi的中心 +# False: 从左上角到右下角选中区域 +fromCenter = False +# Select ROI +rect = cv2.selectROI("image", img, showCrosshair, fromCenter) + +print("选中矩形区域") +(x, y, w, h) = rect + +# Crop image +imCrop = img[y : y+h, x:x+w] + +# Display cropped image +cv2.imshow("image_roi", imCrop) +cv2.imwrite("image_roi.png", imCrop) +cv2.waitKey(0) \ No newline at end of file diff --git "a/01-\351\200\211\346\213\251ROI\345\214\272\345\237\237/\346\274\224\347\244\272\350\247\206\351\242\221/01-\346\265\213\350\257\225\350\247\206\351\242\221-\351\200\211\346\213\251\345\233\276\345\203\217\347\232\204ROI\345\214\272\345\237\237.mkv" "b/01-\351\200\211\346\213\251ROI\345\214\272\345\237\237/\346\274\224\347\244\272\350\247\206\351\242\221/01-\346\265\213\350\257\225\350\247\206\351\242\221-\351\200\211\346\213\251\345\233\276\345\203\217\347\232\204ROI\345\214\272\345\237\237.mkv" new file mode 100644 index 0000000..884ad0f Binary files /dev/null and "b/01-\351\200\211\346\213\251ROI\345\214\272\345\237\237/\346\274\224\347\244\272\350\247\206\351\242\221/01-\346\265\213\350\257\225\350\247\206\351\242\221-\351\200\211\346\213\251\345\233\276\345\203\217\347\232\204ROI\345\214\272\345\237\237.mkv" differ diff --git "a/02-\345\233\276\345\203\217\351\242\234\350\211\262\347\273\237\350\256\241HSV/HSV\351\242\234\350\211\262\347\273\237\350\256\241\346\240\267\344\276\213.png" "b/02-\345\233\276\345\203\217\351\242\234\350\211\262\347\273\237\350\256\241HSV/HSV\351\242\234\350\211\262\347\273\237\350\256\241\346\240\267\344\276\213.png" new file mode 100644 index 0000000..751a161 Binary files /dev/null and "b/02-\345\233\276\345\203\217\351\242\234\350\211\262\347\273\237\350\256\241HSV/HSV\351\242\234\350\211\262\347\273\237\350\256\241\346\240\267\344\276\213.png" differ diff --git "a/02-\345\233\276\345\203\217\351\242\234\350\211\262\347\273\237\350\256\241HSV/README.md" "b/02-\345\233\276\345\203\217\351\242\234\350\211\262\347\273\237\350\256\241HSV/README.md" new file mode 100644 index 0000000..e69de29 diff --git "a/02-\345\233\276\345\203\217\351\242\234\350\211\262\347\273\237\350\256\241HSV/image_roi.png" "b/02-\345\233\276\345\203\217\351\242\234\350\211\262\347\273\237\350\256\241HSV/image_roi.png" new file mode 100644 index 0000000..3b42d65 Binary files /dev/null and "b/02-\345\233\276\345\203\217\351\242\234\350\211\262\347\273\237\350\256\241HSV/image_roi.png" differ diff --git "a/02-\345\233\276\345\203\217\351\242\234\350\211\262\347\273\237\350\256\241HSV/img_hsv_stat.py" "b/02-\345\233\276\345\203\217\351\242\234\350\211\262\347\273\237\350\256\241HSV/img_hsv_stat.py" new file mode 100644 index 0000000..9e74f66 --- /dev/null +++ "b/02-\345\233\276\345\203\217\351\242\234\350\211\262\347\273\237\350\256\241HSV/img_hsv_stat.py" @@ -0,0 +1,56 @@ +# -*- coding: utf-8 -*- + +''' +绘制彩图在HSV颜色空间下的统计直方图 +''' + +from matplotlib import pyplot as plt +import numpy as np +import cv2 +import sys + +# 读入图片 +img_path = sys.argv[1] +img = cv2.imread(img_path) +# img = cv2.imread('little_chess.png') +if img is None: + print("图片读入失败, 请检查图片路径及文件名") + exit() + + +# 将图片转换为HSV格式 +img = cv2.cvtColor(img, cv2.COLOR_BGR2HSV) +# 创建画布 +fig, ax = plt.subplots() +# Matplotlib预设的颜色字符 +hsvColor = ('y', 'g', 'k') +# 统计窗口间隔 , 设置小了锯齿状较为明显 最小为1 最好可以被256整除 +bin_win = 3 +# 设定统计窗口bins的总数 +bin_num = int(256/bin_win) +# 控制画布的窗口x坐标的稀疏程度. 最密集就设定xticks_win=1 +xticks_win = 2 +# 设置标题 +ax.set_title('HSV Color Space') +lines = [] +for cidx, color in enumerate(hsvColor): + # cidx channel 序号 + # color r / g / b + cHist = cv2.calcHist([img], [cidx], None, [bin_num], [0, 256]) + # 绘制折线图 + line, = ax.plot(cHist, color=color,linewidth=8) + lines.append(line) + +# 标签 +labels = [cname +' Channel' for cname in 'HSV'] +# 添加channel +plt.legend(lines,labels, loc='upper right') +# 设定画布的范围 +ax.set_xlim([0, bin_num]) +# 设定x轴方向标注的位置 +ax.set_xticks(np.arange(0, bin_num, xticks_win)) +# 设定x轴方向标注的内容 +ax.set_xticklabels(list(range(0, 256, bin_win*xticks_win)),rotation=45) + +# 显示画面 +plt.show() diff --git "a/02-\345\233\276\345\203\217\351\242\234\350\211\262\347\273\237\350\256\241HSV/\346\274\224\347\244\272\350\247\206\351\242\221/02-\346\265\213\350\257\225\350\247\206\351\242\221-\345\233\276\345\203\217\351\242\234\350\211\262\347\273\237\350\256\241HSV.mkv" "b/02-\345\233\276\345\203\217\351\242\234\350\211\262\347\273\237\350\256\241HSV/\346\274\224\347\244\272\350\247\206\351\242\221/02-\346\265\213\350\257\225\350\247\206\351\242\221-\345\233\276\345\203\217\351\242\234\350\211\262\347\273\237\350\256\241HSV.mkv" new file mode 100644 index 0000000..c772db8 Binary files /dev/null and "b/02-\345\233\276\345\203\217\351\242\234\350\211\262\347\273\237\350\256\241HSV/\346\274\224\347\244\272\350\247\206\351\242\221/02-\346\265\213\350\257\225\350\247\206\351\242\221-\345\233\276\345\203\217\351\242\234\350\211\262\347\273\237\350\256\241HSV.mkv" differ diff --git "a/03-HSV\351\242\234\350\211\262\351\230\210\345\200\274\345\212\250\346\200\201\350\260\203\350\212\202\345\267\245\345\205\267/README.md" "b/03-HSV\351\242\234\350\211\262\351\230\210\345\200\274\345\212\250\346\200\201\350\260\203\350\212\202\345\267\245\345\205\267/README.md" new file mode 100644 index 0000000..e69de29 diff --git "a/03-HSV\351\242\234\350\211\262\351\230\210\345\200\274\345\212\250\346\200\201\350\260\203\350\212\202\345\267\245\345\205\267/ThresholdEditorGUIHsv.py" "b/03-HSV\351\242\234\350\211\262\351\230\210\345\200\274\345\212\250\346\200\201\350\260\203\350\212\202\345\267\245\345\205\267/ThresholdEditorGUIHsv.py" new file mode 100644 index 0000000..3ead527 --- /dev/null +++ "b/03-HSV\351\242\234\350\211\262\351\230\210\345\200\274\345\212\250\346\200\201\350\260\203\350\212\202\345\267\245\345\205\267/ThresholdEditorGUIHsv.py" @@ -0,0 +1,104 @@ +# -*- coding: utf-8 -*- +''' + 可视化颜色阈值调参软件 +''' + +import cv2 +import numpy as np +import sys + +# 更新MASK图像,并且刷新windows +def updateMask(): + global img + global lowerb + global upperb + global mask + # 计算MASK + mask = cv2.inRange(img_hsv, lowerb, upperb) + + cv2.imshow('mask', mask) + +# 更新阈值 +def updateThreshold(x): + + global lowerb + global upperb + + minH = cv2.getTrackbarPos('minH','image') + maxH = cv2.getTrackbarPos('maxH','image') + minS = cv2.getTrackbarPos('minS','image') + maxS = cv2.getTrackbarPos('maxS', 'image') + minV = cv2.getTrackbarPos('minV', 'image') + maxV = cv2.getTrackbarPos('maxV', 'image') + + lowerb = np.int32([minH, minS, minV]) + upperb = np.int32([maxH, maxS, maxV]) + + print('更新阈值') + print(lowerb) + print(upperb) + updateMask() + +def main(img): + global img_hsv + global upperb + global lowerb + global mask + # 将图片转换为HSV格式 + img_hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV) + + # 颜色阈值 Upper + upperb = None + # 颜色阈值 Lower + lowerb = None + + mask = None + + cv2.namedWindow('image', flags= cv2.WINDOW_NORMAL | cv2.WINDOW_FREERATIO) + # cv2.namedWindow('image') + cv2.imshow('image', img) + + # cv2.namedWindow('mask') + cv2.namedWindow('mask', flags= cv2.WINDOW_NORMAL | cv2.WINDOW_FREERATIO) + + # 红色阈值 Bar + ## 红色阈值下界 + cv2.createTrackbar('minH','image',0,255,updateThreshold) + ## 红色阈值上界 + cv2.createTrackbar('maxH','image',0,255,updateThreshold) + ## 设定红色阈值上界滑条的值为255 + cv2.setTrackbarPos('maxH', 'image', 255) + cv2.setTrackbarPos('minH', 'image', 0) + # 绿色阈值 Bar + cv2.createTrackbar('minS','image',0,255,updateThreshold) + cv2.createTrackbar('maxS','image',0,255,updateThreshold) + cv2.setTrackbarPos('maxS', 'image', 255) + cv2.setTrackbarPos('minS', 'image', 0) + # 蓝色阈值 Bar + cv2.createTrackbar('minV','image',0,255,updateThreshold) + cv2.createTrackbar('maxV','image',0,255,updateThreshold) + cv2.setTrackbarPos('maxV', 'image', 255) + cv2.setTrackbarPos('minV', 'image', 0) + + # 首次初始化窗口的色块 + # 后面的更新 都是由getTrackbarPos产生变化而触发 + updateThreshold(None) + + print("调试棋子的颜色阈值, 键盘摁e退出程序") + while cv2.waitKey(0) != ord('e'): + continue + + cv2.imwrite('tmp_bin.png', mask) + cv2.destroyAllWindows() + +if __name__ == "__main__": + # 样例图片 (从命令行中填入) + image_path = sys.argv[1] + # 样例图片 (在代码中填入) + # img = cv2.imread('cfs_samples.jpg') + img = cv2.imread(image_path) + if img is None: + print("Error: 文件路径错误,没有此图片 {}".format(image_path)) + exit(1) + + main(img) \ No newline at end of file diff --git "a/03-HSV\351\242\234\350\211\262\351\230\210\345\200\274\345\212\250\346\200\201\350\260\203\350\212\202\345\267\245\345\205\267/demo-pic.png" "b/03-HSV\351\242\234\350\211\262\351\230\210\345\200\274\345\212\250\346\200\201\350\260\203\350\212\202\345\267\245\345\205\267/demo-pic.png" new file mode 100644 index 0000000..d9792f2 Binary files /dev/null and "b/03-HSV\351\242\234\350\211\262\351\230\210\345\200\274\345\212\250\346\200\201\350\260\203\350\212\202\345\267\245\345\205\267/demo-pic.png" differ diff --git "a/03-HSV\351\242\234\350\211\262\351\230\210\345\200\274\345\212\250\346\200\201\350\260\203\350\212\202\345\267\245\345\205\267/\345\233\276\347\211\207\344\272\214\345\200\274\345\214\226\346\240\267\344\276\213.png" "b/03-HSV\351\242\234\350\211\262\351\230\210\345\200\274\345\212\250\346\200\201\350\260\203\350\212\202\345\267\245\345\205\267/\345\233\276\347\211\207\344\272\214\345\200\274\345\214\226\346\240\267\344\276\213.png" new file mode 100644 index 0000000..de74348 Binary files /dev/null and "b/03-HSV\351\242\234\350\211\262\351\230\210\345\200\274\345\212\250\346\200\201\350\260\203\350\212\202\345\267\245\345\205\267/\345\233\276\347\211\207\344\272\214\345\200\274\345\214\226\346\240\267\344\276\213.png" differ diff --git "a/03-HSV\351\242\234\350\211\262\351\230\210\345\200\274\345\212\250\346\200\201\350\260\203\350\212\202\345\267\245\345\205\267/\346\274\224\347\244\272\350\247\206\351\242\221/03-HSV\351\242\234\350\211\262\351\230\210\345\200\274\345\212\250\346\200\201\350\260\203\350\212\202\345\267\245\345\205\267.mkv" "b/03-HSV\351\242\234\350\211\262\351\230\210\345\200\274\345\212\250\346\200\201\350\260\203\350\212\202\345\267\245\345\205\267/\346\274\224\347\244\272\350\247\206\351\242\221/03-HSV\351\242\234\350\211\262\351\230\210\345\200\274\345\212\250\346\200\201\350\260\203\350\212\202\345\267\245\345\205\267.mkv" new file mode 100644 index 0000000..d296b3e Binary files /dev/null and "b/03-HSV\351\242\234\350\211\262\351\230\210\345\200\274\345\212\250\346\200\201\350\260\203\350\212\202\345\267\245\345\205\267/\346\274\224\347\244\272\350\247\206\351\242\221/03-HSV\351\242\234\350\211\262\351\230\210\345\200\274\345\212\250\346\200\201\350\260\203\350\212\202\345\267\245\345\205\267.mkv" differ diff --git "a/04-\350\211\262\345\235\227\350\257\206\345\210\253/README.md" "b/04-\350\211\262\345\235\227\350\257\206\345\210\253/README.md" new file mode 100644 index 0000000..e69de29 diff --git "a/04-\350\211\262\345\235\227\350\257\206\345\210\253/color_feature.py" "b/04-\350\211\262\345\235\227\350\257\206\345\210\253/color_feature.py" new file mode 100644 index 0000000..0f9fc68 --- /dev/null +++ "b/04-\350\211\262\345\235\227\350\257\206\345\210\253/color_feature.py" @@ -0,0 +1,55 @@ +# -*- coding: utf-8 -*- +''' +颜色特征识别 +''' +import numpy as np +import cv2 + + +def color_block_finder(img, lowerb, upperb, + min_w=0, max_w=None, min_h=0, max_h=None): + ''' + 色块识别 返回矩形信息 + ''' + # 转换色彩空间 HSV + img_hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV) + # 根据颜色阈值转换为二值化图像 + img_bin = cv2.inRange(img_hsv, lowerb, upperb) + + # 寻找轮廓(只寻找最外侧的色块) + bimg, contours, hier = cv2.findContours(img_bin, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) + # 声明画布 拷贝自img + canvas = np.copy(img) + # 外接矩形区域集合 + rects = [] + + if max_w is None: + # 如果最大宽度没有设定,就设定为图像的宽度 + max_w = img.shape[1] + if max_h is None: + # 如果最大高度没有设定,就设定为图像的高度 + max_h = img.shape[0] + + # 遍历所有的边缘轮廓集合 + for cidx,cnt in enumerate(contours): + # 获取联通域的外界矩形 + (x, y, w, h) = cv2.boundingRect(cnt) + + if w >= min_w and w <= max_w and h >= min_h and h <= max_h: + # 将矩形的信息(tuple)添加到rects中 + rects.append((x, y, w, h)) + return rects + +def draw_color_block_rect(img, rects,color=(0, 0, 255)): + ''' + 绘制色块的矩形区域 + ''' + # 声明画布(canvas) 拷贝自img + canvas = np.copy(img) + # 遍历矩形区域 + for rect in rects: + (x, y, w, h) = rect + # 在画布上绘制矩形区域(红框) + cv2.rectangle(canvas, pt1=(x, y), pt2=(x+w, y+h),color=color, thickness=3) + + return canvas \ No newline at end of file diff --git "a/04-\350\211\262\345\235\227\350\257\206\345\210\253/color_feature.pyc" "b/04-\350\211\262\345\235\227\350\257\206\345\210\253/color_feature.pyc" new file mode 100644 index 0000000..b632ee7 Binary files /dev/null and "b/04-\350\211\262\345\235\227\350\257\206\345\210\253/color_feature.pyc" differ diff --git "a/04-\350\211\262\345\235\227\350\257\206\345\210\253/demo-pic.png" "b/04-\350\211\262\345\235\227\350\257\206\345\210\253/demo-pic.png" new file mode 100644 index 0000000..d9792f2 Binary files /dev/null and "b/04-\350\211\262\345\235\227\350\257\206\345\210\253/demo-pic.png" differ diff --git "a/04-\350\211\262\345\235\227\350\257\206\345\210\253/demo-video.mkv" "b/04-\350\211\262\345\235\227\350\257\206\345\210\253/demo-video.mkv" new file mode 100644 index 0000000..56af8e4 Binary files /dev/null and "b/04-\350\211\262\345\235\227\350\257\206\345\210\253/demo-video.mkv" differ diff --git "a/04-\350\211\262\345\235\227\350\257\206\345\210\253/test_color_feature.py" "b/04-\350\211\262\345\235\227\350\257\206\345\210\253/test_color_feature.py" new file mode 100644 index 0000000..53c01e0 --- /dev/null +++ "b/04-\350\211\262\345\235\227\350\257\206\345\210\253/test_color_feature.py" @@ -0,0 +1,89 @@ +# -*- coding: utf-8 -*- +''' +颜色特征识别测试代码 +''' +import numpy as np +import cv2 +from color_feature import color_block_finder,draw_color_block_rect + +def test_color_block_finder_01(): + ''' + 色块识别测试样例1 从图片中读取并且识别 + ''' + # 图片路径 + img_path = "demo-pic.png" + # 颜色阈值下界(HSV) lower boudnary + lowerb = (96, 210, 85) + # 颜色阈值上界(HSV) upper boundary + upperb = (114, 255, 231) + + # 读入素材图片 BGR + img = cv2.imread(img_path, cv2.IMREAD_COLOR) + # 检查图片是否读取成功 + if img is None: + print("Error: 请检查图片文件路径") + exit(1) + + # 识别色块 获取矩形区域数组 + rects = color_block_finder(img, lowerb, upperb) + # 绘制色块的矩形区域 + canvas = draw_color_block_rect(img, rects) + # 在HighGUI窗口 展示最终结果 + cv2.namedWindow('result', flags=cv2.WINDOW_NORMAL | cv2.WINDOW_FREERATIO) + cv2.imshow('result', canvas) + + # 等待任意按键按下 + cv2.waitKey(0) + # 关闭其他窗口 + cv2.destroyAllWindows() + +def test_color_block_finder_02(): + ''' + 色块识别测试样例2 从视频流中读取并且识别 + ''' + # 视频路径 + video_path = 'demo-video.mkv' + # 颜色阈值下界(HSV) lower boudnary + lowerb = (96, 210, 85) + # 颜色阈值上界(HSV) upper boundary + upperb = (114, 255, 231) + + + # 读入视频流 + cap = cv2.VideoCapture(video_path) + # 色块识别结果展示 + cv2.namedWindow('result', flags=cv2.WINDOW_NORMAL | cv2.WINDOW_FREERATIO) + + while(True): + # 逐帧获取画面 + # ret ? 画面是否获取成功 + ret, frame = cap.read() + + if ret: + img = frame + # 识别色块 获取矩形区域数组 + # 同时设定最小高度还有宽度,过滤噪声 + rects = color_block_finder(img, lowerb, upperb,min_w=10,min_h=10) + # 绘制色块的矩形区域 + canvas = draw_color_block_rect(img, rects) + # 在HighGUI窗口 展示最终结果 更新画面 + cv2.imshow('result', canvas) + + else: + print("视频读取完毕或者视频路径异常") + break + + # 这里做一下适当的延迟,每帧延时0.1s钟 + if cv2.waitKey(50) & 0xFF == ord('q'): + break + + # 释放资源 + cap.release() + cv2.destroyAllWindows() + + +if __name__ == "__main__": + # 测试图片色块识别 + # test_color_block_finder_01() + # 测试视频流色块识别 + test_color_block_finder_02() diff --git "a/04-\350\211\262\345\235\227\350\257\206\345\210\253/\346\274\224\347\244\272\350\247\206\351\242\221/04-\346\265\213\350\257\225\350\247\206\351\242\221-\350\211\262\345\235\227\350\257\206\345\210\253.mkv" "b/04-\350\211\262\345\235\227\350\257\206\345\210\253/\346\274\224\347\244\272\350\247\206\351\242\221/04-\346\265\213\350\257\225\350\247\206\351\242\221-\350\211\262\345\235\227\350\257\206\345\210\253.mkv" new file mode 100644 index 0000000..2a82a3e Binary files /dev/null and "b/04-\350\211\262\345\235\227\350\257\206\345\210\253/\346\274\224\347\244\272\350\247\206\351\242\221/04-\346\265\213\350\257\225\350\247\206\351\242\221-\350\211\262\345\235\227\350\257\206\345\210\253.mkv" differ diff --git "a/04-\350\211\262\345\235\227\350\257\206\345\210\253/\350\211\262\345\235\227\350\257\206\345\210\253\346\274\224\347\244\272\346\240\267\344\276\213.png" "b/04-\350\211\262\345\235\227\350\257\206\345\210\253/\350\211\262\345\235\227\350\257\206\345\210\253\346\274\224\347\244\272\346\240\267\344\276\213.png" new file mode 100644 index 0000000..dacf59c Binary files /dev/null and "b/04-\350\211\262\345\235\227\350\257\206\345\210\253/\350\211\262\345\235\227\350\257\206\345\210\253\346\274\224\347\244\272\346\240\267\344\276\213.png" differ diff --git a/README.md b/README.md new file mode 100644 index 0000000..155e3ed --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# OpenCV色块识别-例程演示 + + diff --git a/TODO.md b/TODO.md new file mode 100644 index 0000000..e69de29