使用 OpenCV 构建你的第一个可视化工具

点击上方“小白学视觉”,选择加"星标"或“置顶

重磅干货,第一时间送达

介绍

在当今世界,我们看到数以千计的优秀产品,以及销售这些产品的跨国公司。

它们的共同点是它们是从一个想法不断发展而来的。每天,我们的脑海中都会有几十个想法,让我们深入思考并深入想象。

从为窗帘选择颜色到选择算法,我们必须在脑海中想象它。有时最艰难的决定是在几秒钟内做出的,选择一种颜色会让我们思考几个小时。

为了消除问题并节省我们的时间,我们可以在可视化器和虚拟器的帮助下创建更强大的图片,而不是将我们的想法束缚在我们的脑中。

什么是可视化器?

可视化器是一种将某人的想象力变为现实的工具。它提供了最终产品外观图。

假设一家公司想要制造一辆能飞的汽车,并且需要为此进行投资。为了推销他们的想法,可视化工具将付诸行动。它将展示汽车的设计、它的工作原理以及它对市场的影响。

可视化工具更像是一种用于展示实际产品的工具。此外,就像编写算法有助于进一步编写代码一样,可视化工具有助于实现最佳最终产品。

优点和应用

  • 可视化工具有助于节省成本和时间。

  • 由于使用了可视化工具,产品中需要的改动更少。

  • 它为最终产品创造了一个更为形象化的结构。

  • 许多公司已经开始使用可视化工具在用户购买之前向用户展示他们的产品。因此,它还提供客户满意度,并建立消费者对公司的信任。

在本文中,我们将创建一个这样的可视化器,它将帮助我们在房间墙壁上尝试各种颜色和图案组合。

通过这个可视化工具,用户将能够扩展他们的想象力并选择最适合他们的选项。它可能看起来不像你期望的那样,但足以让你了解这个概念。

我们将使用 OpenCV 和 NumPy 的一些基本功能来完成这项任务。在文章的最后,我还将分享改进的范围和你可以工作的部分,以使这个项目更上一层楼。

项目的灵魂是我们将要创造的蒙版,蒙版的质量将决定结果的质量。所以有更好的蒙版,会有更好的结果。

开始吧

  1. 我们首先接受房间图像和纹理。它们的大小可能不同,不过我们都会调整它们的大小。然后我们将图像转换为HSV图像格式。

    为什么是 HSV 而不是任何其他颜色格式?看看这个:https://www.analyticsvidhya.com/blog/2021/08/getting-started-with-object-tracking-using-opencv/。

    之后,我们需要做的是获取墙壁的颜色,过滤掉该颜色并创建一个蒙版图像。

    为了获取墙壁的颜色,我们将使用 cv2.setMouseCallback() 函数创建鼠标事件。我们还必须创建一个单击事件函数,该函数将在鼠标开始动作时被调用。

import cv2
import numpy as np
def click_event1(event, x, y , flags, param):
        global draw, mask, lb, ub
        if event==cv2.EVENT_LBUTTONDOWN:
                print(img[y,x], hsv[y,x])
                lb = hsv[y,x]-10
                ub = hsv[y,x]+10
img = cv2.imread("room2.jpeg")
tex = cv2.imread("tex2.jpg")
img = cv2.GaussianBlur(img,(5,5),0)
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
cv2.imshow("img",img)
cv2.setMouseCallback('img', click_event1)
cv2.waitKey(0)

aa6ce5d86537fce499547cf7821cd854.png

