当前位置:   article > 正文

基于OpenCV的图像分割处理!

opencv图像分割

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

重磅干货,第一时间送达

图像阈值化分割是一种传统的最常用的图像分割方法,因其实现简单、计算量小、性能较稳定而成为图像分割中最基本和应用最广泛的分割技术。它特别适用于目标和背景占据不同灰度级范围的图像。它不仅可以极大的压缩数据量,而且也大大简化了分析和处理步骤,因此在很多情况下,是进行图像分析、特征提取与模式识别之前的必要的图像预处理过程。

阈值处理是指剔除图像内像素值高于阈值或者低于阈值得像素点。例如,设定阈值为127,将图像内所有像素值大于127的像素点的值设为255;将图像内所有像素值小于127的像素点的值设为0。

图像阈值化的目的是要按照灰度级,对像素集合进行一个划分,得到的每个子集形成一个与现实景物相对应的区域,各个区域内部具有一致的属性,而相邻区域不具有这种一致属性。这样的划分可以通过从灰度级出发选取一个或多个阈值来实现。

学习目标

  • 了解阈值分割基本概念

  • 理解最大类间方差法(大津法)、自适应阈值分割的原理

  • 掌握OpenCV框架下上述阈值分割算法API的使用

算法理论介绍

阈值处理

threshold函数

OpenCV使用threshold函数实现阈值化处理。

  1. double cv::threshold ( InputArray src,
  2. OutputArray dst,
  3. double thresh,
  4. double maxval,
  5. int type )

参数

  • src — 原图像,8或32位浮点类型的Mat。

  • dst —  输出图像,与原始图像具有相同大小和类型。

  • thresh —  要设定的阈值

  • maxval   — 当type为THRESH_BINARY或者THRESH_BINARY_INV时,设定的最大值

  • type — 阈值分割的类型

    • THRESH_BINARY;二值化阈值处理:灰度值大于阈值的点,将其灰度值设定为最大值,灰度值小于或等于阈值的点,将其灰度值设定为0

    • THRESH_BINARY_INV;反二值化阈值处理:灰度值大于阈值的点,将其灰度值设定为0,灰度值小于或等于阈值的点,将其灰度值设定为最大值

    • THRESH_TRUNC;截断阈值化处理:灰度值大于阈值的点,将其灰度值设定为阈值,灰度值小于或等于阈值的点,其灰度值保持不变

    • THRESH_TOZERO;低阈值零处理:灰度值大于阈值的点,其灰度值保持不变,灰度值小于或等于阈值的点,将其灰度值设定为0

    • THRESH_TOZERO_INV;高阈值零处理:灰度值大于阈值的点,将其灰度值设定为0,灰度值小于或等于阈值的点,其灰度值保持不变

如下表:

6d3e6ad6156a8694745b6c9bebb5bb38.png

OTSU(大津法)

使用threshold进行阈值处理时,需要自定义一个阈值,并以此阈值作为图像阈值处理的依据 。通常情况下对于色彩均衡的图像,直接将阈值设为127即可,但有时图像灰度级的分布是不均衡的,如果此时还将阈值设为127,那么阈值处理的结果就是失败的。所以需要找出图像的最佳的分割阈值。OTSU就是获得最佳阈值的方法。

OTSU(大津法)是一种确定图像二值化分割阈值的算法,由日本学者大津于1979年提出。从大津法的原理上来讲,该方法又称作最大类间方差法,因为按照大津法求得的阈值进行图像二值化分割后,前景与背景图像的类间方差最大。

它被认为是图像分割中阈值选取的最佳算法,计算简单,不受图像亮度和对比度的影响,因此在数字图像处理上得到了广泛的应用。它是按图像的灰度特性,将图像分成背景和前景两部分。因方差是灰度分布均匀性的一种度量,背景和前景之间的类间方差越大,说明构成图像的两部分的差别越大,当部分前景错分为背景或部分背景错分为前景都会导致两部分差别变小。因此,使类间方差最大的分割意味着错分概率最小。

OTSU 是求图像全局阈值的最佳方法,适用于大部分需要求图像全局阈值的场合。

