I was trying to follow the example here: http://docs.oracle.com/javase/tutorial/displayCode.html?code=http://docs.oracle.com/javase/tutorial/essential/concurrency/examples/ForkBlur.java
/*
* Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* - Neither the name of Oracle or the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
import java.awt.image.BufferedImage;
import java.io.File;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.RecursiveAction;
import javax.imageio.ImageIO;
/**
* ForkBlur implements a simple horizontal image blur. It averages pixels in the
* source array and writes them to a destination array. The sThreshold value
* determines whether the blurring will be performed directly or split into two
* tasks.
*
* This is not the recommended way to blur images; it is only intended to
* illustrate the use of the Fork/Join framework.
*/
public class ForkBlur extends RecursiveAction {
private int[] mSource;
private int mStart;
private int mLength;
private int[] mDestination;
private int mBlurWidth = 15; // Processing window size, should be odd.
public ForkBlur(int[] src, int start, int length, int[] dst) {
mSource = src;
mStart = start;
mLength = length;
mDestination = dst;
}
// Average pixels from source, write results into destination.
protected void computeDirectly() {
int sidePixels = (mBlurWidth - 1) / 2;
for (int index = mStart; index < mStart + mLength; index++) {
// Calculate average.
float rt = 0, gt = 0, bt = 0;
for (int mi = -sidePixels; mi <= sidePixels; mi++) {
int mindex = Math.min(Math.max(mi + index, 0), mSource.length - 1);
int pixel = mSource[mindex];
rt += (float) ((pixel & 0x00ff0000) >> 16) / mBlurWidth;
gt += (float) ((pixel & 0x0000ff00) >> 8) / mBlurWidth;
bt += (float) ((pixel & 0x000000ff) >> 0) / mBlurWidth;
}
// Re-assemble destination pixel.
int dpixel = (0xff000000)
| (((int) rt) << 16)
| (((int) gt) << 8)
| (((int) bt) << 0);
mDestination[index] = dpixel;
}
}
protected static int sThreshold = 10000;
#Override
protected void compute() {
if (mLength < sThreshold) {
computeDirectly();
return;
}
int split = mLength / 2;
invokeAll(new ForkBlur(mSource, mStart, split, mDestination),
new ForkBlur(mSource, mStart + split, mLength - split,
mDestination));
}
// Plumbing follows.
public static void main(String[] args) throws Exception {
String srcName = "awesome_face.jpg";
File srcFile = new File(srcName);
BufferedImage image = ImageIO.read(srcFile);
System.out.println("Source image: " + srcName);
BufferedImage blurredImage = blur(image);
String dstName = "blurred-awesome-face.jpg";
File dstFile = new File(dstName);
ImageIO.write(blurredImage, "jpg", dstFile);
System.out.println("Output image: " + dstName);
}
public static BufferedImage blur(BufferedImage srcImage) {
int w = srcImage.getWidth();
int h = srcImage.getHeight();
int[] src = srcImage.getRGB(0, 0, w, h, null, 0, w);
int[] dst = new int[src.length];
System.out.println("Array size is " + src.length);
System.out.println("Threshold is " + sThreshold);
int processors = Runtime.getRuntime().availableProcessors();
System.out.println(Integer.toString(processors) + " processor"
+ (processors != 1 ? "s are " : " is ")
+ "available");
ForkBlur fb = new ForkBlur(src, 0, src.length, dst);
ForkJoinPool pool = new ForkJoinPool();
long startTime = System.currentTimeMillis();
pool.invoke(fb);
long endTime = System.currentTimeMillis();
System.out.println("Image blur took " + (endTime - startTime) +
" milliseconds.");
BufferedImage dstImage =
new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
dstImage.setRGB(0, 0, w, h, dst, 0, w);
return dstImage;
}
}
I copied this code to my computer and compiled and ran it and received the following error:
Source image: awesome_face.jpg
Array size is 100000
Threshold is 10000
4 processors are available
Image blur took 19 milliseconds.
Exception in thread "main" javax.imageio.IIOException: Invalid argument to native writeImage
at com.sun.imageio.plugins.jpeg.JPEGImageWriter.writeImage(Native Method)
at com.sun.imageio.plugins.jpeg.JPEGImageWriter.writeOnThread(JPEGImageWriter.java:1058)
at com.sun.imageio.plugins.jpeg.JPEGImageWriter.write(JPEGImageWriter.java:360)
at javax.imageio.ImageWriter.write(ImageWriter.java:615)
at javax.imageio.ImageIO.doWrite(ImageIO.java:1612)
at javax.imageio.ImageIO.write(ImageIO.java:1536)
at ForkBlur.main(ForkBlur.java:112)
It looks like the problem is at line 112 in the ImageIO.write() method. I looked at the API for this method here: http://docs.oracle.com/javase/7/docs/api/javax/imageio/ImageIO.html From my understanding the arguments to write() are okay but something is really messed up at runtime.
Does anyone know what is wrong with this program?
Regards,
...
Related
I am busy with transmitting a Bluetooth Low Energy UUID via my Raspberry Pi. That setup is done and works to a satisfactory extent.
But now my personal objective is to make a very simple android app, that catches this UUID and displays it on the android screen.
So after some browsing around, I found this Radius Network iBeacon package with a iBeacon.java code in it.
I am very new to Android Studio. For the life of me, I can't seem to debug that iBeacon.java code to my android (Jellybean) phone..
So basically the directory for that code looks like this:
android-ibeacon-service/src/com/radiusnetworks/ibeacon/client/iBeacon.java
I guess the first directory part is the package?
I have tried importing the whole thing, but it shows so many things not working,
I have also tried just importing the iBeacon.java code.. But that doesn't run to my phone..
The iBeacon.java looks like this:
/**
* Radius Networks, Inc.
* http://www.radiusnetworks.com
*
* #author David G. Young
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package com.radiusnetworks.ibeacon;
import java.util.Collections;
import com.radiusnetworks.ibeacon.client.RangedIBeacon;
import android.util.Log;
/**
* The <code>IBeacon</code> class represents a single hardware iBeacon detected by
* an Android device.
*
* <pre>An iBeacon is identified by a three part identifier based on the fields
* proximityUUID - a string UUID typically identifying the owner of a
* number of ibeacons
* major - a 16 bit integer indicating a group of iBeacons
* minor - a 16 bit integer identifying a single iBeacon</pre>
*
* An iBeacon sends a Bluetooth Low Energy (BLE) advertisement that contains these
* three identifiers, along with the calibrated tx power (in RSSI) of the
* iBeacon's Bluetooth transmitter.
*
* This class may only be instantiated from a BLE packet, and an RSSI measurement for
* the packet. The class parses out the three part identifier, along with the calibrated
* tx power. It then uses the measured RSSI and calibrated tx power to do a rough
* distance measurement (the accuracy field) and group it into a more reliable buckets of
* distance (the proximity field.)
*
* #author David G. Young
* #see Region#matchesIBeacon(IBeacon iBeacon)
*/
public class IBeacon {
/**
* Less than half a meter away
*/
public static final int PROXIMITY_IMMEDIATE = 1;
/**
* More than half a meter away, but less than four meters away
*/
public static final int PROXIMITY_NEAR = 2;
/**
* More than four meters away
*/
public static final int PROXIMITY_FAR = 3;
/**
* No distance estimate was possible due to a bad RSSI value or measured TX power
*/
public static final int PROXIMITY_UNKNOWN = 0;
final private static char[] hexArray = {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
private static final String TAG = "IBeacon";
/**
* A 16 byte UUID that typically represents the company owning a number of iBeacons
* Example: E2C56DB5-DFFB-48D2-B060-D0F5A71096E0
*/
protected String proximityUuid;
/**
* A 16 bit integer typically used to represent a group of iBeacons
*/
protected int major;
/**
* A 16 bit integer that identifies a specific iBeacon within a group
*/
protected int minor;
/**
* An integer with four possible values representing a general idea of how far the iBeacon is away
* #see #PROXIMITY_IMMEDIATE
* #see #PROXIMITY_NEAR
* #see #PROXIMITY_FAR
* #see #PROXIMITY_UNKNOWN
*/
protected Integer proximity;
/**
* A double that is an estimate of how far the iBeacon is away in meters. This name is confusing, but is copied from
* the iOS7 SDK terminology. Note that this number fluctuates quite a bit with RSSI, so despite the name, it is not
* super accurate. It is recommended to instead use the proximity field, or your own bucketization of this value.
*/
protected Double accuracy;
/**
* The measured signal strength of the Bluetooth packet that led do this iBeacon detection.
*/
protected int rssi;
/**
* The calibrated measured Tx power of the iBeacon in RSSI
* This value is baked into an iBeacon when it is manufactured, and
* it is transmitted with each packet to aid in the distance estimate
*/
protected int txPower;
/**
* If multiple RSSI samples were available, this is the running average
*/
protected Double runningAverageRssi = null;
/**
* #see #accuracy
* #return accuracy
*/
public double getAccuracy() {
if (accuracy == null) {
accuracy = calculateAccuracy(txPower, runningAverageRssi != null ? runningAverageRssi : rssi );
}
return accuracy;
}
/**
* #see #major
* #return major
*/
public int getMajor() {
return major;
}
/**
* #see #minor
* #return minor
*/
public int getMinor() {
return minor;
}
/**
* #see #proximity
* #return proximity
*/
public int getProximity() {
if (proximity == null) {
proximity = calculateProximity(getAccuracy());
}
return proximity;
}
/**
* #see #rssi
* #return rssi
*/
public int getRssi() {
return rssi;
}
/**
* #see #txPower
* #return txPowwer
*/
public int getTxPower() {
return txPower;
}
/**
* #see #proximityUuid
* #return proximityUuid
*/
public String getProximityUuid() {
return proximityUuid;
}
#Override
public int hashCode() {
return minor;
}
/**
* Two detected iBeacons are considered equal if they share the same three identifiers, regardless of their distance or RSSI.
*/
#Override
public boolean equals(Object that) {
if (!(that instanceof IBeacon)) {
return false;
}
IBeacon thatIBeacon = (IBeacon) that;
return (thatIBeacon.getMajor() == this.getMajor() && thatIBeacon.getMinor() == this.getMinor() && thatIBeacon.getProximityUuid() == thatIBeacon.getProximityUuid());
}
/**
* Construct an iBeacon from a Bluetooth LE packet collected by Android's Bluetooth APIs
*
* #param scanData The actual packet bytes
* #param rssi The measured signal strength of the packet
* #return An instance of an <code>IBeacon</code>
*/
public static IBeacon fromScanData(byte[] scanData, int rssi) {
if (((int)scanData[5] & 0xff) == 0x4c &&
((int)scanData[6] & 0xff) == 0x00 &&
((int)scanData[7] & 0xff) == 0x02 &&
((int)scanData[8] & 0xff) == 0x15) {
// yes! This is an iBeacon
}
else if (((int)scanData[5] & 0xff) == 0x2d &&
((int)scanData[6] & 0xff) == 0x24 &&
((int)scanData[7] & 0xff) == 0xbf &&
((int)scanData[8] & 0xff) == 0x16) {
// this is an Estimote beacon
IBeacon iBeacon = new IBeacon();
iBeacon.major = 0;
iBeacon.minor = 0;
iBeacon.proximityUuid = "00000000-0000-0000-0000-000000000000";
iBeacon.txPower = -55;
return iBeacon;
}
else {
// This is not an iBeacon
Log.d(TAG, "This is not an iBeacon advertisment. The bytes I see are: "+bytesToHex(scanData));
return null;
}
IBeacon iBeacon = new IBeacon();
iBeacon.major = (scanData[25] & 0xff) * 0x100 + (scanData[26] & 0xff);
iBeacon.minor = (scanData[27] & 0xff) * 0x100 + (scanData[28] & 0xff);
iBeacon.txPower = (int)scanData[29]; // this one is signed
iBeacon.rssi = rssi;
// AirLocate:
// 02 01 1a 1a ff 4c 00 02 15 # Apple's fixed iBeacon advertising prefix
// e2 c5 6d b5 df fb 48 d2 b0 60 d0 f5 a7 10 96 e0 # iBeacon profile uuid
// 00 00 # major
// 00 00 # minor
// c5 # The 2's complement of the calibrated Tx Power
// Estimote:
// 02 01 1a 11 07 2d 24 bf 16
// 394b31ba3f486415ab376e5c0f09457374696d6f7465426561636f6e00000000000000000000000000000000000000000000000000
byte[] proximityUuidBytes = new byte[16];
System.arraycopy(scanData, 9, proximityUuidBytes, 0, 16);
String hexString = bytesToHex(proximityUuidBytes);
StringBuilder sb = new StringBuilder();
sb.append(hexString.substring(0,8));
sb.append("-");
sb.append(hexString.substring(8,12));
sb.append("-");
sb.append(hexString.substring(12,16));
sb.append("-");
sb.append(hexString.substring(16,20));
sb.append("-");
sb.append(hexString.substring(20,32));
iBeacon.proximityUuid = sb.toString();
return iBeacon;
}
protected IBeacon(IBeacon otherIBeacon) {
this.major = otherIBeacon.major;
this.minor = otherIBeacon.minor;
this.accuracy = otherIBeacon.accuracy;
this.proximity = otherIBeacon.proximity;
this.rssi = otherIBeacon.rssi;
this.proximityUuid = otherIBeacon.proximityUuid;
this.txPower = otherIBeacon.txPower;
}
protected IBeacon() {
}
protected static double calculateAccuracy(int txPower, double rssi) {
if (rssi == 0) {
return -1.0; // if we cannot determine accuracy, return -1.
}
Log.d(TAG, "calculating accuracy based on rssi of "+rssi);
double ratio = rssi*1.0/txPower;
if (ratio < 1.0) {
return Math.pow(ratio,10);
}
else {
double accuracy = (0.89976)*Math.pow(ratio,7.7095) + 0.111;
Log.d(TAG, " avg rssi: "+rssi+" accuracy: "+accuracy);
return accuracy;
}
}
protected static int calculateProximity(double accuracy) {
if (accuracy < 0) {
return PROXIMITY_UNKNOWN;
// is this correct? does proximity only show unknown when accuracy is negative? I have seen cases where it returns unknown when
// accuracy is -1;
}
if (accuracy < 0.5 ) {
return IBeacon.PROXIMITY_IMMEDIATE;
}
// forums say 3.0 is the near/far threshold, but it looks to be based on experience that this is 4.0
if (accuracy <= 4.0) {
return IBeacon.PROXIMITY_NEAR;
}
// if it is > 4.0 meters, call it far
return IBeacon.PROXIMITY_FAR;
}
private static String bytesToHex(byte[] bytes) {
char[] hexChars = new char[bytes.length * 2];
int v;
for ( int j = 0; j < bytes.length; j++ ) {
v = bytes[j] & 0xFF;
hexChars[j * 2] = hexArray[v >>> 4];
hexChars[j * 2 + 1] = hexArray[v & 0x0F];
}
return new String(hexChars);
}
}
How can I solve this mystery? :)
I'm the one who wrote that IBeacon.java code about two years ago. Since then it has been rewritten and is now available in a ready to run Android Studio reference app here:
https://github.com/AltBeacon/android-beacon-library-reference
You do have to make one change to it to get it to detect iBeacons. See here for that change:
Is this the correct layout to detect iBeacons with AltBeacon's Android Beacon Library?
I am using javax.sound to make sounds, however when you play it they have some sort of noise in background, which even overcomes the sound if you play few notes at once. Here is the code:
public final static double notes[] = new double[] {130.81, 138.59, 146.83, 155.56, 164.81, 174.61, 185,
196, 207.65, 220, 233.08, 246.94, 261.63, 277.18, 293.66,
311.13, 329.63, 349.23, 369.99, 392, 415.3, 440, 466.16,
493.88, 523.25, 554.37};
public static void playSound(int note, int type) throws LineUnavailableException { //type 0 = sin, type 1 = square
Thread t = new Thread() {
public void run() {
try {
int sound = (int) (notes[note] * 100);
byte[] buf = new byte[1];
AudioFormat af = new AudioFormat((float) sound, 8, 1, true,
false);
SourceDataLine sdl;
sdl = AudioSystem.getSourceDataLine(af);
sdl = AudioSystem.getSourceDataLine(af);
sdl.open(af);
sdl.start();
int maxi = (int) (1000 * (float) sound / 1000);
for (int i = 0; i < maxi; i++) {
double angle = i / ((float) 44100 / 440) * 2.0
* Math.PI;
double val = 0;
if (type == 0) val = Math.sin(angle)*100;
if (type == 1) val = square(angle)*50;
buf[0] = (byte) (val * (maxi - i) / maxi);
sdl.write(buf, 0, 1);
}
sdl.drain();
sdl.stop();
sdl.close();
} catch (LineUnavailableException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
};
};
t.start();
}
public static double square (double angle){
angle = angle % (Math.PI*2);
if (angle > Math.PI) return 1;
else return 0;
}
This code is from here: https://stackoverflow.com/a/1932537/3787777
In this answer I will refer to 1) your code, 2) better approach (IMHO:) and 3) playing of two notes in the same time.
Your code
First, the sample rate should not depend on note frequency. Therefore try:
AudioFormat(44100,...
Next, use 16 bit sampling (sounds better!). Here is your code that plays simple tone without noise - but I would use it bit differently (see later). Please look for the comments:
Thread t = new Thread() {
public void run() {
try {
int sound = (440 * 100); // play A
AudioFormat af = new AudioFormat(44100, 16, 1, true, false);
SourceDataLine sdl;
sdl = AudioSystem.getSourceDataLine(af);
sdl.open(af, 4096 * 2);
sdl.start();
int maxi = (int) (1000 * (float) sound / 1000); // should not depend on notes frequency!
byte[] buf = new byte[maxi * 2]; // try to find better len!
int i = 0;
while (i < maxi * 2) {
// formula is changed to be simple sine!!
double val = Math.sin(Math.PI * i * 440 / 44100);
short s = (short) (Short.MAX_VALUE * val);
buf[i++] = (byte) s;
buf[i++] = (byte) (s >> 8); // little endian
}
sdl.write(buf, 0, maxi);
sdl.drain();
sdl.stop();
sdl.close();
} catch (LineUnavailableException e) {
e.printStackTrace();
}
}
};
t.start();
Proposal for better code
Here is a simplified version of your code that plays some note (frequency) without noise. I like it better as we first create array of doubles, which are universal values. These values can be combined together, or stored or further modified. Then we convert them to (8bit or 16bit) samples values.
private static byte[] buffer = new byte[4096 * 2 / 3];
private static int bufferSize = 0;
// plays a sample in range (-1, +1).
public static void play(SourceDataLine line, double in) {
if (in < -1.0) in = -1.0; // just sanity checks
if (in > +1.0) in = +1.0;
// convert to bytes - need 2 bytes for 16 bit sample
short s = (short) (Short.MAX_VALUE * in);
buffer[bufferSize++] = (byte) s;
buffer[bufferSize++] = (byte) (s >> 8); // little Endian
// send to line when buffer is full
if (bufferSize >= buffer.length) {
line.write(buffer, 0, buffer.length);
bufferSize = 0;
}
// todo: be sure that whole buffer is sent to line!
}
// prepares array of doubles, not related with the sampling value!
private static double[] tone(double hz, double duration) {
double amplitude = 1.0;
int N = (int) (44100 * duration);
double[] a = new double[N + 1];
for (int i = 0; i <= N; i++) {
a[i] = amplitude * Math.sin(2 * Math.PI * i * hz / 44100);
}
return a;
}
// finally:
public static void main(String[] args) throws LineUnavailableException {
AudioFormat af = new AudioFormat(44100, 16, 1, true, false);
SourceDataLine sdl = AudioSystem.getSourceDataLine(af);
sdl.open(af, 4096 * 2);
sdl.start();
double[] tones = tone(440, 2.0); // play A for 2 seconds
for (double t : tones) {
play(sdl, t);
}
sdl.drain();
sdl.stop();
sdl.close();
}
Sounds nice ;)
Play two notes in the same time
Just combine two notes:
double[] a = tone(440, 1.0); // note A
double[] b = tone(523.25, 1.0); // note C (i hope:)
for (int i = 0; i < a.length; i++) {
a[i] = (a[i] + b[i]) / 2;
}
for (double t : a) {
play(sdl, t);
}
Remember that with double array you can combine and manipulate your tones - i.e. to make composition of tone sounds that are being played in the same time. Of course, if you add 3 tones, you need to normalize the value by dividing with 3 and so on.
Ding Dong :)
The answer has already been provided, but I want to provide some information that might help understanding the solution.
Why 44100?
44.1 kHz audio is widely used, due to this being the sampling rate used in CDs. Analog audio is recorded by sampling it 44,100 times per second (1 cycle per second = 1 Hz), and then these samples are used to reconstruct the audio signal when playing it back. The reason behind the selection of this frequency is rather complex; and unimportant for this explanation. That said, the suggestion of using 22000 is not very good because that frequency is too close to the human hearing range (20Hz - 20kHz). You would want to use a sampling rate higher than 40kHz for good sound quality. I think mp4 uses 96kHz.
Why 16-bit?
The standard used for CDs is 44.1kHz/16-bit. MP4 uses 96kHz/24-bit. The sample rate refers to how many X-bit samples are recorded every second. CD-quality sampling uses 44,100 16-bit samples to reproduce sound.
Why is this explanation important?
The thing to remember is that you are trying to produce digital sound (not analog). This means that these bits and bytes have to be processed by an audio CODEC. In hardware, an audio CODEC is a device that encodes analog audio as digital signals and decodes digital back into analog. For audio outputs, the digitized sound must go through a Digital-to-Analog Converter (DAC) in order for proper sound to come out of the speakers. Two of the most important characteristics of a DAC are its bandwidth and its signal-to-noise ratio and the actual bandwidth of a DAC is characterized primarily by its sampling rate.
Basically, you can't use an arbitrary sampling rate because the audio will not be reproduced well by your audio device for the reasons stated above. When in doubt, check your computer hardware and find out what your CODEC supports.
if ((e != null && l.size() > 0) && (l != null)) {
Graphics2D g2d = (Graphics2D) g;
g2d.setStroke(new BasicStroke(2));
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
for (int i = 0; i < l.size() - 1; i++) {
Point p1 = l.get(i);
Point p2 = l.get(i + 1);
g.setColor(currentColor);
g.drawLine(p1.x, p1.y, p2.x, p2.y);
}
Point p3 = l.get(l.size() - 1);
g.drawLine(p3.x, p3.y, e.getPoint().x, e.getPoint().y);
}
I am creating a pencil-like tool in java and I'm having some problems when it comes to drawing the lines. It works as it should, but the lines are very edged and really not smooth at all.
I've looked into smoothing them by bezier things but that would be tricky for points close to one another. I just maybe want some form of line next to the one I have but with a lower alpha-value or something like that. That is the effect i'm after.
This is what it looks like now.
http://imgur.com/dycSEwT
I suspect that the problem you are running into is that you are preserving a little too much information. You could certainly do some blurring or fattening of the line, which might help you to create a blurrier, fatter line, but I think that that would not actually help you to clean up the jitters. My recommendation to make the lines look really nice would be a two step process. The first process would be to use DP Line simplification to remove a lot of the slight jitters. The second step of the process would be to use a Centripetal Catmull-Rom Spline in order to make the whole line flow like a very elegant curve. The beauty of using this sort of spline for this purpose is that you don't need to do any serious work trying to figure out how to come up with all the control points that you would for doing Besier curves. You can just use the original points plus two points at the start and end of your curve.
The Duglas-Peucker line simplifier is available in java using JTS from vivid solutions:
http://www.vividsolutions.com/jts/JTSHome.htm
Here is the link to the Catmull-Rom code.
Catmull-rom curve with no cusps and no self-intersections
Direct Drawing (jittery)
Simplified Drawing (Using JTS DP Line Simplification)
Simplified and Smoothed using Centripetal Catmull-Rom
Draw Panel Example Code
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package demo;
import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.geom.GeometryFactory;
import com.vividsolutions.jts.geom.LineString;
import com.vividsolutions.jts.simplify.DouglasPeuckerSimplifier;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
*
* #author hdunsford
*/
public class DrawPanel extends javax.swing.JPanel {
private List<Coordinate> coords;
/**
* Creates new form DrawPanel
*/
public DrawPanel() {
initComponents();
coords = new ArrayList<>();
this.addMouseMotionListener(new MouseMotionListener() {
#Override
public void mouseDragged(MouseEvent e) {
coords.add(new Coordinate(e.getX(), e.getY()));
repaint();
}
#Override
public void mouseMoved(MouseEvent e) {
}
});
}
#Override
protected void paintComponent(Graphics g) {
try {
super.paintComponent(g); // paint background
Graphics2D g2d = (Graphics2D) g;
g2d.setStroke(new BasicStroke(2));
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
GeometryFactory f = new GeometryFactory();
if (coords.size() < 2) {
return;
}
LineString ls = f.createLineString(coords.toArray(new Coordinate[0]));
//Geometry simple = ls;
Geometry simple = DouglasPeuckerSimplifier.simplify(ls, 3.0);
if (simple.getCoordinates().length < 2) {
return;
}
List<Coordinate> raw = new ArrayList<>();
raw.addAll(Arrays.asList(simple.getCoordinates()));
List<Coordinate> spline = CatmullRom.interpolate(raw, 10);
int[] xPoints = new int[spline.size()];
int[] yPoints = new int[spline.size()];
for (int i = 0; i < spline.size(); i++) {
xPoints[i] = (int) spline.get(i).x;
yPoints[i] = (int) spline.get(i).y;
}
g2d.setColor(Color.red);
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.drawPolyline(xPoints, yPoints, xPoints.length);
} catch (Exception ex) {
Logger.getLogger(DrawPanel.class.getName()).log(Level.SEVERE, null, ex);
}
}
/**
* This method is called from within the constructor to initialize the form. WARNING:
* Do NOT modify this code. The content of this method is always regenerated by the
* Form Editor.
*/
#SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">
private void initComponents() {
setBackground(new java.awt.Color(255, 255, 255));
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGap(0, 400, Short.MAX_VALUE)
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGap(0, 300, Short.MAX_VALUE)
);
}// </editor-fold>
// Variables declaration - do not modify
// End of variables declaration
}
Centripetal Catmull-Rom (tweaked to work with JTS Coordinate)
package demo;
import com.vividsolutions.jts.geom.Coordinate;
import java.util.ArrayList;
import java.util.List;
/**
*
* #author hdunsford
*/
public class CatmullRom {
/**
* This method will calculate the Catmull-Rom interpolation curve, returning it as a
* list of Coordinate coordinate objects. This method in particular adds the first and
* last control points which are not visible, but required for calculating the spline.
*
* #param coordinates The list of original straight line points to calculate an
* interpolation from.
* #param pointsPerSegment The integer number of equally spaced points to return along
* each curve. The actual distance between each point will depend on the spacing
* between the control points.
* #return The list of interpolated coordinates.
*/
public static List<Coordinate> interpolate(List<Coordinate> coordinates, int pointsPerSegment)
throws Exception {
List<Coordinate> vertices = new ArrayList<>();
for (Coordinate c : coordinates) {
vertices.add(new Coordinate(c.x, c.y));
}
if (pointsPerSegment < 2) {
throw new Exception("The pointsPerSegment parameter must be greater than 2, since 2 points is just the linear segment.");
}
// Cannot interpolate curves given only two points. Two points
// is best represented as a simple line segment.
if (vertices.size() < 3) {
return vertices;
}
// Test whether the shape is open or closed by checking to see if
// the first point intersects with the last point. M and Z are ignored.
boolean isClosed = vertices.get(0).x == vertices.get(vertices.size() - 1).x
&& vertices.get(0).y == vertices.get(vertices.size() - 1).y;
if (isClosed) {
// Use the second and second from last points as control points.
// get the second point.
Coordinate p2 = new Coordinate(vertices.get(1));
// get the point before the last point
Coordinate pn1 = new Coordinate(vertices.get(vertices.size() - 2));
// insert the second from the last point as the first point in the list
// because when the shape is closed it keeps wrapping around to
// the second point.
vertices.add(0, pn1);
// add the second point to the end.
vertices.add(p2);
} else {
// The shape is open, so use control points that simply extend
// the first and last segments
// Get the change in x and y between the first and second coordinates.
double dx = vertices.get(1).x - vertices.get(0).x;
double dy = vertices.get(1).y - vertices.get(0).y;
// Then using the change, extrapolate backwards to find a control point.
double x1 = vertices.get(0).x - dx;
double y1 = vertices.get(0).y - dy;
// Actaully create the start point from the extrapolated values.
Coordinate start = new Coordinate(x1, y1);
// Repeat for the end control point.
int n = vertices.size() - 1;
dx = vertices.get(n).x - vertices.get(n - 1).x;
dy = vertices.get(n).y - vertices.get(n - 1).y;
double xn = vertices.get(n).x + dx;
double yn = vertices.get(n).y + dy;
Coordinate end = new Coordinate(xn, yn);
// insert the start control point at the start of the vertices list.
vertices.add(0, start);
// append the end control ponit to the end of the vertices list.
vertices.add(end);
}
// Dimension a result list of coordinates.
List<Coordinate> result = new ArrayList<>();
// When looping, remember that each cycle requires 4 points, starting
// with i and ending with i+3. So we don't loop through all the points.
for (int i = 0; i < vertices.size() - 3; i++) {
// Actually calculate the Catmull-Rom curve for one segment.
List<Coordinate> points = interpolate(vertices, i, pointsPerSegment);
// Since the middle points are added twice, once for each bordering
// segment, we only add the 0 index result point for the first
// segment. Otherwise we will have duplicate points.
if (result.size() > 0) {
points.remove(0);
}
// Add the coordinates for the segment to the result list.
result.addAll(points);
}
return result;
}
/**
* Given a list of control points, this will create a list of pointsPerSegment points
* spaced uniformly along the resulting Catmull-Rom curve.
*
* #param points The list of control points, leading and ending with a coordinate that
* is only used for controling the spline and is not visualized.
* #param index The index of control point p0, where p0, p1, p2, and p3 are used in
* order to create a curve between p1 and p2.
* #param pointsPerSegment The total number of uniformly spaced interpolated points to
* calculate for each segment. The larger this number, the smoother the resulting
* curve.
* #return the list of coordinates that define the CatmullRom curve between the points
* defined by index+1 and index+2.
*/
public static List<Coordinate> interpolate(List<Coordinate> points, int index, int pointsPerSegment) {
List<Coordinate> result = new ArrayList<>();
double[] x = new double[4];
double[] y = new double[4];
double[] time = new double[4];
for (int i = 0; i < 4; i++) {
x[i] = points.get(index + i).x;
y[i] = points.get(index + i).y;
time[i] = i;
}
double tstart;
double tend;
double total = 0;
for (int i = 1; i < 4; i++) {
double dx = x[i] - x[i - 1];
double dy = y[i] - y[i - 1];
total += Math.pow(dx * dx + dy * dy, .25);
time[i] = total;
}
tstart = time[1];
tend = time[2];
int segments = pointsPerSegment - 1;
result.add(points.get(index + 1));
for (int i = 1; i < segments; i++) {
double xi = interpolate(x, time, tstart + (i * (tend - tstart)) / segments);
double yi = interpolate(y, time, tstart + (i * (tend - tstart)) / segments);
result.add(new Coordinate(xi, yi));
}
result.add(points.get(index + 2));
return result;
}
/**
* Unlike the other implementation here, which uses the default "uniform" treatment of
* t, this computation is used to calculate the same values but introduces the ability
* to "parameterize" the t values used in the calculation. This is based on Figure 3
* from http://www.cemyuksel.com/research/catmullrom_param/catmullrom.pdf
*
* #param p An array of double values of length 4, where interpolation occurs from p1
* to p2.
* #param time An array of time measures of length 4, corresponding to each p value.
* #param t the actual interpolation ratio from 0 to 1 representing the position
* between p1 and p2 to interpolate the value.
* #return
*/
public static double interpolate(double[] p, double[] time, double t) {
double L01 = p[0] * (time[1] - t) / (time[1] - time[0]) + p[1] * (t - time[0]) / (time[1] - time[0]);
double L12 = p[1] * (time[2] - t) / (time[2] - time[1]) + p[2] * (t - time[1]) / (time[2] - time[1]);
double L23 = p[2] * (time[3] - t) / (time[3] - time[2]) + p[3] * (t - time[2]) / (time[3] - time[2]);
double L012 = L01 * (time[2] - t) / (time[2] - time[0]) + L12 * (t - time[0]) / (time[2] - time[0]);
double L123 = L12 * (time[3] - t) / (time[3] - time[1]) + L23 * (t - time[1]) / (time[3] - time[1]);
double C12 = L012 * (time[2] - t) / (time[2] - time[1]) + L123 * (t - time[1]) / (time[2] - time[1]);
return C12;
}
}
I've been working recently on a fractal generator, and have been specifically working on the Mandelbrot set. Unfortunately, zooming and moving seems to be very inneficient and takes quite a while to refresh. I am generating it every time I zoom, and I know this is probably not the most efficient way of doing this, and I can't seem to find code that uses another method that I understand.
These are the following methods I use, the first being an intial generation, the second being a refresh method.
private void genMandelbrot(Dimension size) {
for(int x=0;x<size.width;x++) {
for(int y=0;y<size.height;y++) {
double moveX=globalx;
double moveY=globalx;
//zoom and x/y offset.
double real = 1.5 * (x - size.width / 2) / (0.5 * zoom * size.width) + moveX;
double imaginary=(y - size.height / 2) / (0.5 * zoom * size.height) + moveY;
double newRe=0,newIm=0,oldRe=0,oldIm=0;
int i;
for(i=0;i<8000;i++) {
oldRe = newRe;
oldIm = newIm;
newRe = oldRe * oldRe - oldIm * oldIm + real;
newIm = 2 * oldRe * oldIm + imaginary;
if((newRe * newRe + newIm * newIm) > 4) break;
}
Cell c = new Cell(Color.getHSBColor(i % 256, i % 255, 255 * ((i<20)? 1:0)), new Dimension(1,1), new Point(x,y));
cells.add(c);
}
}
}
public void refreshMandelbrot(Dimension size) {
for(Cell c : cells) {
double moveX=globalx;
double moveY=globalx;
int x=c.x;
int y=c.y;
//zoom and x/y offset.
double real = 1.5 * (x - size.width / 2) / (0.5 * zoom * size.width) + moveX;
double imaginary=(y - size.height / 2) / (0.5 * zoom * size.height) + moveY;
double newRe=0,newIm=0,oldRe=0,oldIm=0;
int i;
for(i=0;i<8000;i++) {
oldRe = newRe;
oldIm = newIm;
newRe = oldRe * oldRe - oldIm * oldIm + real;
newIm = 2 * oldRe * oldIm + imaginary;
if((newRe * newRe + newIm * newIm) > 4) break;
}
cells.set(cells.indexOf(c), new Cell(Color.getHSBColor(i % 256, i % 255, 255 * ((i<20)? 1:0)), new Dimension(1,1), new Point(x,y)));
}
System.out.println("Set refreshed.");
}
I suppose that cells is some kind of List implementation?
In that case, the most time of your refresh method is spent in this line:
cells.set(cells.indexOf(c), new Cell(Color.getHSBColor(i % 256, i % 255, 255 * ((i<20)? 1:0)), new Dimension(1,1), new Point(x,y)));
More precisely in cells.indexOf(c), where the entire list is iterated to find the correct index of c.
Since you are just changing the colour of each cell, the easiest fix is to change the colour of the cell you are currently working with. I don't know the actual implementation of your Cell class, but if it had a method setColor(...), you could replace the above line with
c.setColor(Color.getHSBColor(i % 256, i % 255, 255 * ((i<20)? 1:0)));
This reduces the runtime of the refreshMandelbrot method to the same as for the genMandelbrot method.
I don't know the purpose of the Cell class, but if you are only using it as a wrapper for a colour, you might gain some more performance if you store the computed colours for each pixel in a two-dimensional array or write directly to a Graphics or Raster object instead of handling a flat list of cell wrappers.
Most likely you need to subdivide the fractal and compute the less interesting tiles less intense. 8000 repetiton is a lot. You can also simplify the calculation a bit.
I found an example online which contains a method that back propagates the error and adjusts the weights. I was wondering how this exactly works and what weight update algorithm is used. Could it be gradient descent?
/**
* all output propagate back
*
* #param expectedOutput
* first calculate the partial derivative of the error with
* respect to each of the weight leading into the output neurons
* bias is also updated here
*/
public void applyBackpropagation(double expectedOutput[]) {
// error check, normalize value ]0;1[
for (int i = 0; i < expectedOutput.length; i++) {
double d = expectedOutput[i];
if (d < 0 || d > 1) {
if (d < 0)
expectedOutput[i] = 0 + epsilon;
else
expectedOutput[i] = 1 - epsilon;
}
}
int i = 0;
for (Neuron n : outputLayer) {
ArrayList<Connection> connections = n.getAllInConnections();
for (Connection con : connections) {
double ak = n.getOutput();
double ai = con.leftNeuron.getOutput();
double desiredOutput = expectedOutput[i];
double partialDerivative = -ak * (1 - ak) * ai
* (desiredOutput - ak);
double deltaWeight = -learningRate * partialDerivative;
double newWeight = con.getWeight() + deltaWeight;
con.setDeltaWeight(deltaWeight);
con.setWeight(newWeight + momentum * con.getPrevDeltaWeight());
}
i++;
}
// update weights for the hidden layer
for (Neuron n : hiddenLayer) {
ArrayList<Connection> connections = n.getAllInConnections();
for (Connection con : connections) {
double aj = n.getOutput();
double ai = con.leftNeuron.getOutput();
double sumKoutputs = 0;
int j = 0;
for (Neuron out_neu : outputLayer) {
double wjk = out_neu.getConnection(n.id).getWeight();
double desiredOutput = (double) expectedOutput[j];
double ak = out_neu.getOutput();
j++;
sumKoutputs = sumKoutputs
+ (-(desiredOutput - ak) * ak * (1 - ak) * wjk);
}
double partialDerivative = aj * (1 - aj) * ai * sumKoutputs;
double deltaWeight = -learningRate * partialDerivative;
double newWeight = con.getWeight() + deltaWeight;
con.setDeltaWeight(deltaWeight);
con.setWeight(newWeight + momentum * con.getPrevDeltaWeight());
}
}
}
It seems to me this solution uses stochastic gradient descent. The main difference between it and the regular gradient decent is that the gradient is approximated for each example instead of calculating it for all examples and then selecting the best direction. This is the usual approach to implementing backpropagtion and even has some advantages to gradient decent(can avoid some local minima). I believe the article also exaplains what is the idea and there are also a lot of other articles that explain the main idea behind back-propagation.
This ugly looking article seems to be describing exactly the same version of the algorithm: http://www.speech.sri.com/people/anand/771/html/node37.html. I have the same formulas in my university papers, but regretfully: a) they are not available online; b) they are in language you will not understand.
As for gradient descent, the algorithm resembles gradient descent, but is not guaranteed to reach optimal position. In each step change is done over the network edges changing their values so that the training example value's probability increases.