对于本文,我们将使用上面的图像和相邻的图案。在这里(https://drive.google.com/drive/folders/1uvYtpFHrKuOZgKzYLM-722Yfj7O5CPRl?usp=sharing)下载。

你可以选择你喜欢的任何图像和图案。在这一步中,用户必须做的是左键单击墙上的任意位置,我们将获取该像素值。之后,我们将通过从每个通道添加和减去 10 来使用该像素创建一个颜色范围。

  1. 如你所知,看起来与我们相似的颜色,其像素值可能略有不同。所以,如果你认为使用上述步骤中的像素值就足够了,那你就错了。

    假设我们抓取的像素值为(185, 177, 164)。邻居的像素值可能是 (186, 177, 164)。因此,相反,我们创建了一系列可以显示颜色的值。

    为了使任务更可靠,我们将使用轨迹栏来找到正确的阈值。我们可以稍后移动轨迹栏以找到阈值的最佳值并创建我们的蒙版。

def nothing(x):
        pass
def resize(img):
 return cv2.resize(img,(512,512))
cv2.namedWindow('Tracking')
# creating track bars
cv2.createTrackbar('LH','Tracking',0,255,nothing)
cv2.createTrackbar('LS','Tracking',0,255,nothing)
cv2.createTrackbar('LV','Tracking',0,255,nothing)
cv2.createTrackbar('UH','Tracking',255,255,nothing)
cv2.createTrackbar('US','Tracking',255,255,nothing)
cv2.createTrackbar('UV','Tracking',255,255,nothing)
# Setting trackbar orignial position to lower bound(lb) and upper bound(ub)
cv2.setTrackbarPos("LH",'Tracking',lb[0])
cv2.setTrackbarPos("LS",'Tracking',lb[1])
cv2.setTrackbarPos("LV",'Tracking',lb[2])
cv2.setTrackbarPos("UH",'Tracking',ub[0])
cv2.setTrackbarPos("US",'Tracking',ub[1])
cv2.setTrackbarPos("UV",'Tracking',ub[2])
print("Move the meters, q for next")
while True:
        # getting trackbar position
        l_h=cv2.getTrackbarPos('LH','Tracking')
        l_s=cv2.getTrackbarPos('LS','Tracking')
        l_v=cv2.getTrackbarPos('LV','Tracking')
        u_h=cv2.getTrackbarPos('UH','Tracking')
        u_s=cv2.getTrackbarPos('US','Tracking')
        u_v=cv2.getTrackbarPos('UV','Tracking')
        lb=np.array([l_h, l_s, l_v])
        ub = np.array([u_h,u_s,u_v])
        mask = cv2.inRange(hsv, lb, ub)
        cv2.imshow("mask",resize(mask))
        cv2.imshow("img", resize(img))
        key = cv2.waitKey(1)
        if key==113:                
                break

7767dd6b63f581310d6786d5effd0fbf.png10b0d2a06441bf2e405cacc2c2d60146.png

我们设置步骤1中计算的下限和上限值,结果如上(左图)。显然,一些像素仍然超出范围。为此,我们将使用我们创建的轨迹栏来手动调整值。就我而言,稍微增加 UV 轨迹栏就足够了。

你可以在正确的图像中看到结果。我还附上了我自己使用的阈值。

  1. 如果阈值不能给出好的结果,我们可以创建另一个函数来允许我们在蒙版图像上绘制。我们可以手动绘制我们的蒙版图像,以改进它。

    为此,我们将创建另一个鼠标事件 click_event。click_event() 使用具有三种模式的变量“绘制”。

    0,当我们不想画图,我们只是单纯移动光标时;

    1,使用鼠标左键绘制正遮罩(白色);

    2,使用鼠标右键绘制负遮罩(黑色)。

draw = 0 # 0->no draw, 1-> +mask, 2-> -mask
def click_event(event, x, y , flags, param):
        global draw, mask
        if event==cv2.EVENT_LBUTTONDOWN:
                draw = 1
        elif event==cv2.EVENT_RBUTTONDOWN:
                draw = 2
        elif event==cv2.EVENT_MOUSEMOVE:
                if draw == 1:
                        cv2.circle(mask,(x,y),10, (255), -1)
                elif draw == 2:
                        cv2.circle(mask,(x,y),10, (0), -1)
                cv2.imshow('mask',mask)
                cv2.waitKey(1)
        elif event== cv2.EVENT_LBUTTONUP:
                draw = 0
        elif event==cv2.EVENT_RBUTTONUP:
                draw = 0
mask = cv2.inRange(hsv, lb, ub)
cv2.imshow("mask",mask)
cv2.setMouseCallback('mask', click_event)
cv2.imshow("mask",mask)
cv2.waitKey(0)
cv2.destroyAllWindows()

ea6bc02ae254f1da5106cef7c5352b33.png

我在墙上的小创意,但我希望你明白这张图的目的是什么。

我们可以选择要包含在蒙版中的区域以及要排除的区域。

  1. 现在我们的蒙版图像已经准备好了。大部分工作已经完成,奇迹即将发生。

    我们将使用纹理图像并使用 cv2.bitwise_and() 函数在其上应用蒙版。这只会给我们修改后的墙。

    接下来,我们将反转蒙版并将其应用于我们的输入图像。所以,我们有一张带有修改过的墙的图像和一张除了墙外的图像。现在最后一步是添加这两个图像,我们的结果已经准备好了。

tex = cv2.bitwise_and(tex, tex, mask=mask)
mask_i = cv2.bitwise_not(mask)
img = cv2.bitwise_and(img,img,mask=mask_i)
res = tex+img # our final image

79b57439f1626a2d20a66552c8ffa35c.png

为了更好地理解,你可以使用 cv2.imshow() 分别查看纹理(tex)和图像(img)部分,然后再使用 cv2.bitwise_and() 添加它们。

最后,我们的代码看起来像这样。

import cv2
import numpy as np

draw = 0 # 0->no draw, 1-> +mask, 2-> -mask
def nothing(x):
        pass
def resize(img, size=(512,512)):
        img2 = cv2.resize(img,size)
        return img2
# grab the color of the wall and provide the lower and upper value
def click_event1(event, x, y , flags, param):
        global draw, mask, lb, ub
        if event==cv2.EVENT_LBUTTONDOWN:
                print("RGB: ",img[y,x], "HSV: ", hsv[y,x])
                lb = hsv[y,x]-10
                ub = hsv[y,x]+10
def click_event(event, x, y , flags, param):
        global draw, mask
        if event==cv2.EVENT_LBUTTONDOWN:
                draw = 1
        elif event==cv2.EVENT_RBUTTONDOWN:
                draw = 2
        elif event==cv2.EVENT_MOUSEMOVE:
                if draw == 1:
                        cv2.circle(mask,(x,y),10, (255), -1)
                elif draw == 2:
                        cv2.circle(mask,(x,y),10, (0), -1)
                cv2.imshow('mask',mask)
                cv2.waitKey(1)
        elif event== cv2.EVENT_LBUTTONUP:
                draw = 0
        elif event==cv2.EVENT_RBUTTONUP:
                draw = 0
cv2.namedWindow('Tracking')
cv2.createTrackbar('LH','Tracking',0,255,nothing)
cv2.createTrackbar('LS','Tracking',0,255,nothing)
cv2.createTrackbar('LV','Tracking',0,255,nothing)
cv2.createTrackbar('UH','Tracking',255,255,nothing)
cv2.createTrackbar('US','Tracking',255,255,nothing)
cv2.createTrackbar('UV','Tracking',255,255,nothing)
img = cv2.imread("room2.jpeg") # room image
tex = cv2.imread("tex2.jpg") # pattern
# https://www.feathr.com/wp-content/uploads/maximalist-living-room-wallpaper.jpg
img = cv2.GaussianBlur(img,(5,5),0)
print("img shape - ",img.shape)
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
# gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
cv2.imshow("img",img)
cv2.setMouseCallback('img', click_event1)
print("Select your wall. Press any key for next")
################ select wall ###########
cv2.waitKey(0)
cv2.setTrackbarPos("LH",'Tracking',lb[0])
cv2.setTrackbarPos("LS",'Tracking',lb[1])
cv2.setTrackbarPos("LV",'Tracking',lb[2])
cv2.setTrackbarPos("UH",'Tracking',ub[0])
cv2.setTrackbarPos("US",'Tracking',ub[1])
cv2.setTrackbarPos("UV",'Tracking',ub[2])
print("Move the meters, q for next")
while True:
        l_h=cv2.getTrackbarPos('LH','Tracking')
        l_s=cv2.getTrackbarPos('LS','Tracking')
        l_v=cv2.getTrackbarPos('LV','Tracking')
        u_h=cv2.getTrackbarPos('UH','Tracking')
        u_s=cv2.getTrackbarPos('US','Tracking')
        u_v=cv2.getTrackbarPos('UV','Tracking')
        lb=np.array([l_h, l_s, l_v])
        ub = np.array([u_h,u_s,u_v])
        mask = cv2.inRange(hsv, lb, ub)
        cv2.imshow("mask",resize(mask))
        cv2.imshow("img", resize(img))
        key = cv2.waitKey(1)
        if key==113:                
                break
print("lb and ub",lb, ub)
mask = cv2.inRange(hsv, lb, ub)
# red =np.zeros(img.shape, np.uint8) # In case you want single color
# red[:,:,1] = 255 # replace me with any color
tex = cv2.resize(tex, (img.shape[1],img.shape[0]))
cv2.setMouseCallback('mask', click_event)
# cv2.imshow("texture",tex)
# print(tex.shape)
print("Paint your mask, if you need to. press any key for next")
####### mask breakpoint ##########
cv2.waitKey(0)
tex = cv2.bitwise_and(tex, tex, mask=mask)
mask_i = cv2.bitwise_not(mask)
img = cv2.bitwise_and(img,img,mask=mask_i)
res = tex+img
cv2.imshow("result",resize(res))
print("Thank you !!!")
cv2.waitKey(0)
cv2.destroyAllWindows()

699a3b02e8a07cfea5dc76edc854d22f.png

你可以从上图中看到我们取得的进展。

然后做什么?

恭喜,你已经创建了你的第一个可视化工具。你一定对你的下一个项目感到兴奋。不过,如果你愿意,你也可以在此方面进行一些改进。你可能已经注意到,结果不是很好,这里仍有改进的余地。

首先,你可以想出一些方法来获得更好的上下限范围,而不是添加一些随机值。你也可以做一些预处理。使用 cv2.GaussianBlur() 进行预处理,它确实提供了更好的蒙版。

好消息!

小白学视觉知识星球

开始面向外开放啦👇👇👇

 
 

2cf125ca22f76ce53412ec67c90b1a02.jpeg

下载1:OpenCV-Contrib扩展模块中文版教程

在「小白学视觉」公众号后台回复:扩展模块中文教程,即可下载全网第一份OpenCV扩展模块教程中文版,涵盖扩展模块安装、SFM算法、立体视觉、目标跟踪、生物视觉、超分辨率处理等二十多章内容。


下载2:Python视觉实战项目52讲
在「小白学视觉」公众号后台回复:Python视觉实战项目,即可下载包括图像分割、口罩检测、车道线检测、车辆计数、添加眼线、车牌识别、字符识别、情绪检测、文本内容提取、面部识别等31个视觉实战项目,助力快速学校计算机视觉。


下载3:OpenCV实战项目20讲
在「小白学视觉」公众号后台回复:OpenCV实战项目20讲,即可下载含有20个基于OpenCV实现20个实战项目,实现OpenCV学习进阶。


交流群

欢迎加入公众号读者群一起和同行交流,目前有SLAM、三维视觉、传感器、自动驾驶、计算摄影、检测、分割、识别、医学影像、GAN、算法竞赛等微信群(以后会逐渐细分),请扫描下面微信号加群,备注:”昵称+学校/公司+研究方向“,例如:”张三 + 上海交大 + 视觉SLAM“。请按照格式备注,否则不予通过。添加成功后会根据研究方向邀请进入相关微信群。请勿在群内发送广告,否则会请出群,谢谢理解~
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
二维Catmull-Rom细分算法实现: ```c++ #include <iostream> #include <vector> #include "opencv2/opencv.hpp" using namespace cv; using namespace std; vector<Point2f> catmullRom(vector<Point2f> points, float alpha) { vector<Point2f> result; for (int i = 1; i < points.size() - 2; i++) { Point2f p0 = points[i - 1]; Point2f p1 = points[i]; Point2f p2 = points[i + 1]; Point2f p3 = points[i + 2]; float t0 = 0; float t1 = powf(powf((p1.x - p0.x), 2.0f) + powf((p1.y - p0.y), 2.0f), 0.5f); float t2 = t1 + powf(powf((p2.x - p1.x), 2.0f) + powf((p2.y - p1.y), 2.0f), 0.5f); float t3 = t2 + powf(powf((p3.x - p2.x), 2.0f) + powf((p3.y - p2.y), 2.0f), 0.5f); for (float t = t1; t < t2; t += alpha) { float a1 = (t1 - t) / (t1 - t0); float a2 = (t - t0) / (t1 - t0); Point2f p = a1 * p0 + a2 * p1; a1 = (t2 - t) / (t2 - t1); a2 = (t - t1) / (t2 - t1); p += a1 * p1 + a2 * p2; a1 = (t3 - t) / (t3 - t2); a2 = (t - t2) / (t3 - t2); p += a1 * p2 + a2 * p3; p *= 0.5f; result.push_back(p); } } return result; } int main(int argc, char** argv) { vector<Point2f> points = { Point2f(100, 100), Point2f(200, 100), Point2f(200, 200), Point2f(100, 200) }; float alpha = 0.01f; vector<Point2f> result = catmullRom(points, alpha); Mat img = Mat::zeros(300, 300, CV_8UC3); for (int i = 0; i < points.size(); i++) { circle(img, points[i], 3, Scalar(0, 255, 0), -1); } for (int i = 0; i < result.size(); i++) { circle(img, result[i], 1, Scalar(0, 0, 255), -1); } for (int i = 0; i < points.size() - 1; i++) { line(img, points[i], points[i + 1], Scalar(0, 255, 0), 1); } for (int i = 0; i < result.size() - 1; i++) { line(img, result[i], result[i + 1], Scalar(0, 0, 255), 1); } imshow("Catmull-Rom", img); waitKey(0); return 0; } ``` 这里我们先定义了一个`catmullRom`函数来实现二维Catmull-Rom细分算法。传入参数为一个点集和步长alpha,返回值为细分后的点集。在函数内部,我们首先对每个点进行处理,得到其所在的四个点p0、p1、p2、p3和其各自在总长度上的位置t0、t1、t2、t3。接着,我们对p1和p2之间的一段进行细分,每次细分按照步长alpha进行。 在`main`函数中,我们传入一个点集和步长alpha,得到细分后的点集,并在图像上绘制出来。 运行结果如下: ![catmull-rom](https://s1.ax1x.com/2020/03/31/GUhOY9.png)

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值