缺点:对图像噪声敏感;只能针对单一目标分割;当图像中的目标与背景的面积相差很大时,表现为直方图没有明显的双峰,或者两个峰的大小相差很大,分割效果不佳,或者目标与背景的灰度有较大的重叠时也不能准确的将目标与背景分开。导致这种现象出现的原因是该方法忽略了图像的空间信息,同时该方法将图像的灰度分布作为分割图像的依据,因而对噪声也相当敏感。所以,在实际应用中,总是将其与其他方法结合起来使用。

图像直方图

ca9b09cad2945fcf983a5aebfa6295af.png

效果:

ff680f69470a3cd9862dd05e90de9ff1.png

图像直方图:

46d94899da71c06810c1508c7854cc46.png

效果:

9b4bbc2a91e7318b40942ddabeada6df.png


OTSU求阈值过程:

假设图像的像素个数为M×N。假设存在阈值T将图像所有像素分为两类C1(像素值小于T)和C2(像素值大于T)。假设图片背景较暗,则C1类为背景,C2类为前景。像素被分为C1和C2类的概率分别为p1、p2。图像中属于C1类的像素个数记作N1,其平均灰度;属于C2类的的像素个数记作N2,其平均灰度为。图像的总平均灰度记为,类间方差记为。

因此有如下关系式:

aa24f138e09477676feab20f48546039.png

把带入类间方差公式,化简,可以得到:

be5a49f1d693ee868aaf63d2e75e7302.png

L为灰度级数,为灰度级为的像素点数

faee7e24fa29d69c2e799662638d8767.png

小于或等于灰度级K的累加均值为:

0ae6d7f5af79350b63b6033cbcb0bde6.png

所以,

003ce19a27849ed8ec71cb12458457a4.png

类间方差公式可以化为:

3b259657be14c0019c838098b020c80c.png

求得使最大的灰度级K,就是OTSU的阈值。OTSU方法会遍历所有灰度级,找到最佳阈值。

自适应阈值处理

前面介绍了OTSU算法,但这算法还属于全局阈值法,即整张图片只有一个阈值。所以对于某些光照不均的图像,这种方法无法得到清晰有效的阈值分割结果图像,如下图:

a559e8197f990fa99fb840b3e68d5165.png

显然,这样的阈值处理结果不是我们想要的,所以需要使用变化的阈值对图像进行分割,这种技术称为自适应阈值处理方式。它的思想不是计算全局图像的阈值,而是根据图像不同区域亮度分布,计算其局部阈值,所以对于图像不同区域,能够自适应计算不同的阈值,因此被称为自适应阈值法。

确定局部阈值的方法:计算每个像素点周围临近区域的加权平均值获得阈值,并使用该阈值对该像素点进行处理。

adaptiveThreshold函数

OpenCV提供了adaptiveThreshold函数实现自适应阈值处理。

  1. void adaptiveThreshold(InputArray src, OutputArray dst,
  2. double maxValue,
  3. int adaptiveMethod,
  4. int thresholdType,
  5. int blockSize, double C)

参数:

  • src — 原图像,8或32位浮点类型的Mat。必须为单通道灰度图像。

  • dst — 输出图像,与原始图像具有相同大小和类型。

  • maxValue — 像素最大值

  • adaptiveMethod — 自适应方法,只有两种:THRESH_BINARY 和THRESH_BINARY_INV。

  • thresholdType — 阈值计算方式,有两种:

    • ADAPTIVE_THRESH_MEAN_C:计算方法是计算出领域内的像素平均值再减去C的值

    • ADAPTIVE_THRESH_GAUSSIAN_C:计算方法是计算出领域内像素的高斯均值再减去C的值

  • blockSize — 表示一个像素在计算阈值时使用的邻域尺寸,通常为3、5、7。

  • C — 常数,用均值或高斯计算阈值后,再减去C就是最终阈值。

基于OpenCV的实现

c++实现

