|
|
|
@ -0,0 +1,453 @@
|
|
|
|
|
package com.yuxue.easypr.core;
|
|
|
|
|
|
|
|
|
|
import static com.yuxue.easypr.core.CoreFunc.getPlateType;
|
|
|
|
|
import static org.bytedeco.javacpp.opencv_core.CV_32F;
|
|
|
|
|
import static org.bytedeco.javacpp.opencv_core.countNonZero;
|
|
|
|
|
import static org.bytedeco.javacpp.opencv_imgproc.CV_CHAIN_APPROX_NONE;
|
|
|
|
|
import static org.bytedeco.javacpp.opencv_imgproc.CV_RETR_EXTERNAL;
|
|
|
|
|
import static org.bytedeco.javacpp.opencv_imgproc.CV_RGB2GRAY;
|
|
|
|
|
import static org.bytedeco.javacpp.opencv_imgproc.CV_THRESH_BINARY;
|
|
|
|
|
import static org.bytedeco.javacpp.opencv_imgproc.CV_THRESH_BINARY_INV;
|
|
|
|
|
import static org.bytedeco.javacpp.opencv_imgproc.CV_THRESH_OTSU;
|
|
|
|
|
import static org.bytedeco.javacpp.opencv_imgproc.INTER_LINEAR;
|
|
|
|
|
import static org.bytedeco.javacpp.opencv_imgproc.boundingRect;
|
|
|
|
|
import static org.bytedeco.javacpp.opencv_imgproc.cvtColor;
|
|
|
|
|
import static org.bytedeco.javacpp.opencv_imgproc.findContours;
|
|
|
|
|
import static org.bytedeco.javacpp.opencv_imgproc.resize;
|
|
|
|
|
import static org.bytedeco.javacpp.opencv_imgproc.threshold;
|
|
|
|
|
import static org.bytedeco.javacpp.opencv_imgproc.warpAffine;
|
|
|
|
|
|
|
|
|
|
import java.util.Vector;
|
|
|
|
|
|
|
|
|
|
import org.bytedeco.javacpp.opencv_core;
|
|
|
|
|
import org.bytedeco.javacpp.opencv_core.Mat;
|
|
|
|
|
import org.bytedeco.javacpp.opencv_core.MatVector;
|
|
|
|
|
import org.bytedeco.javacpp.opencv_core.Rect;
|
|
|
|
|
import org.bytedeco.javacpp.opencv_core.Scalar;
|
|
|
|
|
import org.bytedeco.javacpp.opencv_core.Size;
|
|
|
|
|
import org.bytedeco.javacpp.opencv_imgcodecs;
|
|
|
|
|
|
|
|
|
|
import com.yuxue.enumtype.PlateColor;
|
|
|
|
|
import com.yuxue.util.Convert;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 字符分割
|
|
|
|
|
* @author yuxue
|
|
|
|
|
* @date 2020-04-28 09:45
|
|
|
|
|
*/
|
|
|
|
|
public class CharsSegment {
|
|
|
|
|
|
|
|
|
|
// preprocessChar所用常量
|
|
|
|
|
final static int CHAR_SIZE = 20;
|
|
|
|
|
final static int HORIZONTAL = 1;
|
|
|
|
|
final static int VERTICAL = 0;
|
|
|
|
|
|
|
|
|
|
final static int DEFAULT_LIUDING_SIZE = 7;
|
|
|
|
|
final static int DEFAULT_MAT_WIDTH = 136;
|
|
|
|
|
|
|
|
|
|
final static int DEFAULT_COLORTHRESHOLD = 150;
|
|
|
|
|
final static float DEFAULT_BLUEPERCEMT = 0.3f;
|
|
|
|
|
final static float DEFAULT_WHITEPERCEMT = 0.1f;
|
|
|
|
|
|
|
|
|
|
private int liuDingSize = DEFAULT_LIUDING_SIZE;
|
|
|
|
|
private int theMatWidth = DEFAULT_MAT_WIDTH;
|
|
|
|
|
|
|
|
|
|
private int colorThreshold = DEFAULT_COLORTHRESHOLD;
|
|
|
|
|
|
|
|
|
|
private float bluePercent = DEFAULT_BLUEPERCEMT;
|
|
|
|
|
private float whitePercent = DEFAULT_WHITEPERCEMT;
|
|
|
|
|
|
|
|
|
|
private boolean isDebug = true;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 字符分割
|
|
|
|
|
*
|
|
|
|
|
* @param input
|
|
|
|
|
* @param resultVec
|
|
|
|
|
* @return <ul>
|
|
|
|
|
* <li>more than zero: the number of chars;
|
|
|
|
|
* <li>-3: null;
|
|
|
|
|
* </ul>
|
|
|
|
|
*/
|
|
|
|
|
public int charsSegment(final Mat input, Vector<Mat> resultVec, String tempPath) {
|
|
|
|
|
if (input.data().isNull()) {
|
|
|
|
|
return -3;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 判断车牌颜色以此确认threshold方法
|
|
|
|
|
Mat img_threshold = new Mat();
|
|
|
|
|
|
|
|
|
|
Mat input_grey = new Mat();
|
|
|
|
|
cvtColor(input, input_grey, CV_RGB2GRAY);
|
|
|
|
|
|
|
|
|
|
int w = input.cols();
|
|
|
|
|
int h = input.rows();
|
|
|
|
|
Mat tmpMat = new Mat(input, new Rect((int) (w * 0.1), (int) (h * 0.1), (int) (w * 0.8), (int) (h * 0.8)));
|
|
|
|
|
|
|
|
|
|
PlateColor color= getPlateType(tmpMat, true);
|
|
|
|
|
switch (color) {
|
|
|
|
|
case BLUE:
|
|
|
|
|
threshold(input_grey, img_threshold, 10, 255, CV_THRESH_OTSU + CV_THRESH_BINARY);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case YELLOW:
|
|
|
|
|
threshold(input_grey, img_threshold, 10, 255, CV_THRESH_OTSU + CV_THRESH_BINARY_INV);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case GREEN:
|
|
|
|
|
threshold(input_grey, img_threshold, 10, 255, CV_THRESH_OTSU + CV_THRESH_BINARY_INV);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
return -3;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (this.isDebug) {
|
|
|
|
|
opencv_imgcodecs.imwrite(tempPath + "debug_char_threshold.jpg", img_threshold);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 去除车牌上方的柳钉以及下方的横线等干扰 //会导致虚拟机崩溃
|
|
|
|
|
// clearLiuDing(img_threshold);
|
|
|
|
|
/*if (this.isDebug) {
|
|
|
|
|
String str = tempPath + "debug_char_clearLiuDing.jpg";
|
|
|
|
|
opencv_imgcodecs.imwrite(str, img_threshold);
|
|
|
|
|
}*/
|
|
|
|
|
|
|
|
|
|
// 找轮廓
|
|
|
|
|
Mat img_contours = new Mat();
|
|
|
|
|
img_threshold.copyTo(img_contours);
|
|
|
|
|
|
|
|
|
|
MatVector contours = new MatVector();
|
|
|
|
|
|
|
|
|
|
findContours(img_contours, contours, // a vector of contours
|
|
|
|
|
CV_RETR_EXTERNAL, // retrieve the external contours
|
|
|
|
|
CV_CHAIN_APPROX_NONE); // all pixels of each contours
|
|
|
|
|
|
|
|
|
|
// Remove patch that are no inside limits of aspect ratio and area.
|
|
|
|
|
// 将不符合特定尺寸的图块排除出去
|
|
|
|
|
Vector<Rect> vecRect = new Vector<Rect>();
|
|
|
|
|
for (int i = 0; i < contours.size(); ++i) {
|
|
|
|
|
Rect mr = boundingRect(contours.get(i));
|
|
|
|
|
Mat contour = new Mat(img_threshold, mr);
|
|
|
|
|
|
|
|
|
|
if (this.isDebug) {
|
|
|
|
|
String str = tempPath + "debug_char_contour"+i+".jpg";
|
|
|
|
|
opencv_imgcodecs.imwrite(str, contour);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (verifySizes(contour)) { // 将不符合特定尺寸的图块排除出去
|
|
|
|
|
vecRect.add(mr);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (vecRect.size() == 0) {
|
|
|
|
|
return -3;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Vector<Rect> sortedRect = new Vector<Rect>();
|
|
|
|
|
// 对符合尺寸的图块按照从左到右进行排序
|
|
|
|
|
SortRect(vecRect, sortedRect);
|
|
|
|
|
|
|
|
|
|
// 获得指示城市的特定Rect,如苏A的"A"
|
|
|
|
|
int specIndex = GetSpecificRect(sortedRect, color);
|
|
|
|
|
|
|
|
|
|
if (this.isDebug) {
|
|
|
|
|
if (specIndex < sortedRect.size()) {
|
|
|
|
|
Mat specMat = new Mat(img_threshold, sortedRect.get(specIndex));
|
|
|
|
|
String str = tempPath + "debug_specMat.jpg";
|
|
|
|
|
opencv_imgcodecs.imwrite(str, specMat);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 根据特定Rect向左反推出中文字符
|
|
|
|
|
// 这样做的主要原因是根据findContours方法很难捕捉到中文字符的准确Rect,因此仅能
|
|
|
|
|
// 通过特定算法来指定
|
|
|
|
|
Rect chineseRect = new Rect();
|
|
|
|
|
if (specIndex < sortedRect.size()) {
|
|
|
|
|
chineseRect = GetChineseRect(sortedRect.get(specIndex));
|
|
|
|
|
} else {
|
|
|
|
|
return -3;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (this.isDebug) {
|
|
|
|
|
Mat chineseMat = new Mat(img_threshold, chineseRect);
|
|
|
|
|
String str = tempPath + "debug_chineseMat.jpg";
|
|
|
|
|
opencv_imgcodecs.imwrite(str, chineseMat);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 新建一个全新的排序Rect
|
|
|
|
|
// 将中文字符Rect第一个加进来,因为它肯定是最左边的
|
|
|
|
|
// 其余的Rect只按照顺序去6个,车牌只可能是7个字符!这样可以避免阴影导致的“1”字符
|
|
|
|
|
Vector<Rect> newSortedRect = new Vector<Rect>();
|
|
|
|
|
newSortedRect.add(chineseRect);
|
|
|
|
|
RebuildRect(sortedRect, newSortedRect, specIndex, color);
|
|
|
|
|
|
|
|
|
|
if (newSortedRect.size() == 0) {
|
|
|
|
|
return -3;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < newSortedRect.size(); i++) {
|
|
|
|
|
Rect mr = newSortedRect.get(i);
|
|
|
|
|
Mat auxRoi = new Mat(img_threshold, mr);
|
|
|
|
|
|
|
|
|
|
auxRoi = preprocessChar(auxRoi);
|
|
|
|
|
if (this.isDebug) {
|
|
|
|
|
String str = tempPath + "debug_char_auxRoi_" + Integer.valueOf(i).toString() + ".jpg";
|
|
|
|
|
opencv_imgcodecs.imwrite(str, auxRoi);
|
|
|
|
|
}
|
|
|
|
|
resultVec.add(auxRoi);
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 字符尺寸验证;去掉尺寸不符合的图块
|
|
|
|
|
* @param r
|
|
|
|
|
* @return
|
|
|
|
|
*/
|
|
|
|
|
public static Boolean verifySizes(Mat r) {
|
|
|
|
|
float aspect = 45.0f / 90.0f;
|
|
|
|
|
float charAspect = (float) r.cols() / (float) r.rows();
|
|
|
|
|
float error = 0.7f;
|
|
|
|
|
float minHeight = 10f;
|
|
|
|
|
float maxHeight = 35f;
|
|
|
|
|
// We have a different aspect ratio for number 1, and it can be ~0.2
|
|
|
|
|
float minAspect = 0.05f;
|
|
|
|
|
float maxAspect = aspect + aspect * error;
|
|
|
|
|
// area of pixels
|
|
|
|
|
float area = countNonZero(r);
|
|
|
|
|
// bb area
|
|
|
|
|
float bbArea = r.cols() * r.rows();
|
|
|
|
|
// % of pixel in area
|
|
|
|
|
float percPixels = area / bbArea;
|
|
|
|
|
|
|
|
|
|
return percPixels <= 1 && charAspect > minAspect && charAspect < maxAspect && r.rows() >= minHeight && r.rows() < maxHeight;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 字符预处理: 统一每个字符的大小
|
|
|
|
|
*
|
|
|
|
|
* @param in
|
|
|
|
|
* @return
|
|
|
|
|
*/
|
|
|
|
|
private Mat preprocessChar(Mat in) {
|
|
|
|
|
int h = in.rows();
|
|
|
|
|
int w = in.cols();
|
|
|
|
|
int charSize = CHAR_SIZE;
|
|
|
|
|
Mat transformMat = Mat.eye(2, 3, CV_32F).asMat();
|
|
|
|
|
int m = Math.max(w, h);
|
|
|
|
|
transformMat.ptr(0, 2).put(Convert.getBytes(((m - w) / 2f)));
|
|
|
|
|
transformMat.ptr(1, 2).put(Convert.getBytes((m - h) / 2f));
|
|
|
|
|
|
|
|
|
|
Mat warpImage = new Mat(m, m, in.type());
|
|
|
|
|
warpAffine(in, warpImage, transformMat, warpImage.size(), INTER_LINEAR, opencv_core.BORDER_CONSTANT, new Scalar(0));
|
|
|
|
|
|
|
|
|
|
Mat out = new Mat();
|
|
|
|
|
resize(warpImage, out, new Size(charSize, charSize));
|
|
|
|
|
|
|
|
|
|
return out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 去除车牌上方的钮钉
|
|
|
|
|
* <p>
|
|
|
|
|
* 计算每行元素的阶跃数,如果小于X认为是柳丁,将此行全部填0(涂黑), X可根据实际调整
|
|
|
|
|
*
|
|
|
|
|
* @param img
|
|
|
|
|
* @return
|
|
|
|
|
*/
|
|
|
|
|
private Mat clearLiuDing(Mat img) {
|
|
|
|
|
final int x = this.liuDingSize;
|
|
|
|
|
|
|
|
|
|
Mat jump = Mat.zeros(1, img.rows(), CV_32F).asMat();
|
|
|
|
|
for (int i = 0; i < img.rows(); i++) {
|
|
|
|
|
int jumpCount = 0;
|
|
|
|
|
for (int j = 0; j < img.cols() - 1; j++) {
|
|
|
|
|
if (img.ptr(i, j).get() != img.ptr(i, j + 1).get())
|
|
|
|
|
jumpCount++;
|
|
|
|
|
}
|
|
|
|
|
jump.ptr(i).put(Convert.getBytes((float) jumpCount));
|
|
|
|
|
}
|
|
|
|
|
for (int i = 0; i < img.rows(); i++) {
|
|
|
|
|
if (Convert.toFloat(jump.ptr(i)) <= x) {
|
|
|
|
|
for (int j = 0; j < img.cols(); j++) {
|
|
|
|
|
img.ptr(i, j).put((byte) 0);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return img;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 根据特殊车牌来构造猜测中文字符的位置和大小
|
|
|
|
|
*
|
|
|
|
|
* @param rectSpe
|
|
|
|
|
* @return
|
|
|
|
|
*/
|
|
|
|
|
private Rect GetChineseRect(final Rect rectSpe) {
|
|
|
|
|
int height = rectSpe.height();
|
|
|
|
|
float newwidth = rectSpe.width() * 1.15f;
|
|
|
|
|
int x = rectSpe.x();
|
|
|
|
|
int y = rectSpe.y();
|
|
|
|
|
|
|
|
|
|
int newx = x - (int) (newwidth * 1.15);
|
|
|
|
|
newx = Math.max(newx, 0);
|
|
|
|
|
Rect a = new Rect(newx, y, (int) newwidth, height);
|
|
|
|
|
return a;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 找出指示城市的字符的Rect,例如苏A7003X,就是A的位置
|
|
|
|
|
* 之所以选择城市的字符位置,是因为该位置不管什么字母,占用的宽度跟高度的差不多,能大大提高位置的准确性
|
|
|
|
|
* @param vecRect
|
|
|
|
|
* @return
|
|
|
|
|
*/
|
|
|
|
|
private int GetSpecificRect(final Vector<Rect> vecRect, PlateColor color) {
|
|
|
|
|
Vector<Integer> xpositions = new Vector<Integer>();
|
|
|
|
|
int maxHeight = 0;
|
|
|
|
|
int maxWidth = 0;
|
|
|
|
|
for (int i = 0; i < vecRect.size(); i++) {
|
|
|
|
|
xpositions.add(vecRect.get(i).x());
|
|
|
|
|
|
|
|
|
|
if (vecRect.get(i).height() > maxHeight) {
|
|
|
|
|
maxHeight = vecRect.get(i).height();
|
|
|
|
|
}
|
|
|
|
|
if (vecRect.get(i).width() > maxWidth) {
|
|
|
|
|
maxWidth = vecRect.get(i).width();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int specIndex = 0;
|
|
|
|
|
for (int i = 0; i < vecRect.size(); i++) {
|
|
|
|
|
Rect mr = vecRect.get(i);
|
|
|
|
|
int midx = mr.x() + mr.width() / 2;
|
|
|
|
|
|
|
|
|
|
if(PlateColor.GREEN.equals(color)) {
|
|
|
|
|
if ((mr.width() > maxWidth * 0.8 || mr.height() > maxHeight * 0.8)
|
|
|
|
|
&& (midx < this.theMatWidth * 2 / 8 && midx > this.theMatWidth / 8)) {
|
|
|
|
|
specIndex = i;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
// 如果一个字符有一定的大小,并且在整个车牌的1/7到2/7之间,则是我们要找的特殊车牌
|
|
|
|
|
if ((mr.width() > maxWidth * 0.8 || mr.height() > maxHeight * 0.8)
|
|
|
|
|
&& (midx < this.theMatWidth * 2 / 7 && midx > this.theMatWidth / 7)) {
|
|
|
|
|
specIndex = i;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return specIndex;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 这个函数做两个事情
|
|
|
|
|
* <ul>
|
|
|
|
|
* <li>把特殊字符Rect左边的全部Rect去掉,后面再重建中文字符的位置;
|
|
|
|
|
* <li>从特殊字符Rect开始,依次选择6个Rect,多余的舍去。
|
|
|
|
|
* <ul>
|
|
|
|
|
*
|
|
|
|
|
* @param vecRect
|
|
|
|
|
* @param outRect
|
|
|
|
|
* @param specIndex
|
|
|
|
|
* @return
|
|
|
|
|
*/
|
|
|
|
|
private int RebuildRect(final Vector<Rect> vecRect, Vector<Rect> outRect, int specIndex, PlateColor color) {
|
|
|
|
|
// 最大只能有7个Rect,减去中文的就只有6个Rect
|
|
|
|
|
int count = 6;
|
|
|
|
|
if(PlateColor.GREEN.equals(color)) {
|
|
|
|
|
count = 7; // 绿牌要多一个
|
|
|
|
|
}
|
|
|
|
|
for (int i = 0; i < vecRect.size(); i++) {
|
|
|
|
|
// 将特殊字符左边的Rect去掉,这个可能会去掉中文Rect,不过没关系,我们后面会重建。
|
|
|
|
|
if (i < specIndex)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
outRect.add(vecRect.get(i));
|
|
|
|
|
if (--count == 0)
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 将Rect按位置从左到右进行排序
|
|
|
|
|
*
|
|
|
|
|
* @param vecRect
|
|
|
|
|
* @param out
|
|
|
|
|
* @return
|
|
|
|
|
*/
|
|
|
|
|
public static void SortRect(final Vector<Rect> vecRect, Vector<Rect> out) {
|
|
|
|
|
Vector<Integer> orderIndex = new Vector<Integer>();
|
|
|
|
|
Vector<Integer> xpositions = new Vector<Integer>();
|
|
|
|
|
for (int i = 0; i < vecRect.size(); ++i) {
|
|
|
|
|
orderIndex.add(i);
|
|
|
|
|
xpositions.add(vecRect.get(i).x());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
float min = xpositions.get(0);
|
|
|
|
|
int minIdx;
|
|
|
|
|
for (int i = 0; i < xpositions.size(); ++i) {
|
|
|
|
|
min = xpositions.get(i);
|
|
|
|
|
minIdx = i;
|
|
|
|
|
for (int j = i; j < xpositions.size(); ++j) {
|
|
|
|
|
if (xpositions.get(j) < min) {
|
|
|
|
|
min = xpositions.get(j);
|
|
|
|
|
minIdx = j;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
int aux_i = orderIndex.get(i);
|
|
|
|
|
int aux_min = orderIndex.get(minIdx);
|
|
|
|
|
orderIndex.remove(i);
|
|
|
|
|
orderIndex.insertElementAt(aux_min, i);
|
|
|
|
|
orderIndex.remove(minIdx);
|
|
|
|
|
orderIndex.insertElementAt(aux_i, minIdx);
|
|
|
|
|
|
|
|
|
|
float aux_xi = xpositions.get(i);
|
|
|
|
|
float aux_xmin = xpositions.get(minIdx);
|
|
|
|
|
xpositions.remove(i);
|
|
|
|
|
xpositions.insertElementAt((int) aux_xmin, i);
|
|
|
|
|
xpositions.remove(minIdx);
|
|
|
|
|
xpositions.insertElementAt((int) aux_xi, minIdx);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < orderIndex.size(); i++)
|
|
|
|
|
out.add(vecRect.get(orderIndex.get(i)));
|
|
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void setLiuDingSize(int param) {
|
|
|
|
|
this.liuDingSize = param;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void setColorThreshold(int param) {
|
|
|
|
|
this.colorThreshold = param;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void setBluePercent(float param) {
|
|
|
|
|
this.bluePercent = param;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public final float getBluePercent() {
|
|
|
|
|
return this.bluePercent;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void setWhitePercent(float param) {
|
|
|
|
|
this.whitePercent = param;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public final float getWhitePercent() {
|
|
|
|
|
return this.whitePercent;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public boolean getDebug() {
|
|
|
|
|
return this.isDebug;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void setDebug(boolean isDebug) {
|
|
|
|
|
this.isDebug = isDebug;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|