# 图像处理之霍夫变换（直线检测算法）

X *cos(theta) + y * sin(theta)  = r 其中角度theta指r与X轴之间的夹角，r为到直线几何垂

(x –a ) ^2 + (y-b) ^ 2 = r^2其中(a, b)为圆的中心点坐标，r圆的半径。这样霍夫的参数空间就

1.      读取一幅带处理二值图像，最好背景为黑色。

2.      取得源像素数据

3.      根据直线的霍夫变换公式完成霍夫变换，预览霍夫空间结果

4.       寻找最大霍夫值，设置阈值，反变换到图像RGB值空间(程序难点之一)

5.      越界处理，显示霍夫变换处理以后的图像

 // prepare for hough transform
int centerX = width / 2;
int centerY = height / 2;
double hough_interval = PI_VALUE/(double)hough_space;

int max = Math.max(width, height);
int max_length = (int)(Math.sqrt(2.0D) * max);
hough_1d = new int[2 * hough_space * max_length];

// start hough transform now....
int[][] image_2d = convert1Dto2D(inPixels);
for (int row = 0; row < height; row++) {
for (int col = 0; col < width; col++) {
int p = image_2d[row][col] & 0xff;
if(p == 0) continue; // which means background color

// since we does not know the theta angle and r value,
// we have to calculate all hough space for each pixel point
// then we got the max possible theta and r pair.
// r = x * cos(theta) + y * sin(theta)
for(int cell=0; cell < hough_space; cell++ ) {
max = (int)((col - centerX) * Math.cos(cell * hough_interval) + (row - centerY) * Math.sin(cell * hough_interval));
max += max_length; // start from zero, not (-max_length)
if (max < 0 || (max >= 2 * max_length)) {// make sure r did not out of scope[0, 2*max_lenght]
continue;
}
hough_2d[cell][max] +=1;
}
}
}

// find the max hough value
int max_hough = 0;
for(int i=0; i<hough_space; i++) {
for(int j=0; j<2*max_length; j++) {
hough_1d[(i + j * hough_space)] = hough_2d[i][j];
if(hough_2d[i][j] > max_hough) {
max_hough = hough_2d[i][j];
}
}
}
System.out.println("MAX HOUGH VALUE = " + max_hough);

// transfer back to image pixels space from hough parameter space
int hough_threshold = (int)(threshold * max_hough);

      // transfer back to image pixels space from hough parameter space
int hough_threshold = (int)(threshold * max_hough);
for(int row = 0; row < hough_space; row++) {
for(int col = 0; col < 2*max_length; col++) {
if(hough_2d[row][col] < hough_threshold) // discard it
continue;
int hough_value = hough_2d[row][col];
boolean isLine = true;
for(int i=-1; i<2; i++) {
for(int j=-1; j<2; j++) {
if(i != 0 || j != 0) {
int yf = row + i;
int xf = col + j;
if(xf < 0) continue;
if(xf < 2*max_length) {
if (yf < 0) {
yf += hough_space;
}
if (yf >= hough_space) {
yf -= hough_space;
}
if(hough_2d[yf][xf] <= hough_value) {
continue;
}
isLine = false;
break;
}
}
}
}
if(!isLine) continue;

// transform back to pixel data now...
double dy = Math.sin(row * hough_interval);
double dx = Math.cos(row * hough_interval);
if ((row <= hough_space / 4) || (row >= 3 * hough_space / 4)) {
for (int subrow = 0; subrow < height; ++subrow) {
int subcol = (int)((col - max_length - ((subrow - centerY) * dy)) / dx) + centerX;
if ((subcol < width) && (subcol >= 0)) {
image_2d[subrow][subcol] = -16776961;
}
}
} else {
for (int subcol = 0; subcol < width; ++subcol) {
int subrow = (int)((col - max_length - ((subcol - centerX) * dx)) / dy) + centerY;
if ((subrow < height) && (subrow >= 0)) {
image_2d[subrow][subcol] = -16776961;
}
}
}
}
}

package com.gloomyfish.image.transform;

import java.awt.image.BufferedImage;

import com.process.blur.study.AbstractBufferedImageOp;

