private void plotchart(String s[], float[] f1) {
chart.setBackgroundColor(Color.rgb(63, 81, 181));
chart.setDescription("");
// enable touch gestures
chart.setTouchEnabled(true);
chart.setScaleEnabled(false);
chart.setPinchZoom(false);
chart.setGridBackgroundColor(Color.rgb(30, 46, 141));
chart.setDrawGridBackground(false);
chart.getAxisRight().setEnabled(false);
chart.setDrawMarkerViews(true);
chart.setDragEnabled(true);
chart.setViewPortOffsets(0f, 0f, 0f, 0f);
// chart.setVisibleXRangeMaximum(4);
chart.getLegend().setEnabled(false);
XAxis x = chart.getXAxis();
x.setEnabled(true);
x.setDrawGridLines(false);
x.setPosition(XAxis.XAxisPosition.BOTTOM_INSIDE);
x.setTextColor(Color.rgb(128, 128, 255));
x.isDrawLabelsEnabled();
x.setAxisLineColor(Color.BLACK);
x.setSpaceBetweenLabels(3);
x.setAvoidFirstLastClipping(true);
YAxis y = chart.getAxisLeft();
y.setAxisLineColor(Color.BLACK);
y.setTextColor(Color.BLACK);
y.setEnabled(true);
y.setAxisLineColor(Color.rgb(128, 128, 255));
y.setPosition(YAxis.YAxisLabelPosition.INSIDE_CHART);
y.setDrawGridLines(false);
y.setLabelCount(5,true);
y.setGridColor(Color.rgb(128, 128, 255));
y.setDrawZeroLine(false);
y.setDrawLimitLinesBehindData(true);
y.setDrawGridLines(true);
y.setDrawLimitLinesBehindData(true);
y.setTextColor(Color.rgb(128, 128, 255));
// chart.setExtraOffsets(20f,2f,20f,2f);
ArrayList<com.github.mikephil.charting.data.Entry> entries= new ArrayList<>();
for (int i = 0; i < f1.length; i++) {
// Log.i("f1",f1[i]+"");
entries.add(new com.github.mikephil.charting.data.Entry(f1[i], i));
}
MymarkerView mv =new MymarkerView(this,R.layout.custom_marker_view);
chart.setMarkerView(mv);
LineDataSet dataset = new LineDataSet(entries, "");
dataset.isDrawCirclesEnabled();
dataset.setCircleRadius(0f);
dataset.setDrawFilled(true);
dataset.setFillColor(Color.rgb(0, 0, 0));
dataset.setLineWidth(0.2f);
dataset.setValueTextSize(0f);
dataset.setColor(Color.rgb(0, 0, 0));
// chart.setVisibleXRange(1, 5);
// chart.setVisibleYRangeMaximum(5, YAxis.AxisDependency.LEFT);
// chart.setClickable(true);
chart.invalidate();
ArrayList<String> time = new ArrayList<String>();
for (int i = 0; i < s.length; i++) {
time.add(i, s[i]);
}
LineData data = new LineData(time, dataset);
chart.setData(data);
}
I Have fixed the Y-Axis values by using setLabelCount() command.But how to fix the number of X-Axis values in my chart..For the above code i have the following chart formed.Chart1 Chart2..These are the two charts formed for different arguments given to the plotchart(String [],float[]) method.In all these charts I am facing the problem that it is not showing the fixed number of x-axis values.Somewhere it is showing 9 values and somewhere it is showing 7 values..And one more issue I am facing is that my first and the last Y-Axis values are getting hidden in my display.
setLabelCount() property is there for xAxis too.
XAxis xAxis = lineChart.getXAxis();
xAxis.setLabelCount(5);
If I understood your question well, it seems that you want to use the setVisibleXRangeMaximum method.
Restraining what's visible
setVisibleXRangeMaximum(float maxXRange): Sets the size of the area (range on the x-axis) that should be maximum visible at once. If this is e.g. set to 10, no more than 10 values on the x-axis can be viewed at once without scrolling.
This method is documented in the Viewport part of the MPAndroidChart tutorial, which is linked here.
Of course, I think you will have to use the setVisibleXRangeMinimum if you want to fix an upper and a lower bounds for the number of X values.
To set the X-axis values to a fixed number (10 for example):
float minXRange = 10;
float maxXRange = 10;
chart.setVisibleXRange(minXRange, maxXRange);
If the gap between your values on the X-axis is constant, it will also fix the scale for the X-axis.
To auto set the scale on the Y-Axis with 10% spacing according to your maximum and minimum values:
YAxis y1 = chart.getAxisLeft();
float percent = 10;
y1.setSpaceTop(percent);
y1.setSpaceBottom(percent);
You can use IndexAxisValueFormatter method of library
ArrayList<String> xAxisLabel = new ArrayList<>();
xAxisLabel.add("Mon");
xAxisLabel.add("Tue");
xAxisLabel.add("Wed");
xAxisLabel.add("Thu");
xAxisLabel.add("Fri");
xAxisLabel.add("Sat");
xAxisLabel.add("Sun");
chart.getXAxis().setValueFormatter(new IndexAxisValueFormatter(texts));
Current library version v3.0.3
Set X-Axis values
xAxis.setValueFormatter(new IAxisValueFormatter() {
#Override
public String getFormattedValue(float value, AxisBase axis) {
return (int) value + "";
}
});
I'm using org.jzy3d package (v 0.9) to create Surface plots.
Here's my code:
int stepsX = 6;
Range rX = new Range(1,6);
int stepsY = 7;
Range rY = new Range(0,6);
Mapper mapper = new Mapper(){
#Override
public double f(double x, double y) {
return //My function to get z;
}
};
org.jzy3d.plot3d.primitives.Shape surface = Builder.buildOrthonormal(new OrthonormalGrid(rX, stepsX, rY, stepsY), mapper);
surface.setColorMapper(new ColorMapper(new ColorMapRainbow(), surface.getBounds().getZmin(), surface.getBounds().getZmax(), new org.jzy3d.colors.Color(1, 1, 1, .5f)));
surface.setFaceDisplayed(true);
surface.setWireframeDisplayed(false);
org.jzy3d.chart.Chart chart = new org.jzy3d.chart.Chart(Quality.Advanced,"swing");
chart.getScene().getGraph().add(surface);
IAxeLayout l = chart.getAxeLayout();
l.setXAxeLabel("Observation");
l.setYAxeLabel("Week");
l.setZAxeLabel("Rate");
l.setMainColor(org.jzy3d.colors.Color.GRAY);
JPanel p = new JPanel(new BorderLayout()); //another panel will be added to this panel and aligned left (BorderLayout.WEST)
p.add((JPanel)chart.getCanvas(),BorderLayout.CENTER);
... and this is what I get:
I'd like to customize this chart further, but I really cannot figure out how.
In particular I'd like to:
Zoom out the chart to fit my panel (in the attached picture you can see that the bottom of the chart is not visible);
Format axis labels (e.g. 0.6 displayed instead of 0.600000 for z axis, 2 displayed instead of 2.000 for x axis and so on...);
Invert color mapping (e.g. red when z value is lower, blue/green when z value is higher).
I solved by myself 2 of the 3 above mentioned points. Here are the solution just in case someone is interested:
Format Axis Labels: I createad a custom ITickRenderer which formats the axis labels basing on a DecimalFormat I have previously defined.
ITickRenderer r = new ITickRenderer(){
#Override
public String format(float arg0) {
return m.df.format(arg0);
}
};
Invert color mapping: I posted the solution here.
This is my situation. It involves aligning a scanned image which will account for incorrect scanning. I must align the scanned image with my Java program.
These are more details:
There is a table-like form printed on a sheet of paper, which will be scanned into an image file.
I will open the picture with Java, and I will have an OVERLAY of text boxes.
The text boxes are supposed to align correctly with the scanned image.
In order to align correctly, my Java program must analyze the scanned image and detect the coordinates of the edges of the table on the scanned image, and thus position the image and the textboxes so that the textboxes and the image both align properly (in case of incorrect scanning)
You see, the guy scanning the image might not necessarily place the image in a perfectly correct position, so I need my program to automatically align the scanned image as it loads it. This program will be reusable on many of such scanned images, so I need the program to be flexible in this way.
My question is one of the following:
How can I use Java to detect the y coordinate of the upper edge of the table and the x-coordinate of the leftmost edge of the table. The table is a a regular table with many cells, with black thin border, printed on a white sheet of paper (horizontal printout)
If an easier method exists to automatically align the scanned image in such a way that all scanned images will have the graphical table align to the same x, y coordinates, then share this method :).
If you don't know the answer to the above to questions, do tell me where I should start. I don't know much about graphics java programming and I have about 1 month to finish this program. Just assume that I have a tight schedule and I have to make the graphics part as simple as possible for me.
Cheers and thank you.
Try to start from a simple scenario and then improve the approach.
Detect corners.
Find the corners in the boundaries of the form.
Using the form corners coordinates, calculate the rotation angle.
Rotate/scale the image.
Map the position of each field in the form relative to form origin coordinates.
Match the textboxes.
The program presented at the end of this post does the steps 1 to 3. It was implemented using Marvin Framework. The image below shows the output image with the detected corners.
The program also outputs: Rotation angle:1.6365770416167182
Source code:
import java.awt.Color;
import java.awt.Point;
import marvin.image.MarvinImage;
import marvin.io.MarvinImageIO;
import marvin.plugin.MarvinImagePlugin;
import marvin.util.MarvinAttributes;
import marvin.util.MarvinPluginLoader;
public class FormCorners {
public FormCorners(){
// Load plug-in
MarvinImagePlugin moravec = MarvinPluginLoader.loadImagePlugin("org.marvinproject.image.corner.moravec");
MarvinAttributes attr = new MarvinAttributes();
// Load image
MarvinImage image = MarvinImageIO.loadImage("./res/printedForm.jpg");
// Process and save output image
moravec.setAttribute("threshold", 2000);
moravec.process(image, null, attr);
Point[] boundaries = boundaries(attr);
image = showCorners(image, boundaries, 12);
MarvinImageIO.saveImage(image, "./res/printedForm_output.jpg");
// Print rotation angle
double angle = (Math.atan2((boundaries[1].y*-1)-(boundaries[0].y*-1),boundaries[1].x-boundaries[0].x) * 180 / Math.PI);
angle = angle >= 0 ? angle : angle + 360;
System.out.println("Rotation angle:"+angle);
}
private Point[] boundaries(MarvinAttributes attr){
Point upLeft = new Point(-1,-1);
Point upRight = new Point(-1,-1);
Point bottomLeft = new Point(-1,-1);
Point bottomRight = new Point(-1,-1);
double ulDistance=9999,blDistance=9999,urDistance=9999,brDistance=9999;
double tempDistance=-1;
int[][] cornernessMap = (int[][]) attr.get("cornernessMap");
for(int x=0; x<cornernessMap.length; x++){
for(int y=0; y<cornernessMap[0].length; y++){
if(cornernessMap[x][y] > 0){
if((tempDistance = Point.distance(x, y, 0, 0)) < ulDistance){
upLeft.x = x; upLeft.y = y;
ulDistance = tempDistance;
}
if((tempDistance = Point.distance(x, y, cornernessMap.length, 0)) < urDistance){
upRight.x = x; upRight.y = y;
urDistance = tempDistance;
}
if((tempDistance = Point.distance(x, y, 0, cornernessMap[0].length)) < blDistance){
bottomLeft.x = x; bottomLeft.y = y;
blDistance = tempDistance;
}
if((tempDistance = Point.distance(x, y, cornernessMap.length, cornernessMap[0].length)) < brDistance){
bottomRight.x = x; bottomRight.y = y;
brDistance = tempDistance;
}
}
}
}
return new Point[]{upLeft, upRight, bottomRight, bottomLeft};
}
private MarvinImage showCorners(MarvinImage image, Point[] points, int rectSize){
MarvinImage ret = image.clone();
for(Point p:points){
ret.fillRect(p.x-(rectSize/2), p.y-(rectSize/2), rectSize, rectSize, Color.red);
}
return ret;
}
public static void main(String[] args) {
new FormCorners();
}
}
Edge detection is something that is typically done by enhancing the contrast between neighboring pixels, such that you get a easily detectable line, which is suitable for further processing.
To do this, a "kernel" transforms a pixel according it the pixel's inital value, and the value of that pixel's neighbors. A good edge detection kernel will enhance the differences between neighboring pixels, and reduce the strength of a pixel with similar neigbors.
I would start by looking at the Sobel operator. This might not return results that are immediately useful to you; however, it will get you far closer than you would be if you were to approach the problem with little knowledge of the field.
After you have some crisp clean edges, you can use larger kernels to detect points where it seems that a 90% bend in two lines occurs, that might give you the pixel coordinates of the outer rectangle, which might be enough for your purposes.
With those outer coordinates, it still is a bit of math to make the new pixels be composted with the average values between the old pixels rotated and moved to "match". The results (especially if you do not know about anti-aliasing math) can be pretty bad, adding blur to the image.
Sharpening filters might be a solution, but they come with their own issues, mainly they make the picture sharper by adding graininess. Too much, and it is obvious that the original image is not a high-quality scan.
I researched the libraries but in the end I found it more convenient to code up my own edge detection methods.
The class below will detect black/grayed out edges of a scanned sheet of paper that contains such edges, and will return the x and y coordinate of the edges of the sheet of paper, starting from the rightmost end (reverse = true) or from lower end (reverse = true) or from the top edge (reverse = false) or from left edge (reverse = false). Also...the program will take ranges along vertical edges (rangex) measured in pixels, and horizontal ranges (rangey) measured in pixels. The ranges determine outliers in the points received.
The program does 4 vertical cuts using the specified arrays, and 4 horizontal cuts. It retrieves the values of the dark dots. It uses the ranges to eliminate outliers. Sometimes, a little spot on the paper may cause an outlier point. The smaller the range, the fewer the outliers. However, sometimes the edge is slightly tilted, so you don't want to make the range too small.
Have fun. It works perfectly for me.
import java.awt.image.BufferedImage;
import java.awt.Color;
import java.util.ArrayList;
import java.lang.Math;
import java.awt.Point;
public class EdgeDetection {
public App ap;
public int[] horizontalCuts = {120, 220, 320, 420};
public int[] verticalCuts = {300, 350, 375, 400};
public void printEdgesTest(BufferedImage image, boolean reversex, boolean reversey, int rangex, int rangey){
int[] mx = horizontalCuts;
int[] my = verticalCuts;
//you are getting edge points here
//the "true" parameter indicates that it performs a cut starting at 0. (left edge)
int[] xEdges = getEdges(image, mx, reversex, true);
int edgex = getEdge(xEdges, rangex);
for(int x = 0; x < xEdges.length; x++){
System.out.println("EDGE = " + xEdges[x]);
}
System.out.println("THE EDGE = " + edgex);
//the "false" parameter indicates you are doing your cut starting at the end (image.getHeight)
//and ending at 0
//if the parameter was true, it would mean it would start the cuts at y = 0
int[] yEdges = getEdges(image, my, reversey, false);
int edgey = getEdge(yEdges, rangey);
for(int y = 0; y < yEdges.length; y++){
System.out.println("EDGE = " + yEdges[y]);
}
System.out.println("THE EDGE = " + edgey);
}
//This function takes an array of coordinates...detects outliers,
//and computes the average of non-outlier points.
public int getEdge(int[] edges, int range){
ArrayList<Integer> result = new ArrayList<Integer>();
boolean[] passes = new boolean[edges.length];
int[][] differences = new int[edges.length][edges.length-1];
//THIS CODE SEGMENT SAVES THE DIFFERENCES BETWEEN THE POINTS INTO AN ARRAY
for(int n = 0; n<edges.length; n++){
for(int m = 0; m<edges.length; m++){
if(m < n){
differences[n][m] = edges[n] - edges[m];
}else if(m > n){
differences[n][m-1] = edges[n] - edges[m];
}
}
}
//This array determines which points are outliers or nots (fall within range of other points)
for(int n = 0; n<edges.length; n++){
passes[n] = false;
for(int m = 0; m<edges.length-1; m++){
if(Math.abs(differences[n][m]) < range){
passes[n] = true;
System.out.println("EDGECHECK = TRUE" + n);
break;
}
}
}
//Create a new array only using valid points
for(int i = 0; i<edges.length; i++){
if(passes[i]){
result.add(edges[i]);
}
}
//Calculate the rounded mean... This will be the x/y coordinate of the edge
//Whether they are x or y values depends on the "reverse" variable used to calculate the edges array
int divisor = result.size();
int addend = 0;
double mean = 0;
for(Integer i : result){
addend += i;
}
mean = (double)addend/(double)divisor;
//returns the mean of the valid points: this is the x or y coordinate of your calculated edge.
if(mean - (int)mean >= .5){
System.out.println("MEAN " + mean);
return (int)mean+1;
}else{
System.out.println("MEAN " + mean);
return (int)mean;
}
}
//this function computes "dark" points, which include light gray, to detect edges.
//reverse - when true, starts counting from x = 0 or y = 0, and ends at image.getWidth or image.getHeight()
//verticalEdge - determines whether you want to detect a vertical edge, or a horizontal edge
//arr[] - determines the coordinates of the vertical or horizontal cuts you will do
//set the arr[] array according to the graphical layout of your scanned image
//image - this is the image you want to detect black/white edges of
public int[] getEdges(BufferedImage image, int[] arr, boolean reverse, boolean verticalEdge){
int red = 255;
int green = 255;
int blue = 255;
int[] result = new int[arr.length];
for(int n = 0; n<arr.length; n++){
for(int m = reverse ? (verticalEdge ? image.getWidth():image.getHeight())-1:0; reverse ? m>=0:m<(verticalEdge ? image.getWidth():image.getHeight());){
Color c = new Color(image.getRGB(verticalEdge ? m:arr[n], verticalEdge ? arr[n]:m));
red = c.getRed();
green = c.getGreen();
blue = c.getBlue();
//determine if the point is considered "dark" or not.
//modify the range if you want to only include really dark spots.
//occasionally, though, the edge might be blurred out, and light gray helps
if(red<239 && green<239 && blue<239){
result[n] = m;
break;
}
//count forwards or backwards depending on reverse variable
if(reverse){
m--;
}else{
m++;
}
}
}
return result;
}
}
A similar such problem I've done in the past basically figured out the orientation of the form, re-aligned it, re-scaled it, and I was all set. You can use the Hough transform to to detect the angular offset of the image (ie: how much it is rotated), but you still need to detect the boundaries of the form. It also had to accommodate for the boundaries of the piece of paper itself.
This was a lucky break for me, because it basically showed a black and white image in the middle of a big black border.
Apply an aggressive, 5x5 median filter to remove some noise.
Convert from grayscale to black and white (rescale intensity values from [0,255] to [0,1]).
Calculate the Principal Component Analysis (ie: calculate the Eigenvectors of the covariance matrix for your image from the calculated Eigenvalues) (http://en.wikipedia.org/wiki/Principal_component_analysis#Derivation_of_PCA_using_the_covariance_method)
4) This gives you a basis vector. You simply use that to re-orient your image to a standard basis matrix (ie: [1,0],[0,1]).
Your image is now aligned beautifully. I did this for normalizing the orientation of MRI scans of entire human brains.
You also know that you have a massive black border around the actual image. You simply keep deleting rows from the top and bottom, and both sides of the image until they are all gone. You can temporarily apply a 7x7 median or mode filter to a copy of the image so far at this point. It helps rule out too much border remaining in the final image from thumbprints, dirt, etc.
I'm currently trying to use JFreeChart to represent 3D data in a 2D graph.
Essentially, I have a 2d array called data[i][j]. The i and j represent the y and x coordinates where I want to plot. The value of data[i][j] represents a frequency value, which I want to represent in the graph as a colour.
I'm not entirely sure what something like this is called, but it would look something like this:
Now I have been trying to do this using XYBlockRenderer, however I am having issues with defining the dataset. I am trying to use DefaultXYZDataset, but I'm really confused at how to even define the data here.
Can someone explain how to use the DefaultXYZDataset to accomplish such a task?
DefaultXYZDataset dataset = new DefaultXYZDataset();
Concentration.dataoutHeight = Concentration.dataout[0].length;
System.out.println(Concentration.dataoutHeight);
System.out.println(ImageProcessor.MAXCBVINT);
double[][] data = new double[3][ImageProcessor.MAXCBVINT];
for (int i = 0; i < Concentration.dataoutHeight; i++) {
for (int j = 0; j < ImageProcessor.MAXCBVINT; j++) {
data[0][j] = j;//x value
data[1][j] = i;//y value
data[2][j] = Concentration.dataout[j][i][0];//Colour
}
dataset.addSeries(i, data);
}
NumberAxis xAxis = new NumberAxis("Intensity");
xAxis.setStandardTickUnits(NumberAxis.createIntegerTickUnits());
xAxis.setLowerMargin(0.0);
xAxis.setUpperMargin(0.0);
NumberAxis yAxis = new NumberAxis("Distance to Closest Blood Vessel (um)");
yAxis.setStandardTickUnits(NumberAxis.createIntegerTickUnits());
yAxis.setLowerMargin(0.0);
yAxis.setUpperMargin(0.0);
XYBlockRenderer renderer = new XYBlockRenderer();
PaintScale scale = new GrayPaintScale(0, 10000.0);
renderer.setPaintScale(scale);
renderer.setBlockHeight(1);
renderer.setBlockWidth(1);
XYPlot plot = new XYPlot(dataset, xAxis, yAxis, renderer);
plot.setBackgroundPaint(Color.lightGray);
plot.setDomainGridlinesVisible(false);
plot.setRangeGridlinePaint(Color.white);
JFreeChart chart = new JFreeChart("Surface Plot", plot);
chart.removeLegend();
chart.setBackgroundPaint(Color.white);
ChartFrame frame = new ChartFrame("Surface Map - "
+ (Concentration.testing ? "TESTING using "
+ Concentration.testfile : currentFile.getName()), chart);
frame.pack();
frame.setVisible(true);
You have two options:
Represent them as 3d
3D Lib for JFreeChart
You need to use the class : XYBlockRenderer which does exactly what you are asking. You can download the JFreeChart demo collection where the code for this is given.
(source code of class here)
There is also this full code example with 4D very similar.
I am creating a chart using JFreeChart, and I get the following behavior.
Notice the gap between the light and dark shades of pink.
I was just plotting sample data over time, so I don't think it has anything to do with my data. Has anyone encountered this issue before? I am using a stacked XY area chart.
Here is the code to create my dataset:
double sample = 1.0;
TimeTableXYDataset dataset = new TimeTableXYDataset();
List<TimeSeries> timeSeriesList = timeSeriesCollection.getSeries();
for (TimeSeries t : timeSeriesList) {
for (index = 0; index < t.getItemCount(); index++) {
dataset.add(t.getTimePeriod(index), sample, t.getDescription());
}
sample++;
}
Problem fixed. I was overwriting the plot renderer with itself.