Image processing road lane detection

Image processing road line detection

Experimental ideas

The LR is recorded by subtracting the vertical projection and one-dimensional filtering with a threshold. Finding the midpoint of the LR is the point that may exist on the road line, and Hough transforming these points to obtain a polar coordinate with the most points The curve is the line where the street line is located.

Experiment code

  1. Calculate the projection:
void CalProj(BYTE *pImg, int width, int height,int *projOrg)
{
    
    
int realHeight = height / 15;
int x, y; 
BYTE *pRow, *pCol; 
memset(projOrg, 0, sizeof(int)*width); 
for(pRow=pImg, y=0;y<realHeight;y++,pRow++) 
	for (x = 0, pCol = pRow; x < width; x++, pCol++) 
	{
    
     
		projOrg[x] += *pCol; 
	} 
for (x = 0; x < width; x++) 
{
    
    
	projOrg[x] /= realHeight; 
}
}

Because the image is to be processed in blocks, only a part of the projection can be calculated when calculating, and each column is added and normalized to obtain the vertical projection map.
2. One-dimensional mean filtering

	void AverFilter(int *pData, int width,int windowLength,int *pResult)
{
    
    
	int i, j, sum;
	int halfWindow = windowLength / 2;
	memset(pResult, 0, sizeof(int)*width);
	for (i = halfWindow; i <= width - halfWindow; i++)
	{
    
    
		sum = 0;
		for (j = i - halfWindow; j <= i + halfWindow; j++)
		{
    
    
			sum += pData[j];
		}
		pResult[i] = sum / windowLength;
	}
}

Here, the average in the window is directly averaged and then assigned, and then the vertical projection image is used to subtract the filtered projection image, and the threshold value of this image is calculated.
3. Otsu 求 Threshold

	int Otsu(int *hist)
{
    
    
	int threshold;
	int gmin,gmax;
	gmin = 0;
	gmax = 256;
	int sum = 0, size = 0;
	float u1 = 0, u2 = 0, u1sum = 0, w1 = 0, w2 = 0, dist, distMax = 0;
	while (hist[gmax] == 0)
		--gmax;
	while (hist[gmin] == 0)
		++gmin;
	for (int i = gmin; i < gmax; i++)
	{
    
    
		sum += hist[i] * i;
		size += hist[i];
	}
	for (int i = gmin; i < gmax; i++)
	{
    
    
		dist = 0;
		w1 += hist[i];
		u1sum += i * hist[i];
		u1 = u1sum / w1;
		w2 = size - w1;
		u2 = (sum - u1sum) / w2;
		dist = w1 * w2*pow(u1 - u2, 2);
		if (dist > distMax)
		{
    
    
			distMax = dist;
			threshold = i;
		}
	}
	return threshold;
}

When calculating the threshold, first obtain the maximum and minimum pixel values ​​of the whole image according to the histogram, so that the search range can be narrowed. When calculating w2, u2, the speed can be increased by subtracting w1 and u1 as a whole, and finally return to this threshold.
4. Find the midpoint of the sidewalk

	int GetMid(int *pProjOrg, int *pProjFilt,int width) 
{
    
    
	bool isBegin;
	int hist[256];
	int midsum = 0;
	int begin[32], end[32],mid[32];
	memset(hist, 0, sizeof(int) * 256);
	int num;
	for (int i = 0; i < width; i++)
	{
    
    
		num = abs(pProjOrg[i] - pProjFilt[i]);
		hist[num]++;
	}
	int threshold = Otsu(hist);
	for (int i = 0, isBegin = true; i < width; i++)
	{
    
    
		num = pProjOrg[i] - pProjFilt[i];
		if (num > threshold)
		{
    
    
			if (isBegin)
			{
    
    
				begin[midsum] = i;
			}
			isBegin = false;
		}
		else
		{
    
    
			if (!isBegin)
			{
    
    
				end[midsum] = i - 1;
				midsum++;
			}
			isBegin = true;
		}
		mid[midsum] = (end[midsum] - begin[midsum]) / 2;
	}
	return mid[32];
}

When the filtering subtraction of projection and projection is greater than the threshold, it is recorded as the starting point of the road line. When the difference is less than the threshold, it is recorded as the end point. The midpoint is calculated and recorded into an array, and the key array is returned.
5. Image block processing

	void Divide(BYTE *pImg, int width,int height)
{
    
    
	int *pProj, *pProjFilt;
	int block = width * height / 15;
	int mid[32], allMid[32][15];
	for (int i = 0; i < 15; i++)
	{
    
    
		CalProj(pImg+i*block, width, height, pProj);
		AverFilter(pProj, width, 3, pProjFilt);
		mid[32] = GetMid(pProj, pProjFilt, width);
		for (int j = 0; j < 32; j++)
		{
    
    
			allMid[j][i] = mid[j];
		}
	}
}

Divide the image into 15 blocks, perform the process of vertical projection, filtering, and finding the midpoint array of this block for each block, and store all midpoint arrays in a two-dimensional array. The first dimension records all midpoint values, and the second The dimension records where these values ​​are.
6. Hough transform

	void houghTransform(int mid[32][15])
{
    
    
	const double pi = 3.14;
	double LUTsin[91], LUTcos[91];
	int rou, theat;
	int count1[533][91];
	int count2[533][91];
	for (int i = 0; i <= 90; i++)
	{
    
    
		float cur = i * pi / 180;
		LUTsin[i] = sin(cur);
		LUTcos[i] = cos(cur);
	}
	int x, int y;
	for(int i=0;i<32;i++)
		for (int j = 0; j < 15; j++)
		{
    
    
			x = mid[i][j];
			y = (j+1)*23;
			if (x <= 452)//图像分左右两块处理
			{
    
    
				for (int theat = 1; theat < 90; theat++)
				{
    
    
					rou = x * LUTcos[theat] + y * LUTsin[theat];
					if (rou < 553 && rou>0)
					{
    
    
						count1[rou][theat]++;
					}	
				}
			}
			if (x > 452)
			{
    
    
				for (int theat = 1; theat < 90; theat++)
				{
    
    
					rou = x * LUTcos[theat] + y * LUTsin[theat];
					if (rou < 553 && rou>0)
					{
    
    
						count2[rou][theat]++;
					}
				}
			}
		}
	int max1 = 0;
	int max1_rou = 0, max1_theat = 0;
	max1 = count1[0][0];
	for (int i = 1; i < 553; i++)
		for (int j = 1; j < 90; j++)
		{
    
    
			if (count1[i][j] > max1)
			{
    
    
				max1 = count1[i][j];
				max1_rou = i;
				max1_theat = j;
			}
		}
	int max2 = 0;
	int max2_rou = 0, max2_theat = 0;
	max2 = count2[0][0];
	for (int i = 1; i < 553; i++)
		for (int j = 1; j < 90; j++)
		{
    
    
			if (count2[i][j] > max2)
			{
    
    
				max2 = count2[i][j];
				max2_rou = i;
				max2_theat = j;
			}
		}
	int k1, k2, b1, b2;
	exchange(max1_rou, max1_theat, &k1, &b1);
	exchange(max2_rou, max2_theat, &k2, &b2);
}

Convert the values ​​in the two-dimensional array to x and y for Hough transform, and process the image into left and right. Because the first two results of Hough transform voting will be very close, they are actually the road line on one side, so they are processed separately. The parameters of the polar coordinate equations on both sides are converted into rectangular coordinates and drawn as shown in the figure:
Insert picture description here

to sum up

This experiment allowed me to learn the importance of the code’s ability to adapt to different environments in dealing with practical problems. I previously felt that the location of the road line can be obtained by directly binarizing the photos of the road, but without considering factors such as lighting. , So when it’s dark, binarization won’t get anything, but by calculating the difference between projection and projection filtering, the problem of too dark light can be solved very well, but when I find the left and right lanes It seems inappropriate to divide the image into two, because the same street line may appear in the left and right halves of the image when the car is turning, so it cannot be recognized as a line, but the Hough transform vote You can't just take the top two. It should be possible to set a threshold to display all the high voting results. This should be able to display many line segments on the image, but in the case of the example image, the block processing effect is better.

Guess you like

Origin blog.csdn.net/qq_36587495/article/details/108165511