霍夫圆
霍夫圆的变换与霍夫直线类似,其原理较为复杂,这里暂时不以叙述。其检测实现是以灰度图像来找到候选区域,然会进行霍夫检测,以此减少算法的时间复杂度。但该方式极易受到噪声影响,对图像的噪声敏感,故先须进行噪声抑制处理。其函数声明如下:
HoughCircles(image, circles, method, dp, minDist, param1, param2, minRadius, maxRadius);
各参数解释如下:
-
image
8单位通道的灰度图形。 -
circles
输出的3个向量的数组,圆心与半径。 -
method
非可选的基于梯度霍夫变换。 -
dp
图片分辨率,dp越大,图像就会减少相应的分辨率。 -
minDist
两圆之间最小距离。 -
param1
边缘检测中Canny的最高阈值。 -
param2
累加阈值。 -
minRadius
检测的最小圆半径,单位像素。 -
maxRadius
检测的最大圆半径,单位像素。
需要说明的是,霍夫圆检测非常消耗时间,但可以通过指定参数范围来减少时间复杂度。这里的示意图即使肉眼可见为类似五环图,但最终仍检测出了3个圆。
Java代码(JavaFX Controller层)
public class Controller{
@FXML private Text fxText;
@FXML private ImageView imageView;
@FXML public void handleButtonEvent(ActionEvent actionEvent) throws IOException {
Node source = (Node) actionEvent.getSource();
Window theStage = source.getScene().getWindow();
FileChooser fileChooser = new FileChooser();
FileChooser.ExtensionFilter extFilter = new FileChooser.ExtensionFilter("PNG files (*.png)", "*.png");
fileChooser.getExtensionFilters().add(extFilter);
fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("JPG Files(*.jpg)", "*.jpg"));
File file = fileChooser.showOpenDialog(theStage);
runInSubThread(file.getPath());
}
private void runInSubThread(String filePath){
new Thread(new Runnable() {
@Override
public void run() {
try {
WritableImage writableImage = houghCircles(filePath);
Platform.runLater(new Runnable() {
@Override
public void run() {
imageView.setImage(writableImage);
}
});
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
}
private WritableImage houghCircles(String filePath) throws IOException {
System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
Mat src = Imgcodecs.imread(filePath);
Mat dst = new Mat();
Mat gray = new Mat();
Imgproc.pyrMeanShiftFiltering(src, gray, 15, 80);
Imgproc.cvtColor(gray, gray, Imgproc.COLOR_BGR2GRAY);
Imgproc.GaussianBlur(gray, gray, new Size(3,3), 0);
Mat circles = new Mat();
dst.create(src.size(), src.type());
Imgproc.HoughCircles(gray, circles, Imgproc.HOUGH_GRADIENT, 1, 20,100,25,35,70);
for (int i = 0; i < circles.cols(); i++) {
float[] info = new float[3];
circles.get(0,i, info);
Imgproc.circle(dst, new Point((int)info[0], (int)info[1]), (int)info[1], new Scalar(255,0,0),2,8,0);
}
MatOfByte matOfByte = new MatOfByte();
Imgcodecs.imencode(".jpg", dst, matOfByte);
byte[] bytes = matOfByte.toArray();
InputStream in = new ByteArrayInputStream(bytes);
BufferedImage bufImage = ImageIO.read(in);
WritableImage writableImage = SwingFXUtils.toFXImage(bufImage, null);
return writableImage;
}
}
运行图
原图: