提取接近竖直物体(粗定位)
由于项目的需要提取图像之中的一个接近于竖直的物体,一般的方法是进行图像分割,分割方式使用什么OTSU方式以及hsv方法等等。但是项目中使用的相机是黑白相机,会受到一定的限制。因此想到的是使用线条提取方式。线条提取方式之中最好的方法是使用canny算法,但是这里不能够将接近竖直特征进行提取,因此,此处使用了Prewitt算子进行提取,但是只用这个算法,轮廓提取不出来,就结合了一下canny算子。下面是我的思路,感觉实现过程比较麻烦,但是居然实现了[苦笑]!!!!
本次测试的案例是使用校门口的一个图片,图中存在很多的干扰,如下图所示
#include <opencv2/opencv.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <iostream>
#include <opencv2/core.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
using namespace cv;
using namespace std;void getPrewitt_oper(cv::Mat& getPrewitt_horizontal, cv::Mat& getPrewitt_vertical, cv::Mat& getPrewitt_Diagonal1, cv::Mat& getPrewitt_Diagonal2) {//水平方向getPrewitt_horizontal = (cv::Mat_<float>(3, 3) << -1, -1, -1, 0, 0, 0, 1, 1, 1);//垂直方向getPrewitt_vertical = (cv::Mat_<float>(3, 3) << -1, 0, 1, -1, 0, 1, -1, 0, 1);//对角135°getPrewitt_Diagonal1 = (cv::Mat_<float>(3, 3) << 0, 1, 1, -1, 0, 1, -1, -1, 0);//对角45°getPrewitt_Diagonal2 = (cv::Mat_<float>(3, 3) << -1, -1, 0, -1, 0, 1, 0, 1, 1);//逆时针反转180°得到卷积核cv::flip(getPrewitt_horizontal, getPrewitt_horizontal, -1);cv::flip(getPrewitt_vertical, getPrewitt_vertical, -1);cv::flip(getPrewitt_Diagonal1, getPrewitt_Diagonal1, -1);cv::flip(getPrewitt_Diagonal2, getPrewitt_Diagonal2, -1);
}void edge_Prewitt(cv::Mat& src, cv::Mat& dst1, cv::Mat& dst2, cv::Mat& dst3, cv::Mat& dst4, cv::Mat& dst, int ddepth, double delta = 0, int borderType = cv::BORDER_DEFAULT) {//获取Prewitt算子cv::Mat getPrewitt_horizontal;cv::Mat getPrewitt_vertical;cv::Mat getPrewitt_Diagonal1;cv::Mat getPrewitt_Diagonal2;getPrewitt_oper(getPrewitt_horizontal, getPrewitt_vertical, getPrewitt_Diagonal1, getPrewitt_Diagonal2);//卷积得到水平方向边缘cv::filter2D(src, dst1, ddepth, getPrewitt_horizontal, cv::Point(-1, -1), delta, borderType);//卷积得到4垂直方向边缘cv::filter2D(src, dst2, ddepth, getPrewitt_vertical, cv::Point(-1, -1), delta, borderType);//卷积得到45°方向边缘cv::filter2D(src, dst3, ddepth, getPrewitt_Diagonal1, cv::Point(-1, -1), delta, borderType);//卷积得到135°方向边缘cv::filter2D(src, dst4, ddepth, getPrewitt_Diagonal2, cv::Point(-1, -1), delta, borderType);//边缘强度(近似)cv::convertScaleAbs(dst1, dst1); //求绝对值并转为无符号8位图cv::convertScaleAbs(dst2, dst2);cv::convertScaleAbs(dst3, dst3); //求绝对值并转为无符号8位图cv::convertScaleAbs(dst4, dst4);dst = dst1 + dst2;}//数组从大到小排序
void reserve(int x[], int n) {int i, j, temp;for (i = 0; i < n - 1; i++) { //一共n个元素,则需要比较n-1次for (j = 0; j < n - 1 - i; j++) { //每一个元素需要比较的次数if (x[i] < x[i + j + 1]) {temp = x[i];x[i] = x[i + j + 1];x[i + j + 1] = temp;}}}
}int main()
{cv::Mat src = cv::imread("楼.jpg");if (src.empty()) {return -1;}cout << "??" << endl;if (src.channels() > 1) cv::cvtColor(src, src, CV_RGB2GRAY);cv::Mat dst, dst1, dst2, dst3, dst4, dst5;Mat src1 = cv::imread("楼.jpg");Mat src2 = cv::imread("楼.jpg");//medianBlur(src, src, 5); //均值滤波GaussianBlur(src, src, Size(5, 5), 0); //高斯滤波cout << "??" << endl;//注意:要采用CV_32F,因为有些地方卷积后为负数,若用8位无符号,则会导致这些地方为0edge_Prewitt(src, dst1, dst2, dst3, dst4, dst, CV_32F);cv::namedWindow("垂直边缘", CV_WINDOW_NORMAL);imshow("垂直边缘", dst2);cout << "??" << endl;//获取结构cv::Mat element1 = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(3, 3));Mat out1;//进行形态学开运算操作 morphologyEx(dst2, out1, MORPH_OPEN, element1);//形态学开运算cv::namedWindow("xingtai", CV_WINDOW_NORMAL);imshow("xingtai", out1);//第二次进行形态学操作edge_Prewitt(dst2, dst1, out1, dst3, dst4, dst, CV_32F);cv::namedWindow("垂直边缘1", CV_WINDOW_NORMAL);imshow("垂直边缘1", out1);cout << "??" << endl;morphologyEx(out1, out1, MORPH_OPEN, element1);//形态学开运算cv::namedWindow("xingtai1", CV_WINDOW_NORMAL);imshow("xingtai1", out1);//获取结构cv::Mat element2 = cv::getStructuringElement(cv::MORPH_ELLIPSE, cv::Size(10, 10));Mat out2;//进行形态学闭运算操作 morphologyEx(out1, out2, MORPH_CLOSE, element2);//形态学开运算cv::namedWindow("xingtai2", CV_WINDOW_NORMAL);imshow("xingtai2", out2);imwrite("xingtai2.jpg", out2);/*//膨胀运算,将细小缝隙填补上,非必要Mat kernel = getStructuringElement(0, Size(3, 3));dilate(out2, dst2, kernel);cv::namedWindow("膨胀", CV_WINDOW_NORMAL);imshow("膨胀", dst2);*/cv::threshold(out2, dst2, 5, 255, cv::THRESH_BINARY);cv::namedWindow("二值化", CV_WINDOW_NORMAL);imshow("二值化", dst2);cv::threshold(dst2, dst2, 5, 255, cv::THRESH_BINARY_INV);cv::namedWindow("反二值化", CV_WINDOW_NORMAL);imshow("反二值化", dst2);//进行形态学闭运算操作 morphologyEx(dst2, out2, MORPH_CLOSE, element2);//形态学开运算cv::namedWindow("xingtai3", CV_WINDOW_NORMAL);imshow("xingtai3", out2);imwrite("xingtai3.jpg", out2);/*cv::threshold(dst2, dst2, 5, 255, cv::THRESH_BINARY);cv::namedWindow("反二值化", CV_WINDOW_NORMAL);imshow("反二值化", dst2);imwrite("反二值化.jpg", dst2);*//*//膨胀运算,将细小缝隙填补上,非必要Mat kernel = getStructuringElement(0, Size(5, 5));dilate(out2, out2, kernel);cv::namedWindow("膨胀1", CV_WINDOW_NORMAL);imshow("膨胀1", out2);*/Canny(out2, dst2, 5, 10);cv::namedWindow("Canny", CV_WINDOW_NORMAL);imshow("Canny", dst2);imwrite("Canny.jpg", dst2);vector<Vec4i> lines;HoughLinesP(dst2, lines, 1, CV_PI / 180, 50, 200, 30);int Length[100] = {0};//存放直线长度for (size_t i = 0; i < lines.size(); i++){Vec4i I = lines[i];double x1 = I[0];double y1 = I[1];double x2 = I[2];double y2 = I[3];//筛选满足条件的点if (abs(x1 - x2) + abs(y1 - y2) > 50){Length[i] = sqrt( (x1 - x2)*(x1 - x2) + (y1 - y2) * (y1 - y2));//将满足条件的点画出line(src1, Point2d(x1, y1), Point2d(x2, y2), Scalar(0, 255, 255), 2);cout << " " << "(" << x1 << "," << y1 << ")" << " " << "(" << x2 << "," << y2 << ")" << endl;//line(canny, Point2d(x1, y1), Point2d(x2, y2), Scalar(0, 255, 255), 2);}}Mat imgShow;imgShow = src1;resize(imgShow, imgShow, Size(imgShow.cols / 4, imgShow.rows / 4));imshow("imgShow", imgShow);imwrite("shuchu.png", src1);reserve(Length, 100);for (int i = 0; i < 100; i++) {cout << "长度"<<Length[i] << endl; //输出排序后的数组元素}for (size_t i = 0; i < lines.size(); i++){Vec4i I = lines[i];double x1 = I[0];double y1 = I[1];double x2 = I[2];double y2 = I[3];cout << "sdjk" << endl;cout << int(sqrt((x1 - x2)*(x1 - x2) + (y1 - y2) * (y1 - y2))) << endl;//筛选满足条件的点if ((int(sqrt((x1 - x2)*(x1 - x2) + (y1 - y2) * (y1 - y2))) == Length[0] ) || (int(sqrt((x1 - x2)*(x1 - x2) + (y1 - y2) * (y1 - y2))) == Length[1])){//将满足条件的点画出line(src2, Point2d(x1, y1), Point2d(x2, y2), Scalar(0, 255, 255), 2);cout << "djfkljsa " << "(" << x1 << "," << y1 << ")" << " " << "(" << x2 << "," << y2 << ")" << endl;//line(canny, Point2d(x1, y1), Point2d(x2, y2), Scalar(0, 255, 255), 2);}}imgShow = src2;resize(imgShow, imgShow, Size(imgShow.cols / 4, imgShow.rows / 4));imshow("imgShow2", imgShow);imwrite("shuchu2.png", src2);waitKey(0);return 0;
}
调试过程:本次在进行调试过程之中进行了两次垂直检测迭代,进一步去排除水平线的干扰.使用形态学操作去除图片之中的空洞等等.
第一次进行垂直检测,注意这个地方只能够用特定的算子进行垂直检测,别的算子没有这个效果.
为了减少图片之中白色空洞的干扰,使用开操作.
重复上述操作,进一步排除水平线的干扰.
接下来是进行闭操作,将图中的白色线条尽可能连在一起,上图之中的楼左侧的线有一些断开了.
闭操作的缺陷是会产生小白点点.如下二值化过程
再进行一次反二值化,因为我不会用别的算子结合霍夫直线检测检测出来直线,只能转回去进行操作.
形态学操作,去除白点
canny一下检测出来轮廓
显示全部直线
直线提取,我的方式是提取最长的两段直线。
在上述操作完成之后,得到了物体的粗定位直线。
但是上面的算法还是存在相应的问题,换了一个别的图像可能就检测的不准。发现问题就是出在了二值化的过程。
为了修正上方的算法的失败,使用提取外部轮廓的方式进行求取,将代码改了改。
#include <opencv2/opencv.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <iostream>
#include <opencv2/core.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
using namespace cv;
using namespace std;void getPrewitt_oper(cv::Mat& getPrewitt_horizontal, cv::Mat& getPrewitt_vertical, cv::Mat& getPrewitt_Diagonal1, cv::Mat& getPrewitt_Diagonal2) {//水平方向getPrewitt_horizontal = (cv::Mat_<float>(3, 3) << -1, -1, -1, 0, 0, 0, 1, 1, 1);//垂直方向getPrewitt_vertical = (cv::Mat_<float>(3, 3) << -1, 0, 1, -1, 0, 1, -1, 0, 1);//对角135°getPrewitt_Diagonal1 = (cv::Mat_<float>(3, 3) << 0, 1, 1, -1, 0, 1, -1, -1, 0);//对角45°getPrewitt_Diagonal2 = (cv::Mat_<float>(3, 3) << -1, -1, 0, -1, 0, 1, 0, 1, 1);//逆时针反转180°得到卷积核cv::flip(getPrewitt_horizontal, getPrewitt_horizontal, -1);cv::flip(getPrewitt_vertical, getPrewitt_vertical, -1);cv::flip(getPrewitt_Diagonal1, getPrewitt_Diagonal1, -1);cv::flip(getPrewitt_Diagonal2, getPrewitt_Diagonal2, -1);
}void edge_Prewitt(cv::Mat& src, cv::Mat& dst1, cv::Mat& dst2, cv::Mat& dst3, cv::Mat& dst4, cv::Mat& dst, int ddepth, double delta = 0, int borderType = cv::BORDER_DEFAULT) {//获取Prewitt算子cv::Mat getPrewitt_horizontal;cv::Mat getPrewitt_vertical;cv::Mat getPrewitt_Diagonal1;cv::Mat getPrewitt_Diagonal2;getPrewitt_oper(getPrewitt_horizontal, getPrewitt_vertical, getPrewitt_Diagonal1, getPrewitt_Diagonal2);//卷积得到水平方向边缘cv::filter2D(src, dst1, ddepth, getPrewitt_horizontal, cv::Point(-1, -1), delta, borderType);//卷积得到4垂直方向边缘cv::filter2D(src, dst2, ddepth, getPrewitt_vertical, cv::Point(-1, -1), delta, borderType);//卷积得到45°方向边缘cv::filter2D(src, dst3, ddepth, getPrewitt_Diagonal1, cv::Point(-1, -1), delta, borderType);//卷积得到135°方向边缘cv::filter2D(src, dst4, ddepth, getPrewitt_Diagonal2, cv::Point(-1, -1), delta, borderType);//边缘强度(近似)cv::convertScaleAbs(dst1, dst1); //求绝对值并转为无符号8位图cv::convertScaleAbs(dst2, dst2);cv::convertScaleAbs(dst3, dst3); //求绝对值并转为无符号8位图cv::convertScaleAbs(dst4, dst4);dst = dst1 + dst2;}//数组从大到小排序
void reserve(int x[], int n) {int i, j, temp;for (i = 0; i < n - 1; i++) { //一共n个元素,则需要比较n-1次for (j = 0; j < n - 1 - i; j++) { //每一个元素需要比较的次数if (x[i] < x[i + j + 1]) {temp = x[i];x[i] = x[i + j + 1];x[i + j + 1] = temp;}}}
}int main()
{cv::Mat src = cv::imread("楼.jpg");if (src.empty()) {return -1;}cout << "??" << endl;if (src.channels() > 1) cv::cvtColor(src, src, CV_RGB2GRAY);cv::Mat dst, dst1, dst2, dst3, dst4, dst5;Mat src1 = cv::imread("楼.jpg");Mat src2 = cv::imread("楼.jpg");//medianBlur(src, src, 5); //均值滤波GaussianBlur(src, src, Size(5, 5), 0); //高斯滤波cout << "??" << endl;//注意:要采用CV_32F,因为有些地方卷积后为负数,若用8位无符号,则会导致这些地方为0edge_Prewitt(src, dst1, dst2, dst3, dst4, dst, CV_32F);cv::namedWindow("垂直边缘", CV_WINDOW_NORMAL);imshow("垂直边缘", dst2);cout << "??" << endl;/*Mat shdjk;cv::threshold(dst2, shdjk, 25, 255, cv::THRESH_BINARY);cv::namedWindow("二值化1212", CV_WINDOW_NORMAL);imshow("二值化1212", shdjk);*///获取结构cv::Mat element1 = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(3, 3));Mat out1;//进行形态学开运算操作 morphologyEx(dst2, out1, MORPH_OPEN, element1);//形态学开运算cv::namedWindow("xingtai", CV_WINDOW_NORMAL);imshow("xingtai", out1);Mat out2;//第二次进行形态学操作edge_Prewitt(out1, dst1, out1, dst3, dst4, dst, CV_32F);cv::namedWindow("垂直边缘1", CV_WINDOW_NORMAL);imshow("垂直边缘1", out1);cout << "??" << endl;/*morphologyEx(out1, out1, MORPH_OPEN, element1);//形态学开运算cv::namedWindow("xingtai1", CV_WINDOW_NORMAL);imshow("xingtai1", out1);//获取结构cv::Mat element2 = cv::getStructuringElement(cv::MORPH_ELLIPSE, cv::Size(10, 10));//进行形态学闭运算操作 morphologyEx(out1, out2, MORPH_CLOSE, element2);//形态学闭合运算cv::namedWindow("xingtai2", CV_WINDOW_NORMAL);imshow("xingtai2", out2);imwrite("xingtai2.jpg", out2);waitKey(0);*/std::vector<std::vector<cv::Point>> contours;std::vector<cv::Vec4i> hierarchy;findContours(out1, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE);double maxArea = 0;int index = 0;vector<cv::Point> maxContour;for (size_t i = 0; i < contours.size(); i++){double area = cv::contourArea(contours[i]);if (area > maxArea){maxArea = area;maxContour = contours[i];index = i;}}drawContours(src1, contours, index, Scalar(255)); // 参数cv::namedWindow("test", CV_WINDOW_NORMAL);imshow("test", src1);waitKey(0);/*Mat shdjk;cv::threshold(out1, shdjk, 10, 255, cv::THRESH_BINARY);cv::namedWindow("二值化1212", CV_WINDOW_NORMAL);imshow("二值化1212", shdjk);std::vector<std::vector<cv::Point>> contours;std::vector<cv::Vec4i> hierarchy;cv::findContours(shdjk, contours, hierarchy, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_NONE); //只找最外层轮廓std::vector<std::vector<cv::Point>> approxCurves(contours.size());for (int i = 0; i < contours.size(); ++i) { //绘制逼近后的轮廓double epsilon = 0.1 * cv::arcLength(contours[i], true);cv::approxPolyDP(contours[i], approxCurves[i], epsilon, true);cv::drawContours(src1, approxCurves, i, cv::Scalar(0, 255, 0), 2);}cv::namedWindow("success", CV_WINDOW_NORMAL);imshow("success", src1);cv::waitKey();*////*Mat dhfjua;cv::threshold(out2, dhfjua, 15, 255, cv::THRESH_BINARY);cv::namedWindow("二值化000", CV_WINDOW_NORMAL);imshow("二值化000", dhfjua);*//*//膨胀运算,将细小缝隙填补上,非必要Mat kernel = getStructuringElement(0, Size(3, 3));dilate(out2, dst2, kernel);cv::namedWindow("膨胀", CV_WINDOW_NORMAL);imshow("膨胀", dst2);*//*0cv::threshold(out2, dst2, 5, 255, cv::THRESH_BINARY);cv::namedWindow("二值化", CV_WINDOW_NORMAL);imshow("二值化", dst2);cv::threshold(dst2, dst2, 5, 255, cv::THRESH_BINARY_INV);cv::namedWindow("反二值化", CV_WINDOW_NORMAL);imshow("反二值化", dst2);*//*Mat out3;//进行形态学闭运算操作 morphologyEx(dst2, out3, MORPH_CLOSE, element2);//形态学开运算cv::namedWindow("xingtai3", CV_WINDOW_NORMAL);imshow("xingtai3", out3);imwrite("xingtai3.jpg", out3);*/waitKey(0);vector<Vec4i> lines;HoughLinesP(src1, lines, 1, CV_PI / 180, 100, 400, 30);int Length[1000] = { 0 };//存放直线长度for (size_t i = 0; i < lines.size(); i++){Vec4i I = lines[i];double x1 = I[0];double y1 = I[1];double x2 = I[2];double y2 = I[3];//筛选满足条件的点if (abs(x1 - x2) + abs(y1 - y2) > 50){Length[i] = sqrt((x1 - x2)*(x1 - x2) + (y1 - y2) * (y1 - y2));//将满足条件的点画出line(src1, Point2d(x1, y1), Point2d(x2, y2), Scalar(0, 255, 255), 2);cout << " " << "(" << x1 << "," << y1 << ")" << " " << "(" << x2 << "," << y2 << ")" << endl;//line(canny, Point2d(x1, y1), Point2d(x2, y2), Scalar(0, 255, 255), 2);}}Mat imgShow;imgShow = src1;resize(imgShow, imgShow, Size(imgShow.cols / 4, imgShow.rows / 4));imshow("imgShow", imgShow);imwrite("shuchu.png", src1);reserve(Length, 1000);for (int i = 0; i < 1000; i++) {cout << "长度" << Length[i] << endl; //输出排序后的数组元素}for (size_t i = 0; i < lines.size(); i++){Vec4i I = lines[i];double x1 = I[0];double y1 = I[1];double x2 = I[2];double y2 = I[3];cout << "sdjk" << endl;cout << int(sqrt((x1 - x2)*(x1 - x2) + (y1 - y2) * (y1 - y2))) << endl;//筛选满足条件的点if ((int(sqrt((x1 - x2)*(x1 - x2) + (y1 - y2) * (y1 - y2))) == Length[0]) || (int(sqrt((x1 - x2)*(x1 - x2) + (y1 - y2) * (y1 - y2))) == Length[1])){//将满足条件的点画出line(src2, Point2d(x1, y1), Point2d(x2, y2), Scalar(0, 255, 255), 2);cout << "djfkljsa " << "(" << x1 << "," << y1 << ")" << " " << "(" << x2 << "," << y2 << ")" << endl;//line(canny, Point2d(x1, y1), Point2d(x2, y2), Scalar(0, 255, 255), 2);}}imgShow = src2;resize(imgShow, imgShow, Size(imgShow.cols / 4, imgShow.rows / 4));imshow("imgShow2", imgShow);imwrite("shuchu2.png", src2);waitKey(0);return 0;
}
效果还是不好,问题就是出在了相应的一个二值化的过程,因此,想到使用区域增长算法改进
#include <iostream>
#include <string>
#include <list>
#include <vector>
#include <map>
#include <stack>
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>using namespace std;
using namespace cv;//------------------------------【两步法新改进版】----------------------------------------------
// 对二值图像进行连通区域标记,从1开始标号
void Two_PassNew(const Mat &bwImg, Mat &labImg)
{assert(bwImg.type() == CV_8UC1);labImg.create(bwImg.size(), CV_32SC1); //bwImg.convertTo( labImg, CV_32SC1 );labImg = Scalar(0);labImg.setTo(Scalar(1), bwImg);assert(labImg.isContinuous());const int Rows = bwImg.rows - 1, Cols = bwImg.cols - 1;int label = 1;vector<int> labelSet;labelSet.push_back(0);labelSet.push_back(1);//the first passint *data_prev = (int*)labImg.data; //0-th row : int* data_prev = labImg.ptr<int>(i-1);int *data_cur = (int*)(labImg.data + labImg.step); //1-st row : int* data_cur = labImg.ptr<int>(i);for (int i = 1; i < Rows; i++){data_cur++;data_prev++;for (int j = 1; j < Cols; j++, data_cur++, data_prev++){if (*data_cur != 1)continue;int left = *(data_cur - 1);int up = *data_prev;int neighborLabels[2];int cnt = 0;if (left > 1)neighborLabels[cnt++] = left;if (up > 1)neighborLabels[cnt++] = up;if (!cnt){labelSet.push_back(++label);labelSet[label] = label;*data_cur = label;continue;}int smallestLabel = neighborLabels[0];if (cnt == 2 && neighborLabels[1] < smallestLabel)smallestLabel = neighborLabels[1];*data_cur = smallestLabel;// 保存最小等价表for (int k = 0; k < cnt; k++){int tempLabel = neighborLabels[k];int& oldSmallestLabel = labelSet[tempLabel]; //这里的&不是取地址符号,而是引用符号if (oldSmallestLabel > smallestLabel){labelSet[oldSmallestLabel] = smallestLabel;oldSmallestLabel = smallestLabel;}else if (oldSmallestLabel < smallestLabel)labelSet[smallestLabel] = oldSmallestLabel;}}data_cur++;data_prev++;}//更新等价队列表,将最小标号给重复区域for (size_t i = 2; i < labelSet.size(); i++){int curLabel = labelSet[i];int prelabel = labelSet[curLabel];while (prelabel != curLabel){curLabel = prelabel;prelabel = labelSet[prelabel];}labelSet[i] = curLabel;}//second passdata_cur = (int*)labImg.data;for (int i = 0; i < Rows; i++){for (int j = 0; j < bwImg.cols - 1; j++, data_cur++)*data_cur = labelSet[*data_cur];data_cur++;}
}//-------------------------------【老版两步法】-------------------------------------------
void Two_PassOld(const cv::Mat& _binImg, cv::Mat& _lableImg)
{//connected component analysis (4-component)//use two-pass algorithm//1. first pass: label each foreground pixel with a label//2. second pass: visit each labeled pixel and merge neighbor label////foreground pixel: _binImg(x,y) = 1//background pixel: _binImg(x,y) = 0if (_binImg.empty() || _binImg.type() != CV_8UC1){return;}// 1. first pass_lableImg.release();_binImg.convertTo(_lableImg, CV_32SC1);int label = 1; // start by 2std::vector<int> labelSet;labelSet.push_back(0); //background: 0labelSet.push_back(1); //foreground: 1int rows = _binImg.rows - 1;int cols = _binImg.cols - 1;for (int i = 1; i < rows; i++){int* data_preRow = _lableImg.ptr<int>(i - 1);int* data_curRow = _lableImg.ptr<int>(i);for (int j = 1; j < cols; j++){if (data_curRow[j] == 1){std::vector<int> neighborLabels;neighborLabels.reserve(2); //reserve(n) 预分配n个元素的存储空间int leftPixel = data_curRow[j - 1];int upPixel = data_preRow[j];if (leftPixel > 1){neighborLabels.push_back(leftPixel);}if (upPixel > 1){neighborLabels.push_back(upPixel);}if (neighborLabels.empty()){labelSet.push_back(++label); //assign to a new labeldata_curRow[j] = label;labelSet[label] = label;}else{std::sort(neighborLabels.begin(), neighborLabels.end());int smallestLabel = neighborLabels[0];data_curRow[j] = smallestLabel;//save equivalencefor (size_t k = 1; k < neighborLabels.size(); k++){int tempLabel = neighborLabels[k];int& oldSmallestLabel = labelSet[tempLabel];if (oldSmallestLabel > smallestLabel){labelSet[oldSmallestLabel] = smallestLabel;oldSmallestLabel = smallestLabel;}else if (oldSmallestLabel < smallestLabel){labelSet[smallestLabel] = oldSmallestLabel;}}}}}}//update equivalent labels//assigned with the smallest label in each equivalent label setfor (size_t i = 2; i < labelSet.size(); i++){int curLabel = labelSet[i];int prelabel = labelSet[curLabel];while (prelabel != curLabel){curLabel = prelabel;prelabel = labelSet[prelabel];}labelSet[i] = curLabel;}//2. second passfor (int i = 0; i < rows; i++){int *data = _lableImg.ptr<int>(i);for (int j = 0; j < cols; j++){int& pixelLabel = data[j];pixelLabel = labelSet[pixelLabel];}}
}//---------------------------------【种子填充法老版】-------------------------------
void SeedFillOld(const cv::Mat& binImg, cv::Mat& lableImg) //种子填充法
{// 4邻接方法if (binImg.empty() ||binImg.type() != CV_8UC1){return;}lableImg.release();binImg.convertTo(lableImg, CV_32SC1);int label = 1;int rows = binImg.rows - 1;int cols = binImg.cols - 1;for (int i = 1; i < rows - 1; i++){int* data = lableImg.ptr<int>(i);for (int j = 1; j < cols - 1; j++){if (data[j] == 1){std::stack<std::pair<int, int>> neighborPixels;neighborPixels.push(std::pair<int, int>(i, j)); // 像素位置: <i,j>++label; // 没有重复的团,开始新的标签while (!neighborPixels.empty()){std::pair<int, int> curPixel = neighborPixels.top(); //如果与上一行中一个团有重合区域,则将上一行的那个团的标号赋给它int curX = curPixel.first;int curY = curPixel.second;lableImg.at<int>(curX, curY) = label;neighborPixels.pop();if (lableImg.at<int>(curX, curY - 1) == 1){//左边neighborPixels.push(std::pair<int, int>(curX, curY - 1));}if (lableImg.at<int>(curX, curY + 1) == 1){// 右边neighborPixels.push(std::pair<int, int>(curX, curY + 1));}if (lableImg.at<int>(curX - 1, curY) == 1){// 上边neighborPixels.push(std::pair<int, int>(curX - 1, curY));}if (lableImg.at<int>(curX + 1, curY) == 1){// 下边neighborPixels.push(std::pair<int, int>(curX + 1, curY));}}}}}}//-------------------------------------------【种子填充法新版】---------------------------
void SeedFillNew(const cv::Mat& _binImg, cv::Mat& _lableImg)
{// connected component analysis(4-component)// use seed filling algorithm// 1. begin with a forgeground pixel and push its forground neighbors into a stack;// 2. pop the pop pixel on the stack and label it with the same label until the stack is empty// // forground pixel: _binImg(x,y)=1// background pixel: _binImg(x,y) = 0if (_binImg.empty() ||_binImg.type() != CV_8UC1){return;}_lableImg.release();_binImg.convertTo(_lableImg, CV_32SC1);int label = 0; //start by 1int rows = _binImg.rows;int cols = _binImg.cols;Mat mask(rows, cols, CV_8UC1);mask.setTo(0);int *lableptr;for (int i = 0; i < rows; i++){int* data = _lableImg.ptr<int>(i);uchar *masKptr = mask.ptr<uchar>(i);for (int j = 0; j < cols; j++){if (data[j] == 255 && mask.at<uchar>(i, j) != 1){mask.at<uchar>(i, j) = 1;std::stack<std::pair<int, int>> neighborPixels;neighborPixels.push(std::pair<int, int>(i, j)); // pixel position: <i,j>++label; //begin with a new labelwhile (!neighborPixels.empty()){//get the top pixel on the stack and label it with the same labelstd::pair<int, int> curPixel = neighborPixels.top();int curY = curPixel.first;int curX = curPixel.second;_lableImg.at<int>(curY, curX) = label;//pop the top pixelneighborPixels.pop();//push the 4-neighbors(foreground pixels)if (curX - 1 >= 0){if (_lableImg.at<int>(curY, curX - 1) == 255 && mask.at<uchar>(curY, curX - 1) != 1) //leftpixel{neighborPixels.push(std::pair<int, int>(curY, curX - 1));mask.at<uchar>(curY, curX - 1) = 1;}}if (curX + 1 <= cols - 1){if (_lableImg.at<int>(curY, curX + 1) == 255 && mask.at<uchar>(curY, curX + 1) != 1)// right pixel{neighborPixels.push(std::pair<int, int>(curY, curX + 1));mask.at<uchar>(curY, curX + 1) = 1;}}if (curY - 1 >= 0){if (_lableImg.at<int>(curY - 1, curX) == 255 && mask.at<uchar>(curY - 1, curX) != 1)// up pixel{neighborPixels.push(std::pair<int, int>(curY - 1, curX));mask.at<uchar>(curY - 1, curX) = 1;}}if (curY + 1 <= rows - 1){if (_lableImg.at<int>(curY + 1, curX) == 255 && mask.at<uchar>(curY + 1, curX) != 1)//down pixel{neighborPixels.push(std::pair<int, int>(curY + 1, curX));mask.at<uchar>(curY + 1, curX) = 1;}}}}}}
}//---------------------------------【颜色标记程序】-----------------------------------
//彩色显示
cv::Scalar GetRandomColor()
{uchar r = 255 * (rand() / (1.0 + RAND_MAX));uchar g = 255 * (rand() / (1.0 + RAND_MAX));uchar b = 255 * (rand() / (1.0 + RAND_MAX));return cv::Scalar(b, g, r);
}void LabelColor(const cv::Mat& labelImg, cv::Mat& colorLabelImg)
{int num = 0;if (labelImg.empty() ||labelImg.type() != CV_32SC1){return;}std::map<int, cv::Scalar> colors;int rows = labelImg.rows;int cols = labelImg.cols;colorLabelImg.release();colorLabelImg.create(rows, cols, CV_8UC3);colorLabelImg = cv::Scalar::all(0);for (int i = 0; i < rows; i++){const int* data_src = (int*)labelImg.ptr<int>(i);uchar* data_dst = colorLabelImg.ptr<uchar>(i);for (int j = 0; j < cols; j++){int pixelValue = data_src[j];if (pixelValue > 1){if (colors.count(pixelValue) <= 0){colors[pixelValue] = GetRandomColor();num++;}cv::Scalar color = colors[pixelValue];*data_dst++ = color[0];*data_dst++ = color[1];*data_dst++ = color[2];}else{data_dst++;data_dst++;data_dst++;}}}printf("color num : %d \n", num);
}//------------------------------------------【测试主程序】-------------------------------------
int main()
{cv::Mat binImage = cv::imread("sda.jpg", 0);//cv::threshold(binImage, binImage, 50, 1, CV_THRESH_BINARY);cv::Mat labelImg;double time;time = getTickCount();SeedFillNew(binImage, labelImg);time = 1000 * ((double)getTickCount() - time) / getTickFrequency();cout << std::fixed << time << "ms" << endl;//彩色显示/*cv::Mat colorLabelImg;LabelColor(labelImg, colorLabelImg);cv::imshow("colorImg", colorLabelImg);*///灰度显示cv::Mat grayImg;labelImg *= 10;labelImg.convertTo(grayImg, CV_8UC1);cv::imshow("labelImg", grayImg);double minval, maxval;minMaxLoc(labelImg, &minval, &maxval);cout << "minval" << minval << endl;cout << "maxval" << maxval << endl;cv::waitKey(0);return 0;
}
终于知道是啥原因了,我在进行Prewitt算子对边缘进行粗定位检测过后,没有进行去噪处理,一定要把图像转换为二值图像,就方便多了。并且还要记住,霍夫检测的直线像素是255白线才可以,经过长时间的试错终于解决了。输入原图像如下所示,我这里使用的去噪对比了四种,但是下面这种是最好的。Opencv 非局部降噪_51CTO博客_opencv降噪Opencv 非局部降噪,opencv自带的非局部降噪算法:CV_EXPORTS_WvoidfastNlMeansDenoising(InputArraysrc,OutputArraydst,floath=3,inttemplateWindowSize=7,intsearchWindowSize=21);h是过滤强度,templateWindowSize是分块大小,searchWindowSize是搜索区域大小。应用实例intmain(){MatI..https://blog.51cto.com/u_15458280/4843576
#include<opencv2/opencv.hpp>
#include<iostream>
using namespace std;
using namespace cv;//数组从大到小排序
void reserve(int x[], int n) {int i, j, temp;for (i = 0; i < n - 1; i++) { //一共n个元素,则需要比较n-1次for (j = 0; j < n - 1 - i; j++) { //每一个元素需要比较的次数if (x[i] < x[i + j + 1]) {temp = x[i];x[i] = x[i + j + 1];x[i + j + 1] = temp;}}}
}void add_salt_pepper_noise(Mat &image) {RNG rng(12345);int h = image.rows;int w = image.cols;int nums = 10000;for (int i = 0; i < nums; i++) {int x = rng.uniform(0, w);int y = rng.uniform(0, h);if (i % 2 == 1) {image.at<Vec3b>(y, x) = Vec3b(255, 255, 255);}else {image.at<Vec3b>(y, x) = Vec3b(0, 0, 0);}}imshow("salt pepper", image);
}void gaussian_noise(Mat &image) {Mat noise = Mat::zeros(image.size(), image.type());randn(noise, (15, 15, 15), (30, 30, 30));Mat dst;add(image, noise, dst);imshow("gaussian noise", dst);dst.copyTo(image);
}Mat convertTo3Channels(const Mat& binImg)
{Mat three_channel = Mat::zeros(binImg.rows, binImg.cols, CV_8UC3);vector<Mat> channels;for (int i = 0; i < 3; i++){channels.push_back(binImg);}merge(channels, three_channel);return three_channel;
}int main(int argc, char*argv[])
{//加载图像Mat img, gray_image, dst;img = imread("垂直边缘.jpg");Mat img1 = imread("垂直边缘.jpg");//判断图像是否导入成功if (img.empty()){cout << "加载失败" << endl;return -1;}//显示图像namedWindow("original image", WINDOW_AUTOSIZE);imshow("original image", img);//转换灰度图像cvtColor(img, gray_image, COLOR_BGR2GRAY);//获取灰度图像宽度和高度int width = gray_image.cols;int height = gray_image.rows;//遍历像素值(单通道)for (int row = 0; row < height; row++){for (int col = 0; col < width; col++){int gray = gray_image.at<uchar>(row, col);gray_image.at<uchar>(row, col) = 255 - gray; //图像取反};};namedWindow("inv_gray_image", WINDOW_AUTOSIZE);imshow("inv_gray_image", gray_image);Mat sh;fastNlMeansDenoising(gray_image, sh, 21, 7, 21);namedWindow("inv_gray_image1", WINDOW_AUTOSIZE);imshow("inv_gray_image1", sh);waitKey(50);//Mat s;//获取灰度图像宽度和高度width = sh.cols;height = sh.rows;//遍历像素值(单通道)for (int row = 0; row < height; row++){for (int col = 0; col < width; col++){int gray = sh.at<uchar>(row, col);sh.at<uchar>(row, col) = 255 - gray; //图像取反};};namedWindow("inv_gray_image2", WINDOW_AUTOSIZE);imshow("inv_gray_image2", sh);cv::threshold(sh, sh, 50, 255, cv::THRESH_BINARY);cv::namedWindow("二值化", CV_WINDOW_NORMAL);imshow("二值化", sh);vector<Vec4i> lines;HoughLinesP(sh, lines, 1, CV_PI / 180, 50,100, 5);int Length[100] = { 0 };//存放直线长度for (size_t i = 0; i < lines.size(); i++){Vec4i I = lines[i];double x1 = I[0];double y1 = I[1];double x2 = I[2];double y2 = I[3];//筛选满足条件的点if (abs(x1 - x2) + abs(y1 - y2) > 50){Length[i] = sqrt((x1 - x2)*(x1 - x2) + (y1 - y2) * (y1 - y2));//将满足条件的点画出line(img, Point2d(x1, y1), Point2d(x2, y2), Scalar(0, 255, 255), 2);cout << " " << "(" << x1 << "," << y1 << ")" << " " << "(" << x2 << "," << y2 << ")" << endl;//line(canny, Point2d(x1, y1), Point2d(x2, y2), Scalar(0, 255, 255), 2);}}Mat imgShow;imgShow = img;resize(imgShow, imgShow, Size(imgShow.cols / 4, imgShow.rows / 4));imshow("imgShow", imgShow);imwrite("shuchu.png", imgShow);reserve(Length, 100);for (int i = 0; i < 100; i++) {cout << "长度" << Length[i] << endl; //输出排序后的数组元素}for (size_t i = 0; i < lines.size(); i++){Vec4i I = lines[i];double x1 = I[0];double y1 = I[1];double x2 = I[2];double y2 = I[3];cout << "sdjk" << endl;cout << int(sqrt((x1 - x2)*(x1 - x2) + (y1 - y2) * (y1 - y2))) << endl;//筛选满足条件的点if ((int(sqrt((x1 - x2)*(x1 - x2) + (y1 - y2) * (y1 - y2))) == Length[0]) || (int(sqrt((x1 - x2)*(x1 - x2) + (y1 - y2) * (y1 - y2))) == Length[1])){//将满足条件的点画出line(img1, Point2d(x1, y1), Point2d(x2, y2), Scalar(0, 255, 255), 2);cout << "djfkljsa " << "(" << x1 << "," << y1 << ")" << " " << "(" << x2 << "," << y2 << ")" << endl;//line(canny, Point2d(x1, y1), Point2d(x2, y2), Scalar(0, 255, 255), 2);}}imgShow = img1;resize(imgShow, imgShow, Size(imgShow.cols / 4, imgShow.rows / 4));imshow("imgShow2", imgShow);imwrite("shuchu2.png", imgShow);waitKey(0);return 0;
};
结果图如下所示:
终于弄出来了,去干饭。
相关文章:
提取接近竖直物体(粗定位)
由于项目的需要提取图像之中的一个接近于竖直的物体,一般的方法是进行图像分割,分割方式使用什么OTSU方式以及hsv方法等等。但是项目中使用的相机是黑白相机,会受到一定的限制。因此想到的是使用线条提取方式。线条提取方式之中最好的方法是使…...
程序环境和预处理
目录一、程序的翻译环境和执行环境二、编译链接2.1 翻译环境2.2 编译2.2.1 预处理2.2.2 编译2.2.3 汇编2.3 链接2.4 结果三、运行环境四、预处理详解4.1 #define4.1.1 #define定义标识符4.1.2 #define定义宏4.1.3 #define 替换规则4.1.4 #和##4.1.5 带副作用的宏参数4.1.6 宏和…...
财报解读:业务复苏迹象明显,中国中免能否重写增长神话?
2月3日,中国中免披露2022年度业绩快报,2022年总营收为544.63亿元,同比下降19.52%;实现归属于上市公司股东的净利润50.25亿元,同比下降47.95%。来源:中国中免2022年度业绩快报业绩近乎腰斩,但从长…...
macOS中虚拟机桥接模式分配静态ip
1.首先使用dhclient命令,在局域网中分配一个C类地址。 2.获得地址后,输入ifconfig,查看分配的地址。 3.然后编辑vi /etc/sysconfig/network-scripts/ifcfg-en***文件 在该配置文件中编辑,设置ONBOOTyes,而后添加静态配…...
prometheus increase函数统计得到小数
今天发现prometheus的increase函数得到了小数,研究一下源码,以下是rate/increase/delta 对应的计算函数https://github.com/prometheus/prometheus/blob/d77b56e88e3d554a499e22d2073812b59191256c/promql/functions.go#L55// extrapolatedRate is a uti…...
C++学习记录——유 类和对象(3)
文章目录1、赋值运算符重载1、运算符重载1、理解2、运算符重载实例2、赋值运算符重载2、日期类的实现1、加减函数1、加函数2、减函数2、前/后置--重载3.两个日期相减其他1、流插入2、流提取日期类的整体实现代码: https://gitee.com/kongqizyd/start-some-c-codes-for-learning…...
基于Hi3861平台的OpenHarmony程序是如何启动运行的
一、前言 在继续后面课程的内容讲解前,我们要知道在H3861平台上编写的代码到底是如何启动的,这一点很重要。 先分析HelloWorld程序的启动运行流程,并顺便讲解OpenHarmony在H3861平台的,系统是从哪里启动的。 反着推导函数之间具体…...
2023彻底解决Typora使用iPic微博图床失效问题
一、问题描述用Typora搭配iPic图床使用,最近csdn图片显示不出来用浏览器打开图片显示403,这里原因是微博图床出问题了导致的而使用iPic其他图床则需要一直付费,那有没有一劳永逸的解决所有问题呢?二、旧图恢复首先怎么找回旧图&am…...
Revit中添加水平仰视平面图及水平剖面
一、 Revit中如何添加水平仰视平面图 在Revit平面视图中视角是俯视视角,但是在一些特殊的情况下,我们可能需要创建仰视视角的平面视图,例如我们需要向上看天花板的灯具布置的时候。 1.下面讲一下如何在添加仰视平面视图的方法。如图1在模型中…...
Python 循环语句
Python的循环语句,程序在一般情况下是按顺序执行的。编程语言提供了各种控制结构,允许更复杂的执行路径。循环语句允许我们执行一个语句或语句组多次,下面是在大多数编程语言中的循环语句的一般形式:Python 提供了 for 循环和 whi…...
使用 ThreeJS 实现第一个三维场景(详)
文章目录参考描述index.html三维场景的基本实现导入 ThreeJS准备工作场景摄像机视锥体正交摄像机透视摄像机渲染器后续处理将摄像机添加至场景中移动摄像机设置画布尺寸将渲染器创建的画布添加到 HTML 元素中渲染物体结构材质合成将物体添加至场景中代码总汇执行效果动画reques…...
《小猫猫大课堂》三轮5——动态内存管理(通讯录动态内存化)
宝子,你不点个赞吗?不评个论吗?不收个藏吗? 最后的最后,关注我,关注我,关注我,你会看到更多有趣的博客哦!!! 喵喵喵,你对我真的很重…...
【Selenium学习】Selenium 八大定位法
1.1 ID定位HTML Tag 的 id 属性值是唯一的,故不存在根据 id 定位多个元素的情况。下面以在百度首页搜索框输入文本“python”为例。搜索框的 id 属性值为“kw”,如图1.1所示:代码如下,“find_element_by_id”方法已废弃࿰…...
算法训练营 day41 贪心算法 单调递增的数字 买卖股票的最佳时机含手续费
算法训练营 day41 单调递增的数字 买卖股票的最佳时机含手续费 单调递增的数字 738. 单调递增的数字 - 力扣(LeetCode) 当且仅当每个相邻位数上的数字 x 和 y 满足 x < y 时,我们称这个整数是单调递增的。 给定一个整数 n ,…...
【数据结构-JAVA】排序
排序在现实生活中的应用可谓相当广泛,比如电商平台中,选购商品时,使用价格排序或是综合排序、高考填报志愿的时候,会参考全国大学排名的情况。下面介绍一些计算机中与排序相关的概念:排序:所谓排序…...
基于注解管理Bean
一、介绍从 Java 5 开始,Java 增加了对注解(Annotation)的支持,它是代码中的一种特殊标记,可以在编译、类加载和运行时被读取,执行相应的处理。开发人员可以通过注解在不改变原有代码和逻辑的情况下&#x…...
Containerd 的 Bug 导致容器被重建!如何避免?
作者简介邓宇星,SUSE Rancher 中国区软件架构师,6 年云原生领域经验,参与Rancher 1.x 到 Rancher 2.x 版本迭代,目前负责 Rancher For openEuler(RFO) 项目开发。最近我们关注到一个关于 containerd 运行时的 issue(https://g…...
win环境安装部署Jenkins
win环境安装部署Jenkins,2022年11月:从2022年 6 月 28 日发布的 Jenkins 2.357 和2022年9 月发布的 LTS 版本开始,Jenkins 需要 Java 11 才能使用,放弃 Java 8,如果用JDK1.8,那么Jenkins版本需要是2.357版本…...
网络变压器与不同芯片之间的匹配原则及POE通讯产品需要注意哪些方面
Hqst盈盛电子导读:网络变压器与不同芯片之间的匹配原则及POE通讯产品需要注意哪些方面网络变压器与不同芯片之间的匹配原则:一,电流型PHY芯片一般要配的网络变压器:1、变压器PHY侧3线共模电感 (更适合POE产品ÿ…...
Spring WebFlux
目录 基于注解编程模型 函数式编程模型 传统的基于Servlet的Web框架,如Spring MVC,在本质上都是阻塞和多线程的,每个连接都会使用一个线程。在请求处理的时候,会在线程池中拉取一个工作者( worker )线程来对请求进行处理。同时,请求线程是阻塞的,直到工作者线程提示它已…...
C++基础面试题:new和malloc的区别
面试题:new和malloc的区别或new和malloc的异同 相同点: 1、new/delete和malloc/free它们都是内存申请和释放的函数。 2、new/delete和malloc/free 都要一一对应,调用了多少次new 就需要调用多少次delete;同 理调用多少次ma…...
WebDAV之葫芦儿·派盘+KMPlayer
KMPlayer 支持WebDAV方式连接葫芦儿派盘。 KMPlayer几乎可以播放您系统上所有的影音文件,支持几乎全部音视频格式。通过其强大的插件功能,可以支持层出不穷的新格式。软件还具有齐全的操控功能,支持捕获音频、捕获AVI、捕获画面、外挂字幕、自定义编辑设置,是视频爱好者的不…...
杨浦区人工智能及大数据(云计算)企业登记工作(2023年度)的通知
各相关单位: 根据《“长阳秀带”在线新经济产业集聚发展若干政策》(杨府发〔2022〕2号)及其实施细则的要求,现组织开展2023年度杨浦区人工智能与大数据(云计算)企业登记备案工作,现将相关工作通知如下: 一…...
2023年去培训机构学前端还是Java?
选择专业肯定是优先考虑更有发展前景和钱途的专业。毕竟IT专业的培训费都不低,基本都要一两万左右,咱们花钱总是希望获得最大回报。 那么到底哪个更有发展前景呢? 零基础能学得会吗? 就业薪资如何呢? 前言 不知道大家有…...
【React】组件事件
React(二) 创建组件 函数组件 函数组件:使用JS的函数或者箭头函数创建的组件 使用 JS 的函数(或箭头函数)创建的组件,叫做函数组件约定1:函数名称必须以大写字母开头,React 据此区分组件和普通的 HTML约定2:函数组…...
黑/白盒测试说明
白盒测试白盒测试也称结构测试或逻辑驱动测试,它是按照程序内部的结构测试程序,通过测试来检测产品内部动作是否按照设计规格说明书的规定正常进行,检验程序中的每条通路是否都能按预定要求正确工作。白盒测试的测试方法有代码检查法、静态结…...
车道线检测-Eigenlanes 论文学习笔记
论文:《Eigenlanes: Data-Driven Lane Descriptors for Structurally Diverse Lanes》 代码:https://github.com/dongkwonjin/Eigenlanes 核心:在 Eigenlane Space 中检测车道线 创新点 Eigenlane:数据驱动的车道描述符ÿ…...
docker run mysql -e 的环境变量 Environment Variables
例子 sudo docker run -itd --name DockerMysqlLatest3307 -p 3307:3306 -e MYSQL_ROOT_PASSWORDroot的密码 mysql:latest### root无密码 sudo docker run -itd --name Mysql57 -p 57:3306 -e MYSQL_ALLOW_EMPTY_PASSWORDroot mysql:5.7https://hub.docker.com/_/mysql?tabde…...
第17章 MongoDB 条件操作符教程
第17章 MongoDB 条件操作符教程 描述 条件操作符用于比较两个表达式并从mongoDB集合中获取数据。 在本章节中,咱们将讨论如何在MongoDB中使用条件操作符。 MongoDB中条件操作符有: (>) 大于 - $gt(<) 小于 - $lt(>) 大于等于 - $gte(< …...
电子技术——共源共栅放大器
电子技术——共源共栅放大器 之前我们提到过,提高基础增益单元(共源放大器)的一种方法是提高其 ror_oro 的阻值,之后我们学过共栅放大器作为电流缓冲器可以做到这一点,自然地我们就得到了终极解决方案,也…...
开锁都在什么网站做/淘宝seo搜索引擎原理
1、循环 1.1、for循环 语法结构: for(初始化变量; 条件表达式; 操作表达式 ){//循环体 } 名称作用初始化变量通常被用于初始化一个计数器,该表达式可以使用 var 关键字声明新的变量,这个变量帮我们来记录次数。条件表达式用于确定每一次循…...
网站推广费用ihanshi/什么软件可以排名次
当你需要将DWG或者DXF格式的CAD图纸转为BMP图片格式的时候,你会怎么做呢?在网上找格式转换的软件?先截图再修改格式?......其实,并不需要那么麻烦,因为轻量级CAD绘图软件——浩辰CAD看图王电脑版中直接就有…...
长沙外贸网站建设/图片搜索
如今,蓝牙已成为移动设备不可或缺的一部分,智能手机与智能手表和无线耳机互连。默认情况下,大多数设备都配置为接受来自附近任何未经身份验证的设备的蓝牙连接,蓝牙数据包由蓝牙芯片(也称为控制器)处理,然后传递到主机…...
做家装网站源码/网络销售就是忽悠人
问题描述先看下后台返回的Set-Cookie字段:查看浏览器Cookies:思路发现只有JESSIONID存入到了浏览器Storage中的Cookies。通过比较 Response Headers 中两个 set-cookie字段可以发现字段不同:JSESSIONID:path/ZTEV-JWT-Token&#…...
做淘宝客网站域名是别人的/网页模板免费html
转载自 http://www.linuxsir.org/bbs/showthread.php?t184419 如何从源码包安装软件? 从源码包安装软件最重要的就是仔细阅读README INSTALL等说明文件 它会告诉你怎样才能成功安装 通常从源码包安装软件的步骤是:tar jxvf gtk-2.4.13.tar.bz2 解开源码…...
男人与女人做视频网站/站长工具seo推广 站长工具查询
一、下载安装文件百度网盘:链接:https://pan.baidu.com/s/1SerJrXnVS774lcHY58KIww提取码:txyr二、安装openoffice1、将下载的安装文件上传到linux你指定的目录下,这个没有固定的目录,你放在那里的可以。如图ÿ…...