public class HoughLineFilter extends AbstractBufferedImageOp {
public final static double PI_VALUE = Math.PI;
private int hough_space = 500;
private int[] hough_1d;
private int[][] hough_2d;
private int width;
private int height;

private float threshold;
private float scale;
private float offset;

public HoughLineFilter() {
// default hough transform parameters
//  scale = 1.0f;
//  offset = 0.0f;
threshold = 0.5f;
scale = 1.0f;
offset = 0.0f;
}

public void setHoughSpace(int space) {
this.hough_space = space;
}

public float getThreshold() {
return threshold;
}

public void setThreshold(float threshold) {
this.threshold = threshold;
}

public float getScale() {
return scale;
}

public void setScale(float scale) {
this.scale = scale;
}

public float getOffset() {
return offset;
}

public void setOffset(float offset) {
this.offset = offset;
}

@Override
public BufferedImage filter(BufferedImage src, BufferedImage dest) {
width = src.getWidth();
height = src.getHeight();

if ( dest == null )
dest = createCompatibleDestImage( src, null );

int[] inPixels = new int[width*height];
int[] outPixels = new int[width*height];
getRGB( src, 0, 0, width, height, inPixels );
houghTransform(inPixels, outPixels);
setRGB( dest, 0, 0, width, height, outPixels );
return dest;
}

private void houghTransform(int[] inPixels, int[] outPixels) {
// prepare for hough transform
int centerX = width / 2;
int centerY = height / 2;
double hough_interval = PI_VALUE/(double)hough_space;

int max = Math.max(width, height);
int max_length = (int)(Math.sqrt(2.0D) * max);
hough_1d = new int[2 * hough_space * max_length];

// define temp hough 2D array and initialize the hough 2D
hough_2d = new int[hough_space][2*max_length];
for(int i=0; i<hough_space; i++) {
for(int j=0; j<2*max_length; j++) {
hough_2d[i][j] = 0;
}
}

// start hough transform now....
int[][] image_2d = convert1Dto2D(inPixels);
for (int row = 0; row < height; row++) {
for (int col = 0; col < width; col++) {
int p = image_2d[row][col] & 0xff;
if(p == 0) continue; // which means background color

// since we does not know the theta angle and r value,
// we have to calculate all hough space for each pixel point
// then we got the max possible theta and r pair.
// r = x * cos(theta) + y * sin(theta)
for(int cell=0; cell < hough_space; cell++ ) {
max = (int)((col - centerX) * Math.cos(cell * hough_interval) + (row - centerY) * Math.sin(cell * hough_interval));
max += max_length; // start from zero, not (-max_length)
if (max < 0 || (max >= 2 * max_length)) {// make sure r did not out of scope[0, 2*max_lenght]
continue;
}
hough_2d[cell][max] +=1;
}
}
}

// find the max hough value
int max_hough = 0;
for(int i=0; i<hough_space; i++) {
for(int j=0; j<2*max_length; j++) {
hough_1d[(i + j * hough_space)] = hough_2d[i][j];
if(hough_2d[i][j] > max_hough) {
max_hough = hough_2d[i][j];
}
}
}
System.out.println("MAX HOUGH VALUE = " + max_hough);

// transfer back to image pixels space from hough parameter space
int hough_threshold = (int)(threshold * max_hough);
for(int row = 0; row < hough_space; row++) {
for(int col = 0; col < 2*max_length; col++) {
if(hough_2d[row][col] < hough_threshold) // discard it
continue;
int hough_value = hough_2d[row][col];
boolean isLine = true;
for(int i=-1; i<2; i++) {
for(int j=-1; j<2; j++) {
if(i != 0 || j != 0) {
int yf = row + i;
int xf = col + j;
if(xf < 0) continue;
if(xf < 2*max_length) {
if (yf < 0) {
yf += hough_space;
}
if (yf >= hough_space) {
yf -= hough_space;
}
if(hough_2d[yf][xf] <= hough_value) {
continue;
}
isLine = false;
break;
}
}
}
}
if(!isLine) continue;

// transform back to pixel data now...
double dy = Math.sin(row * hough_interval);
double dx = Math.cos(row * hough_interval);
if ((row <= hough_space / 4) || (row >= 3 * hough_space / 4)) {
for (int subrow = 0; subrow < height; ++subrow) {
int subcol = (int)((col - max_length - ((subrow - centerY) * dy)) / dx) + centerX;
if ((subcol < width) && (subcol >= 0)) {
image_2d[subrow][subcol] = -16776961;
}
}
} else {
for (int subcol = 0; subcol < width; ++subcol) {
int subrow = (int)((col - max_length - ((subcol - centerX) * dx)) / dy) + centerY;
if ((subrow < height) && (subrow >= 0)) {
image_2d[subrow][subcol] = -16776961;
}
}
}
}
}

// convert to hough 1D and return result
for (int i = 0; i < this.hough_1d.length; i++)
{
int value = clamp((int)(scale * this.hough_1d[i] + offset)); // scale always equals 1
this.hough_1d[i] = (0xFF000000 | value + (value << 16) + (value << 8));
}

// convert to image 1D and return
for (int row = 0; row < height; row++) {
for (int col = 0; col < width; col++) {
outPixels[(col + row * width)] = image_2d[row][col];
}
}
}

public BufferedImage getHoughImage() {
BufferedImage houghImage = new BufferedImage(hough_2d[0].length, hough_space, BufferedImage.TYPE_4BYTE_ABGR);
setRGB(houghImage, 0, 0, hough_2d[0].length, hough_space, hough_1d);
return houghImage;
}

public static int clamp(int value) {
if (value < 0)
value = 0;
else if (value > 255) {
value = 255;
}
return value;
}

private int[][] convert1Dto2D(int[] pixels) {
int[][] image_2d = new int[height][width];
int index = 0;
for(int row = 0; row < height; row++) {
for(int col = 0; col < width; col++) {
index = row * width + col;
image_2d[row][col] = pixels[index];
}
}
return image_2d;
}

}

