java Opencv 部分代碼記錄

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********************************************/

}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章