import org.opencv.core.*;
import org.opencv.imgcodecs.Imgcodecs;
import org.opencv.imgproc.Imgproc;
import java.util.Arrays;
import java.util.Stack;
public class OpencvTest {
static{ System.loadLibrary(Core.NATIVE_LIBRARY_NAME); }
static int frontBackground = 0; //前背景
static int backBackground = 255; //後背景
public static void main(String[] args) {
OpencvTest opencvTest = new OpencvTest();
//讀入圖片
// Mat src = Imgcodecs.imread("G:\\opencvPhoto\\photo\\picture2.jpg");
// //Laplacian算子對噪音敏感,事先去噪
// Imgproc.GaussianBlur(src, src, new Size(3, 3), 0);
// //灰度化
// Imgproc.cvtColor(src, src, Imgproc.COLOR_BGR2GRAY);
//圖片
Mat src = new Mat(new Size(3, 3), CvType.CV_8UC1);
src.put(0, 0, 1); src.put(0, 1, 2); src.put(0, 2, 3);
src.put(1, 0, 1); src.put(1, 1, 2); src.put(1, 2, 3);
src.put(2, 0, 1); src.put(2, 1, 2); src.put(2, 2, 3);
Mat sobel1 = new Mat();
Imgproc.Sobel(src, sobel1, -1, 1, 0, 3, 1, 0, Core.BORDER_DEFAULT);
System.out.println("--------------scale=1,delta=0---------------");
for (int row = 0; row < sobel1.rows(); row++) {
for (int col = 0; col < sobel1.cols(); col++) {
System.out.print((int)sobel1.get(row, col)[0] + " ");
}
System.out.println();
}
Mat sobel2 = new Mat();
Imgproc.Sobel(src, sobel2, -1, 1, 0, 3, 2, 3, Core.BORDER_DEFAULT);
System.out.println("--------------scale=2,delta=3---------------");
for (int row = 0; row < sobel2.rows(); row++) {
for (int col = 0; col < sobel2.cols(); col++) {
System.out.print((int)sobel2.get(row, col)[0] + " ");
}
System.out.println();
}
}
/**
* 灰度化
* @param mat 原圖片 (三通道)
* @return 灰度化圖片(單通道)
*/
public Mat gray(Mat mat) {
Mat grayMat = new Mat(mat.size(), CvType.CV_8UC1);
for (int row = 0; row < mat.rows(); row++) {
for (int col = 0; col < mat.cols(); col++){
double b = mat.get(row, col)[0];
double g = mat.get(row, col)[1];
double r = mat.get(row, col)[2];
double gray = r * 0.3 + g * 0.59 + b * 0.11;
grayMat.put(row, col, gray);
}
}
return grayMat;
}
/**
* 中值濾波
* @param mat 原圖片
* @param ksize 內核尺寸
* @return
*/
public Mat medianBlur(Mat mat, int ksize) {
int extendSize = ksize / 2;
//邊緣補全
Mat copyMakeBorder = new Mat(mat.size(), mat.type());
Core.copyMakeBorder(mat, copyMakeBorder, extendSize, extendSize, extendSize, extendSize, Core.BORDER_REPLICATE );
Mat medianBlurMat = new Mat(mat.size(), mat.type());
int x = 0;
int y = 0;
for (int row = extendSize; row < copyMakeBorder.rows() - extendSize; row++) {
for (int col = extendSize; col < copyMakeBorder.cols() - extendSize; col++) {
double[] color = new double[mat.channels()];
for (int channel = 0; channel < mat.channels(); channel++) { //每個通道進行濾波
double[] pixArr = new double[ksize * ksize];
int count = 0;
for (int i = extendSize * -1; i <= extendSize; i++) {
for (int j = extendSize * -1; j <= extendSize; j++) {
pixArr[count++] = copyMakeBorder.get(row + i, col + j)[channel];
}
}
Arrays.sort(pixArr); //排序
if ((count & 1) == 0) { //偶數
color[channel] = (pixArr[pixArr.length / 2] + pixArr[pixArr.length / 2 - 1]) / 2; //選取中間兩點的平均值
}
else { //奇數
color[channel] = pixArr[pixArr.length / 2]; //選取中位數
}
}
medianBlurMat.put(y, x, color);
x++;
}
y++;
x =0;
}
return medianBlurMat;
}
/**
* 二值化
* @param mat 原圖片
* @param thresh 閾值
* @param maxval 最大閾值
* @return 二值化圖片
*/
public Mat threshold(Mat mat, int thresh, int maxval) {
Mat threshold = new Mat(mat.size(), mat.type());
int[] tab = new int[256];
for (int i = 0 ; i < 256; i++) {
tab[i] = i <= thresh ? 0 : maxval;
}
for (int row = 0; row < mat.rows(); row++) {
for (int col = 0; col < mat.cols(); col++) {
double[] color = mat.get(row, col);
for (int channel = 0; channel < mat.channels(); channel++) {
color[channel] = tab[(int)color[channel]];
}
threshold.put(row, col, color);
}
}
return threshold;
}
/**
* 大津算法
* 默認爲白底黑字,及白色爲背景,黑色爲前景
* @param mat 灰度圖
* @return 閾值
*/
public int otsu(Mat mat) {
int GrayScale = 256; //單通道圖像總灰度256級
int[] pixCount = new int[256]; //每個灰度值所佔像素個數
double[] pixPro = new double[256]; //每個灰度值所佔總像素比例
int pixSum = mat.cols() * mat.rows();//圖像總像素點
double w0 = 0; //前景像素點佔整幅圖像的比例
double w1 = 0; //背景景像素點佔整幅圖像的比例
double u0tmp;
double u1tmp;
double u0; //w0平均灰度
double u1; //w1平均灰度
double deltaTmp;
double deltaMax = 0;
int th = 0;
for (int row = 0; row < mat.rows(); row++) {
for (int col = 0; col < mat.cols(); col++) {
pixCount[(int) mat.get(row, col)[0]]++; //統計每個灰度級中像素的個數
}
}
for (int i = 0; i < GrayScale; i++) {
pixPro[i] = pixCount[i] * 1.0 / pixSum; //計算每個灰度級的像素數目佔整幅圖像的比例
}
for (int i = 0; i < GrayScale; i++) { //遍歷所有從0到255灰度級的閾值分割條件,測試哪一個的類間方差最大
w0 = w1 = u0tmp = u1tmp = u0 = u1 = deltaTmp = 0;
for (int j = 0; j < GrayScale; j++) {
if (j <= i) { //前景
w0 += pixPro[j];
u0tmp += j * pixPro[j];
} else { //背景
w1 += pixPro[j];
u1tmp += j * pixPro[j];
}
}
u0 = u0tmp / w0;
u1 = u1tmp / w1;
deltaTmp = (w0 * w1 * Math.pow((u0 - u1), 2)); //類間方差公式 g = w0 * w1 * (u0 - u1) ^ 2
if (deltaTmp > deltaMax) {
deltaMax = deltaTmp;
th = i;
}
}
return th;
}
/**
* 積分圖像的自適應閾值:
* https://blog.csdn.net/diana_z/article/details/81089198
* @param mat 灰度圖
* @return
*/
public Mat integraImageThreshold(Mat mat) {
int s = mat.cols() >> 3;
int t = 15; //如果當前像素的值小於該平均值的t%,則將其設置爲黑色,否則將其設置爲白色
long[][] temp = new long[mat.rows()][mat.cols()];
Mat dst = new Mat(mat.size(), CvType.CV_8UC1, new Scalar(255));
for (int row = 0; row < mat.rows(); row++) {
long sum = 0;
for (int col = 0; col < mat.cols(); col++) {
sum += mat.get(row, col)[0];
if (row == 0) {
temp[row][col] = sum;
}
else {
temp[row][col] = temp[row - 1][col] + sum;
}
}
}
for (int row = 0; row < mat.rows(); row++) {
for (int col = 0; col < mat.cols(); col++) {
int y1 = row - s / 2 - 1 >= 0 ? row - s / 2 - 1 : 0;
int y2 = row + s / 2 < mat.rows() ? row + s / 2 : mat.rows() - 1;
int x1 = col - s / 2 - 1>= 0 ? col - s / 2 - 1 : 0;
int x2 = col + s / 2 < mat.cols() ? col + s / 2 : mat.cols() - 1;
int count = (x2 - x1 + 1) * (y2 - y1 + 1);
long sum = temp[y2][x2] - temp[y1][x2] - temp[y2][x1] + temp[y1][x1];
if (mat.get(row, col)[0] * count <= sum * (100 - t) / 100) {
dst.put(row, col, 0);
}
}
}
return dst;
}
/**
* 積分圖像的自適應閾值原型:
* https://blog.csdn.net/diana_z/article/details/81089198
* @param mat 灰度圖
* @return
*/
public Mat integraImageThresholdTemplate(Mat mat) {
int s = mat.cols() >> 3;
int t = 15; //如果當前像素的值小於該平均值的t%,則將其設置爲黑色,否則將其設置爲白色
Mat dst = new Mat(mat.size(), CvType.CV_8UC1, new Scalar(255));
for (int row = 0; row < mat.rows(); row++) {
for (int col = 0; col < mat.cols(); col++) {
int y1 = row - s / 2 >= 0 ? row - s / 2 : 0;
int y2 = row + s / 2 < mat.rows() ? row + s / 2 : mat.rows() - 1;
int x1 = col - s / 2 >= 0 ? col - s / 2 : 0;
int x2 = col + s / 2 < mat.cols() ? col + s / 2 : mat.cols() - 1;
int sum = 0;
for (int y = y1; y <= y2; y++) {
for (int x = x1; x <= x2; x++) {
sum += mat.get(y, x)[0];
}
}
int count = (x2 - x1) * (y2 - y1);
if (mat.get(row, col)[0] * count <= sum * (100 - t) / 100) {
dst.put(row, col, 0);
}
}
}
return dst;
}
/**
* wallner算法
* https://blog.csdn.net/lien0906/article/details/37557717#commentBox
* https://blog.csdn.net/u012198575/article/details/81147442
* @param mat 灰度圖
* @return
*/
public Mat wallner(Mat mat) {
Mat dst = new Mat(mat.size(), CvType.CV_8UC1, new Scalar(255));
int t = 15;
int s = mat.cols() >> 3;
int S = 9;
int power2S = 1 << S; //加速因子
int factor = power2S * (100 - t) / (100 * s);
int gn = 127 * s; // g(n)初始值
int q = power2S - power2S / s;
int pn, hn;
int[] prev_gn = new int[mat.cols()];
for (int col = 0; col < mat.cols(); col++) {
prev_gn[col] = gn;
}
for (int row = 0; row < mat.rows(); row++) {
for (int col = 0; col < mat.cols(); col++) {
pn = (int)mat.get(row, col)[0];
gn = ((gn * q) >> S) + pn;
hn = (gn + prev_gn[col]) >> 1;
prev_gn[col] = gn;
if (pn < (hn * factor) >> S) {
dst.put(row, col, 0);
}
}
row++;
if (row >= mat.rows()) { break; }
for (int col = mat.cols() - 1; col >= 0; col--) {
pn = (int)mat.get(row, col)[0];
gn = ((gn * q) >> S) + pn;
hn = (gn + prev_gn[col]) >> 1;
prev_gn[col] = gn;
if (pn < (hn * factor) >> S) {
dst.put(row, col, 0);
}
}
}
return dst;
}
/*******************************************細化算法-start********************************************/
/**
* 細化算法:
* https://www.cnblogs.com/xiaotie/archive/2010/08/12/1797760.html
* http://cgm.cs.mcgill.ca/~godfried/teaching/projects97/azar/skeleton.html
* p8 p1 p2
* p7 p0 p3
* p6 p5 p4
* @param mat
*/
public Mat hilditch(Mat mat) {
Mat dst = mat.clone();
boolean isEnd = true;
while (isEnd) {
Mat temp = new Mat();
dst.copyTo(temp);
isEnd = false;
for (int row = 0; row < mat.rows(); row++) {
for (int col = 0; col < mat.cols(); col++) {
//1、必須爲前背景
if (temp.get(row, col)[0] != frontBackground) {
continue;
}
//獲得8領域
double[] neighbors = getNeighbors(temp, row, col);
//2、 2 <= 獲得非零鄰居的數量 <= 6
int notZeroNum = getNeighborsNotZeroNum(neighbors);
if (notZeroNum < 2 || notZeroNum > 6) {
continue;
}
//3、0-1模式數量要爲1
if (getZeroAndOneModelCount(neighbors) != 1) {
continue;
}
//4、p1*p3*p7=0 || p1的0-1模型個數!=1
if (neighbors[1] == 0 || neighbors[3] == 0 || neighbors[7] == 0
|| getZeroAndOneModelCount(getNeighbors(temp, row - 1, col)) != 1) {
//5、p1*p3*p5=0 || p3的0-1模型個數!=1
if (neighbors[1] == 0 || neighbors[3] == 0 || neighbors[5] == 0
|| getZeroAndOneModelCount(getNeighbors(temp, row, col + 1)) != 1) {
//符合以上條件的像素點設爲後背景色
dst.put(row, col, backBackground);
isEnd = true;
}
}
}
}
}
return dst;
}
/**
* zhang細化算法:
* https://www.cnblogs.com/mikewolf2002/p/3321732.html
* @param mat
* @return
*/
public Mat zhangThinning(Mat mat) {
Mat dst = mat.clone();
boolean isEnd = true;
while (isEnd) {
isEnd = false;
Mat temp = new Mat();
dst.copyTo(temp);
for (int row = 0; row < mat.rows(); row++) {
for (int col = 0; col < mat.cols(); col++) {
//1、必須爲前背景
if (temp.get(row, col)[0] != frontBackground) {
continue;
}
//獲得8領域
double[] neighbors = getNeighbors(temp, row, col);
//2、 2 <= 獲得非零鄰居的數量 <= 6
int notZeroNum = getNeighborsNotZeroNum(neighbors);
if (notZeroNum < 2 || notZeroNum > 6) {
continue;
}
//3、0-1模式數量要爲1
if (getZeroAndOneModelCount(neighbors) != 1) {
continue;
}
//4、p1*p3*p5=0
if (neighbors[1] == 0 || neighbors[3] == 0 || neighbors[5] == 0) {
//5、p3*p5*p7=0
if (neighbors[3] == 0 || neighbors[5] == 0 || neighbors[7] == 0) {
//符合以上條件的像素點設爲後背景色
dst.put(row, col, backBackground);
isEnd = true;
}
}
}
}
dst.copyTo(temp);
for (int row = 0; row < mat.rows(); row++) {
for (int col = 0; col < mat.cols(); col++) {
//1、必須爲前背景
if (temp.get(row, col)[0] != frontBackground) {
continue;
}
//獲得8領域
double[] neighbors = getNeighbors(temp, row, col);
//2、 2 <= 獲得非零鄰居的數量 <= 6
int notZeroNum = getNeighborsNotZeroNum(neighbors);
if (notZeroNum < 2 || notZeroNum > 6) {
continue;
}
//3、0-1模式數量要爲1
if (getZeroAndOneModelCount(neighbors) != 1) {
continue;
}
//4、p1*p3*p7=0
if (neighbors[1] == 0 || neighbors[3] == 0 || neighbors[7] == 0) {
//5、p1*p5*p7=0
if (neighbors[1] == 0 || neighbors[5] == 0 || neighbors[7] == 0) {
//符合以上條件的像素點設爲後背景色
dst.put(row, col, backBackground);
isEnd = true;
}
}
}
}
}
return dst;
}
/**
* 獲得8領域
* p8 p1 p2
* p7 p0 p3
* p6 p5 p4
* @param mat 圖片Mat
* @param row 行數
* @param col 列數
* @return
*/
public double[] getNeighbors(Mat mat, int row, int col) {
double[] neighbors = new double[9];
if (row > 0 && row < mat.rows() && col > 0 && col < mat.cols()) {
neighbors[0] = mat.get(row, col)[0];
}
if (row - 1 > 0 && row - 1 < mat.rows() && col > 0 && col < mat.cols()) {
neighbors[1] = mat.get(row - 1, col)[0];
}
if (row - 1 > 0 && row - 1 < mat.rows() && col + 1 > 0 && col + 1 < mat.cols()) {
neighbors[2] = mat.get(row - 1, col + 1)[0];
}
if (row > 0 && row < mat.rows() && col + 1 > 0 && col + 1 < mat.cols()) {
neighbors[3] = mat.get(row, col + 1)[0];
}
if (row + 1 > 0 && row + 1 < mat.rows() && col + 1 > 0 && col + 1 < mat.cols()) {
neighbors[4] = mat.get(row + 1, col + 1)[0];
}
if (row + 1 > 0 && row + 1 < mat.rows() && col > 0 && col < mat.cols()) {
neighbors[5] = mat.get(row + 1, col)[0];
}
if (row + 1 > 0 && row + 1 < mat.rows() && col - 1 > 0 && col - 1 < mat.cols()) {
neighbors[6] = mat.get(row + 1, col - 1)[0];
}
if (row > 0 && row < mat.rows() && col - 1 > 0 && col - 1 < mat.cols()) {
neighbors[7] = mat.get(row, col - 1)[0];
}
if (row - 1 > 0 && row - 1 < mat.rows() && col - 1 > 0 && col - 1 < mat.cols()) {
neighbors[8] = mat.get(row - 1, col - 1)[0];
}
return neighbors;
}
/**
* 獲得前背景鄰居的數量
* p8 p1 p2
* p7 p0 p3
* p6 p5 p4
* @param neighbors
* @return
*/
public int getNeighborsNotZeroNum(double[] neighbors) {
int count = 0;
for (int i = 1; i < neighbors.length; i++) {
if (neighbors[i] == frontBackground) {
count++;
}
}
return count;
}
/**
* 按p1->p8的順序獲得0-1模型個數(後背景-前背景)
* p8 p1 p2
* p7 p0 p3
* p6 p5 p4
* @param neighbors
* @return
*/
public int getZeroAndOneModelCount(double[] neighbors) {
int count = 0;
if (neighbors[1] == backBackground && neighbors[2] == frontBackground) { count++; }
if (neighbors[2] == backBackground && neighbors[3] == frontBackground) { count++; }
if (neighbors[3] == backBackground && neighbors[4] == frontBackground) { count++; }
if (neighbors[4] == backBackground && neighbors[5] == frontBackground) { count++; }
if (neighbors[5] == backBackground && neighbors[6] == frontBackground) { count++; }
if (neighbors[6] == backBackground && neighbors[7] == frontBackground) { count++; }
if (neighbors[7] == backBackground && neighbors[8] == frontBackground) { count++; }
if (neighbors[8] == backBackground && neighbors[1] == frontBackground) { count++; }
return count;
}
/*******************************************細化算法-end********************************************/
/*******************************************連通塊-start********************************************/
/**
* 獲得連通塊
* @param mat 二值圖
* @param dst 聯通圖 開始像素爲全0, 結果爲聯通塊部分修改爲連通塊標號(1, 2...n)
* 前 前 背 前 1 1 0 2
* 前 前 背 前 1 1 0 2
* 前 前 背 前 ==> 1 1 0 2
* 前 前 背 前 1 1 0 2
* 前 前 背 前 1 1 0 2
* @return 連通塊數量
*/
public int getConnecteBlock(Mat mat, Mat dst) {
int connecteBlockNum = 0;
for (int row = 0; row < mat.rows(); row++) {
for (int col = 0; col < mat.cols(); col++) {
if (mat.get(row, col)[0] == frontBackground && dst.get(row, col)[0] == 0) {
dst.put(row, col, ++connecteBlockNum);
Stack<Point> stack = new Stack<Point>();
stack.push(new Point(col, row));
while (!stack.empty()) {
Point pix = stack.pop();
int pixRow = (int) pix.y;
int pixCol = (int) pix.x;
if (pixRow - 1 >= 0 && mat.get(pixRow - 1, pixCol)[0] == frontBackground && dst.get(pixRow - 1, pixCol)[0] == 0) { //up
stack.push(new Point(pixCol, pixRow - 1));
dst.put(pixRow - 1, pixCol, connecteBlockNum);
}
if (pixRow - 1 >= 0 && pixCol + 1 < mat.cols() && mat.get(pixRow - 1, pixCol + 1)[0] == frontBackground && dst.get(pixRow - 1, pixCol + 1)[0] == 0) { //right-up
stack.push(new Point(pixCol + 1, pixRow - 1));
dst.put(pixRow - 1, pixCol + 1, connecteBlockNum);
}
if (pixCol + 1 < mat.cols() && mat.get(pixRow, pixCol + 1)[0] == frontBackground && dst.get(pixRow, pixCol + 1)[0] == 0) { //right
stack.push(new Point(pixCol + 1, pixRow));
}
if (pixRow + 1 < mat.rows() && pixCol + 1 < mat.cols() && mat.get(pixRow + 1, pixCol + 1)[0] == frontBackground && dst.get(pixRow + 1, pixCol + 1)[0] == 0) { //right-down
stack.push(new Point(pixCol + 1, pixRow + 1));
dst.put(pixRow + 1, pixCol + 1, connecteBlockNum);
}
if (pixRow + 1 < mat.rows() && mat.get(pixRow + 1, pixCol)[0] == frontBackground && dst.get(pixRow + 1, pixCol)[0] == 0) { //down
stack.push(new Point(pixCol, pixRow + 1));
dst.put(pixRow + 1, pixCol, connecteBlockNum);
}
if (pixCol - 1 >= 0 && pixRow + 1 < mat.rows() && mat.get(pixRow + 1, pixCol - 1)[0] == frontBackground && dst.get(pixRow + 1, pixCol - 1)[0] == 0) { //left-down
stack.push(new Point(pixCol - 1, pixRow + 1));
dst.put(pixRow + 1, pixCol - 1, connecteBlockNum);
}
if (pixCol - 1 >= 0 && mat.get(pixRow, pixCol - 1)[0] == frontBackground && dst.get(pixRow, pixCol - 1)[0] == 0) { //left
stack.push(new Point(pixCol - 1, pixRow));
dst.put(pixRow, pixCol - 1, connecteBlockNum);
}
if (pixCol - 1 >= 0 && pixRow - 1 >= 0 && mat.get(pixRow - 1, pixCol - 1)[0] == frontBackground && dst.get(pixRow - 1, pixCol - 1)[0] == 0) { //left-up
stack.push(new Point(pixCol - 1, pixRow - 1));
dst.put(pixRow - 1, pixCol - 1, connecteBlockNum);
}
}
}
}
}
return connecteBlockNum;
}
/*******************************************連通塊-end********************************************/
}
java Opencv 部分代碼記錄
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.