|
5天前
|

【7月更文挑战第13天】目标检测作为计算机视觉领域的重要研究方向，近年来在深度学习技术的推动下取得了显著进展。然而，面对复杂多变的实际应用场景，仍需不断研究和探索更加高效、鲁棒的目标检测算法。随着技术的不断发展和应用场景的不断拓展，相信目标检测算法将在更多领域发挥重要作用。
25 9
|
8天前
|

Python实现Prophet时间序列数据建模与异常值检测(Prophet算法)项目实战
Python实现Prophet时间序列数据建模与异常值检测(Prophet算法)项目实战
23 2
|
8天前
|

18 1
|
10天前
|

Python基于局部离群因子LOF算法(LocalOutlierFactor)实现信用卡数据异常值检测项目实战
Python基于局部离群因子LOF算法(LocalOutlierFactor)实现信用卡数据异常值检测项目实战
25 0
|
10天前
|

Python基于孤立森林算法(IsolationForest)实现数据异常值检测项目实战
Python基于孤立森林算法(IsolationForest)实现数据异常值检测项目实战
36 0
|
23小时前
|

MCKP-MMF算法是一种启发式流量估计方法，用于寻找无线传感器网络的局部最优解。它从最小配置开始，逐步优化部分解，调整访问点的状态。算法处理访问点的动态影响半径，根据带宽需求调整，以避免拥塞。在MATLAB 2022a中进行了仿真，显示了访问点半径请求变化和代价函数随时间的演变。算法分两阶段：慢启动阶段识别瓶颈并重设半径，随后进入周期性调整阶段，追求最大最小公平性。
9 1
|
3天前
|

**摘要：** K-means聚类算法分析，利用MATLAB2022a进行实现。算法基于最小化误差平方和，优点在于简单快速，适合大数据集，但易受初始值影响。文中探讨了该依赖性并通过实验展示了随机初始值对结果的敏感性。针对传统算法的局限，提出改进版解决孤点影响和K值选择问题。代码中遍历不同K值，计算距离代价，寻找最优聚类数。最终应用改进后的K-means进行聚类分析。
24 10
|
5天前
|

20 7
|
2天前
|

5 1
|
2天前
|

markdown 探索MATLAB2022a中WOA与DSN弱栅栏覆盖的创新融合，模拟鲸鱼捕食策略解决传感器部署问题。算法结合“搜索”、“包围”、“泡沫网”策略，优化节点位置以最大化复杂环境下的区域覆盖。目标函数涉及能量效率、网络寿命、激活节点数、通信质量及覆盖率。覆盖评估基于覆盖半径比例，旨在最小化未覆盖区域。 
15 2