1. 阈值处理
  1. #include <opencv2/opencv.hpp>
  2. #include <iostream>
  3. using namespace cv;
  4. using namespace std;
  5. int main()
  6. {
  7. //载入图像
  8. Mat img = imread("D:\\yt\\picture\\threshold\\s.jpg");
  9. if (img.empty())
  10. {
  11. cout << "Error: Could not load image" << endl;
  12. return 0;
  13. }
  14. Mat gray;
  15. cvtColor(img, gray, COLOR_BGR2GRAY);//先转为灰度图
  16. Mat dst1,dst2,dst3,dst4,dst5;
  17. threshold(gray, dst1, 127, 255, THRESH_BINARY);//二值化阈值处理
  18. threshold(gray, dst2, 127, 255, THRESH_BINARY_INV);//反二值化阈值处理
  19. threshold(gray, dst3, 127, 255, THRESH_TRUNC);//截断阈值化处理
  20. threshold(gray, dst4, 127, 255, THRESH_TOZERO_INV);//超阈值零处理
  21. threshold(gray, dst5, 127, 255, THRESH_TOZERO);//低阈值零处理
  22. //显示图像
  23. imshow("gray_image", gray);
  24. imshow("THRESH_BINARY", dst1);
  25. imshow("THRESH_BINARY_INV", dst2);
  26. imshow("THRESH_TRUNC", dst3);
  27. imshow("THRESH_TOZERO_INV", dst4);
  28. imshow("THRESH_TOZERO", dst5);
  29. waitKey(0);
  30. return 0;
  31. }
效果
二值化阈值处理:

6c98d57b651817e34e392b55d4829c69.png

反二值化阈值处理:

cfe3b1cc1ffd79f3e110275caadc2719.png

截断阈值化处理:

097914eec550503bb487753833a80f7f.png

超阈值零处理:

e6bf8f9bfc287b523b9eb76c37af2899.png

低阈值零处理:

b719f4f33996c19475fd1beaa2c30819.png

2. OTSU处理

在OpenCV中,设定参数type为“THRESH_OTSU”即可实现OTSU方式的阈值分割。且设定阈值thresh为0。

  1. #include <opencv2/opencv.hpp>
  2. #include <iostream>
  3. using namespace cv;
  4. using namespace std;
  5. int main()
  6. {
  7. //载入图像
  8. Mat img = imread("D:\\yt\\picture\\threshold\\s.jpg");
  9. if (img.empty())
  10. {
  11. cout << "Error: Could not load image" << endl;
  12. return 0;
  13. }
  14. Mat gray;
  15. cvtColor(img, gray, COLOR_BGR2GRAY);//先转为灰度图
  16. Mat dst;
  17. threshold(gray, dst, 0, 255, THRESH_OTSU);//OTSU
  18. //显示图像
  19. imshow("gray_image", gray);
  20. imshow("THRESH_OTSU", dst);
  21. waitKey(0);
  22. return 0;
  23. }
效果

c518c5b398ea53aa77b18f8167ea6764.png

3. 自适应阈值处理
  1. #include <opencv2/opencv.hpp>
  2. #include <iostream>
  3. using namespace cv;
  4. using namespace std;
  5. int main()
  6. {
  7. //载入图像
  8. Mat img = imread("D:\\yt\\picture\\threshold\\1.jpg");
  9. if (img.empty())
  10. {
  11. cout << "Error: Could not load image" << endl;
  12. return 0;
  13. }
  14. Mat gray;
  15. cvtColor(img, gray, COLOR_BGR2GRAY);//先转为灰度图
  16. Mat dst;
  17. adaptiveThreshold(gray, dst, 255, ADAPTIVE_THRESH_MEAN_C, THRESH_BINARY, 7, 10);
  18. //创建窗口,WINDOW_NORMAL使窗口可以自由调节大小
  19. namedWindow("gray_image",WINDOW_NORMAL);
  20. namedWindow("adaptiveThreshold", WINDOW_NORMAL);
  21. //显示图像
  22. imshow("gray_image", gray);
  23. imshow("adaptiveThreshold", dst);
  24. waitKey(0);
  25. return 0;
  26. }
效果

8c93dcedb933ad74a1d557d82fdb6d41.png

进阶实现(根据原理自己实现)

实现示例(c++)

1. OTSU处理
  1. #include <iostream>
  2. #include <opencv2/core.hpp>
  3. #include <opencv2/highgui.hpp>
  4. #include <opencv2/imgproc.hpp>
  5. int Otsu(cv::Mat& src, cv::Mat& dst, int thresh){
  6. const int Grayscale = 256;
  7. int graynum[Grayscale] = { 0 };
  8. int r = src.rows;
  9. int c = src.cols;
  10. for (int i = 0; i < r; ++i){
  11. const uchar* ptr = src.ptr<uchar>(i);
  12. for (int j = 0; j < c; ++j){ //直方图统计
  13. graynum[ptr[j]]++;
  14. }
  15. }
  16. double P[Grayscale] = { 0 };
  17. double PK[Grayscale] = { 0 };
  18. double MK[Grayscale] = { 0 };
  19. double srcpixnum = r*c, sumtmpPK = 0, sumtmpMK = 0;
  20. for (int i = 0; i < Grayscale; ++i){
  21. P[i] = graynum[i] / srcpixnum; //每个灰度级出现的概率
  22. PK[i] = sumtmpPK + P[i]; //概率累计和
  23. sumtmpPK = PK[i];
  24. MK[i] = sumtmpMK + i*P[i]; //灰度级的累加均值
  25. sumtmpMK = MK[i];
  26. }
  27. //计算类间方差
  28. double Var=0;
  29. for (int k = 0; k < Grayscale; ++k){
  30. if ((MK[Grayscale-1] * PK[k] - MK[k])*(MK[Grayscale-1] * PK[k] - MK[k]) / (PK[k] * (1 - PK[k])) > Var){
  31. Var = (MK[Grayscale-1] * PK[k] - MK[k])*(MK[Grayscale-1] * PK[k] - MK[k]) / (PK[k] * (1 - PK[k]));
  32. thresh = k;
  33. }
  34. }
  35. //阈值处理
  36. src.copyTo(dst);
  37. for (int i = 0; i < r; ++i){
  38. uchar* ptr = dst.ptr<uchar>(i);
  39. for (int j = 0; j < c; ++j){
  40. if (ptr[j]> thresh)
  41. ptr[j] = 255;
  42. else
  43. ptr[j] = 0;
  44. }
  45. }
  46. return thresh;
  47. }
  48. int main(){
  49. cv::Mat src = cv::imread("D:\\yt\\picture\\threshold\\1.jpg");
  50. if (src.empty()){
  51. return -1;
  52. }
  53. if (src.channels() > 1)
  54. cv::cvtColor(src, src, CV_RGB2GRAY);
  55. cv::Mat dst;
  56. int thresh=0;
  57. double t2 = (double)cv::getTickCount();
  58. thresh=Otsu(src , dst, thresh); //Otsu
  59. cv::namedWindow("src", CV_WINDOW_NORMAL);
  60. cv::imshow("src", src);
  61. cv::namedWindow("dst", CV_WINDOW_NORMAL);
  62. cv::imshow("dst", dst);
  63. cv::waitKey(0);
  64. }
2. 自适应阈值处理
  1. #include <iostream>
  2. #include <opencv2/core.hpp>
  3. #include <opencv2/highgui.hpp>
  4. #include <opencv2/imgproc.hpp>
  5. enum adaptiveMethod{meanFilter,gaaussianFilter,medianFilter};
  6. void AdaptiveThreshold(cv::Mat& src, cv::Mat& dst, double Maxval, int Subsize, double c, adaptiveMethod method = meanFilter){
  7. if (src.channels() > 1)
  8. cv::cvtColor(src, src, CV_RGB2GRAY);
  9. cv::Mat smooth;
  10. switch (method)
  11. {
  12. case meanFilter:
  13. cv::blur(src, smooth, cv::Size(Subsize, Subsize)); //均值滤波
  14. break;
  15. case gaaussianFilter:
  16. cv::GaussianBlur(src, smooth, cv::Size(Subsize, Subsize),0,0); //高斯滤波
  17. break;
  18. case medianFilter:
  19. cv::medianBlur(src, smooth, Subsize); //中值滤波
  20. break;
  21. default:
  22. break;
  23. }
  24. smooth = smooth - c;
  25. //阈值处理
  26. src.copyTo(dst);
  27. for (int r = 0; r < src.rows;++r){
  28. const uchar* srcptr = src.ptr<uchar>(r);
  29. const uchar* smoothptr = smooth.ptr<uchar>(r);
  30. uchar* dstptr = dst.ptr<uchar>(r);
  31. for (int c = 0; c < src.cols; ++c){
  32. if (srcptr[c]>smoothptr[c]){
  33. dstptr[c] = Maxval;
  34. }
  35. else
  36. dstptr[c] = 0;
  37. }
  38. }
  39. }
  40. int main(){
  41. cv::Mat src = cv::imread("D:\\yt\\picture\\threshold\\1.jpg");
  42. if (src.empty()){
  43. return -1;
  44. }
  45. if (src.channels() > 1)
  46. cv::cvtColor(src, src, CV_RGB2GRAY);
  47. cv::Mat dst;
  48. AdaptiveThreshold(src, dst, 255, 21, 10, meanFilter);
  49. cv::namedWindow("src", CV_WINDOW_NORMAL);
  50. cv::imshow("src", src);
  51. cv::namedWindow("dst", CV_WINDOW_NORMAL);
  52. cv::imshow("dst", dst);
  53. cv::waitKey(0);
  54. }

python实现

与c++不同,python中函数cv2.threshold的返回值有两个

retval,dst = cv2.threshold(src,thresh,maxval,type)
  • retval — 返回的阈值

  • dst — 阈值处理的输出图像

1. 二值化阈值处理
  1. import cv2
  2. if __name__ == "__main__":
  3. img = cv2.imread('D:/yt/picture/threshold/lena.bmp')
  4. t,dst1 = cv2.threshold(img,127,255,cv2.THRESH_BINARY)
  5. # 显示图像
  6. cv2.imshow("origin image", img)
  7. cv2.imshow("THRESH_BINARY", dst1)
  8. # 保存图像
  9. cv2.imwrite("D:/yt/picture/threshold/THRESH_BINARY.bmp", dst1)
  10. cv2.waitKey(0)
  11. cv2.destroyAllWindows()
效果

05fcd7b4e8506893ca16b2340815275b.png

2. 反二值化阈值处理
  1. import cv2
  2. if __name__ == "__main__":
  3. img = cv2.imread('D:/yt/picture/threshold/lena.bmp')
  4. t,dst1 = cv2.threshold(img,127,255,cv2.THRESH_BINARY_INV)
  5. # 显示图像
  6. cv2.imshow("origin image", img)
  7. cv2.imshow("THRESH_BINARY_INV", dst1)
  8. # 保存图像
  9. cv2.imwrite("D:/yt/picture/threshold/THRESH_BINARY_INV.bmp", dst1)
  10. cv2.waitKey(0)
  11. cv2.destroyAllWindows()
效果

c72a48c85a0cb046eb7fa6e795122b8f.png

3. 截断阈值化处理
  1. import cv2
  2. if __name__ == "__main__":
  3. img = cv2.imread('D:/yt/picture/threshold/lena.bmp')
  4. t,dst1 = cv2.threshold(img,127,255,cv2.THRESH_TRUNC)
  5. # 显示图像
  6. cv2.imshow("origin image", img)
  7. cv2.imshow("THRESH_TRUNC", dst1)
  8. # 保存图像
  9. cv2.imwrite("D:/yt/picture/threshold/THRESH_TRUNC.bmp", dst1)
  10. cv2.waitKey(0)
  11. cv2.destroyAllWindows()
效果

56fcd4a977d538d6bda52d0b57586c91.png

4. 超阈值零处理
  1. import cv2
  2. if __name__ == "__main__":
  3. img = cv2.imread('D:/yt/picture/threshold/lena.bmp')
  4. t,dst1 = cv2.threshold(img,127,255,cv2.THRESH_TOZERO_INV)
  5. # 显示图像
  6. cv2.imshow("origin image", img)
  7. cv2.imshow("THRESH_TOZERO_INV", dst1)
  8. # 保存图像
  9. cv2.imwrite("D:/yt/picture/threshold/THRESH_TOZERO_INV.bmp", dst1)
  10. cv2.waitKey(0)
  11. cv2.destroyAllWindows()
效果

965889b304f29fc6421ee463cc4134f6.png

5. 低阈值零处理
  1. import cv2
  2. if __name__ == "__main__":
  3. img = cv2.imread('D:/yt/picture/threshold/lena.bmp')
  4. t,dst1 = cv2.threshold(img,127,255,cv2.THRESH_TOZERO)
  5. # 显示图像
  6. cv2.imshow("origin image", img)
  7. cv2.imshow("THRESH_TOZERO", dst1)
  8. # 保存图像
  9. cv2.imwrite("D:/yt/picture/threshold/THRESH_TOZERO.bmp", dst1)
  10. cv2.waitKey(0)
  11. cv2.destroyAllWindows()
效果

cb8a5801772d013de5b18216de0b310a.png

6. OTSU处理

在OpenCV中,给参数type多传递一个参数“THRESH_OTSU”即可实现OTSU方式的阈值分割。且设定阈值thresh为0。

  1. import cv2
  2. import numpy as np
  3. if __name__ == "__main__":
  4. img = cv2.imread('D:/yt/picture/threshold/tiffany.bmp')
  5. img = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)#原图像不是灰度图,必须先转换为灰度图
  6. #普通二值化阈值处理
  7. t1, dst1 = cv2.threshold(img,127,255,cv2.THRESH_BINARY)
  8. #采用OTSU的处理
  9. t2, dst2 = cv2.threshold(img, 0, 255, cv2.THRESH_BINARY+cv2.THRESH_OTSU)
  10. # 创建窗口
  11. cv2.namedWindow("origin image",cv2.WINDOW_NORMAL)#cv2.WINDOW_NORMAL使窗口大小可调整
  12. cv2.namedWindow("THRESH_TOZERO",cv2.WINDOW_NORMAL)
  13. cv2.namedWindow("THRESH_OTSU",cv2.WINDOW_NORMAL)
  14. # 显示图像
  15. cv2.imshow("origin image", img)
  16. cv2.imshow("THRESH_TOZERO", dst1)
  17. cv2.imshow("THRESH_OTSU",dst2)
  18. cv2.waitKey(0)
  19. cv2.destroyAllWindows()
效果:

e614e789951ab1256ba5f4575c05a3d9.png

7. 自适应阈值处理
  1. import cv2
  2. import numpy as np
  3. if __name__ == "__main__":
  4. img = cv2.imread('D:/yt/picture/threshold/computer.jpg')
  5. img = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)#原图像不是灰度图,必须先转换为灰度图
  6. #普通二值化阈值处理
  7. t1, dst1 = cv2.threshold(img,127,255,cv2.THRESH_BINARY)
  8. #自适应阈值处理,采用均值计算阈值
  9. dst2 = cv2.adaptiveThreshold(img,255,cv2.ADAPTIVE_THRESH_MEAN_C,cv2.THRESH_BINARY,5,3)
  10. #自适应阈值处理,采用高斯均值计算阈值
  11. dst3 = cv2.adaptiveThreshold(img,255,cv2.ADAPTIVE_THRESH_GAUSSIAN_C,cv2.THRESH_BINARY,5,3)
  12. # 创建窗口
  13. cv2.namedWindow("origin image",cv2.WINDOW_NORMAL)#cv2.WINDOW_NORMAL使窗口大小可调整
  14. cv2.namedWindow("THRESH_BINARY",cv2.WINDOW_NORMAL)
  15. cv2.namedWindow("MEAN_C",cv2.WINDOW_NORMAL)
  16. cv2.namedWindow("GAUSSIAN_C", cv2.WINDOW_NORMAL)
  17. # 显示图像
  18. cv2.imshow("origin image", img)
  19. cv2.imshow("THRESH_BINARY", dst1)
  20. cv2.imshow("MEAN_C",dst2)
  21. cv2.imshow("GAUSSIAN_C", dst3)
  22. cv2.waitKey(0)
  23. cv2.destroyAllWindows()
效果

1eff3c15e1c54dbd18ec370c87fa7f50.png

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

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

下载2:Python视觉实战项目52讲

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

下载3:OpenCV实战项目20讲

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

交流群

欢迎加入公众号读者群一起和同行交流,目前有SLAM、三维视觉、传感器、自动驾驶、计算摄影、检测、分割、识别、医学影像、GAN、算法竞赛等微信群(以后会逐渐细分),请扫描下面微信号加群,备注:”昵称+学校/公司+研究方向“,例如:”张三 + 上海交大 + 视觉SLAM“。请按照格式备注,否则不予通过。添加成功后会根据研究方向邀请进入相关微信群。请勿在群内发送广告,否则会请出群,谢谢理解~

7d76e8b1996c4732c111913ca3010cef.png

e8ab7f0713bd93df6831c2734ad3d203.png

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/盐析白兔/article/detail/604271
推荐阅读
相关标签
  

闽ICP备14008679号