getting 'UnsatisfiedLinkError' using JNI to pass string from C++ to java w/OpenCV
up vote
0
down vote
favorite
The picture linked below shows the specific exception I'm getting. I'm not quite sure why I'm having this particular issue as I've built everything in the same directory, so the library file is there. From what I understand this has something to do with what I'm returning to my main method from my c++ function.
What I'm essentially trying to do is pass the name (printId) of the recognized person, as a string, from my c++ function to java.
Picture of command line:

Here's my C++ code:
#include <jni.h>
#include <iostream>
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/core/core.hpp"
#include "opencv2/opencv.hpp"
#include "opencv2/objdetect.hpp"
#include "opencv2/face.hpp"
#include "opencv2/face/facerec.hpp"
#include <vector>
#include <string>
#include "recognitionJNI.h"
#include <fstream>
#include <sstream>
using namespace cv;
using namespace std;
String face_cascade_name = "/Users/greg/Downloads/opencv-3.4.2/data/haarcascades/haarcascade_frontalface_alt.xml";
CascadeClassifier face_cascade;
String fn_csv = "/Users/greg/Desktop/faceBuild/faceRecognition/faceRecognition/csv.txt";
//User Defined Function for reading csv
static void read_csv(const string& filename, vector<Mat>& images, vector<int>& labels, char separator = ';') {
ifstream file(filename.c_str(), ifstream::in); //opens file for reading
if(!file) {
cout << "ERROR: There was a problem loading the csv file" << endl;
}
string line, path, classlabel;
while(getline(file,line)) {
stringstream liness(line);
getline(liness, path, separator); //read stream object up to the semicolon
getline(liness, classlabel); //read the rest of stream object up to null terminated character
//make sure that the filepath and userID are not empty
if(!path.empty() && !classlabel.empty()) {
images.push_back(imread(path,0)); //appends grayscale image to images vector
labels.push_back(atoi(classlabel.c_str())); //appends userID to labels vector
}
}
}
JNIEXPORT jstring JNICALL Java_testJNIString_userName(JNIEnv *env, jobject thisObj, jstring inJNIStr) {
const char *inCStr = env->GetStringUTFChars(inJNIStr, NULL);
if (NULL == inCStr) return NULL;
string outCppStr;
cout << "In C++, the received string is: " << inCStr << endl;
env->ReleaseStringUTFChars(inJNIStr, inCStr);
string printId;
vector<Mat> images; //This vector will hold the images
vector<int> labels; //This vector will hold the userID
//read the csv file contain image paths and userID's
try {
read_csv(fn_csv, images, labels);
} catch (Exception& e) {
cerr << "Error opening file"" << fn_csv << "". Reason: " << e.msg << endl;
exit(1);
}
//we'll need to resize the images to their origal size
//These two lines capture the length and width of the mat object
int im_width = images[0].cols;
int im_height = images[0].rows;
for(int j=0; j < images.size(); j++) {
resize(images[j],images[j],Size(im_width, im_height),1.0,1.0,INTER_CUBIC);
}
//int numComponents = 2;
//double threshold = 10.0;
//creats a faceRecognizer to train with given images
Ptr<cv::face::FisherFaceRecognizer> model = cv::face::FisherFaceRecognizer::create();
model->train(images, labels);
string camera_msg = "No camera found";
Mat webcam; // creates Mat object for to store frames
VideoCapture cap(0); // opens default webcam
if(!cap.isOpened()) {
return env->NewStringUTF(camera_msg.c_str());
}
face_cascade.load(face_cascade_name); //loads xml file into classifier
//load capture device into Mat object
while (cap.read(webcam)) {
vector<Rect> faces;
Mat frame_gray; //will be used to store grayscale copy of webcam
cvtColor(webcam, frame_gray, COLOR_BGR2GRAY); //coverts Mat object frames into grayscale
equalizeHist(frame_gray, frame_gray); //maps input distrubution to more uniform distribution
//locate the faces in the frame
face_cascade.detectMultiScale(frame_gray, faces, 1.1, 5, 0|CASCADE_SCALE_IMAGE,Size(30,30));
for(size_t i=0; i < faces.size(); i++) {
Rect face_i = faces[i]; //process faces by frame
Mat face = frame_gray(face_i); //takes the face from the live images
//resize faces for prediction
Mat face_resized;
resize(face,face_resized,Size(im_width, im_height),1.0,1.0,INTER_CUBIC);
int prediction = model->predict(face_resized); //predict based on resize faces
if(prediction == 1 ) {
printId = "Matthew";
}
else if (prediction == 2) {
printId = "Greg";
return env->NewStringUTF(printId.c_str());
}
else if(prediction != 1 || 2 ){
printId = "Unknown";
}
rectangle(webcam, face_i, CV_RGB(0,255,0), 1); //draws a rectangle around the face
string box_text = "Prediction = " + printId;
int pos_x = std::max(face_i.tl().x - 10, 0);
int pos_y = std::max(face_i.tl().y - 10, 0);
putText(webcam, box_text, Point(pos_x,pos_y), FONT_HERSHEY_PLAIN, 1.0, CV_RGB(0,255,0), 1);
}
imshow("Webcam", webcam);
waitKey(1);
destroyAllWindows();
}
return env->NewStringUTF(printId.c_str());
}
Here's my Java code:
public class recognitionJNI{
static {
System.loadLibrary("recogjni");
}
private native String userName(String msg);
public static void main(String args) {
String result = new recognitionJNI().userName("Pass arg from c++ function");
System.out.println(result);
}
}
java opencv c++11 jni
add a comment |
up vote
0
down vote
favorite
The picture linked below shows the specific exception I'm getting. I'm not quite sure why I'm having this particular issue as I've built everything in the same directory, so the library file is there. From what I understand this has something to do with what I'm returning to my main method from my c++ function.
What I'm essentially trying to do is pass the name (printId) of the recognized person, as a string, from my c++ function to java.
Picture of command line:

Here's my C++ code:
#include <jni.h>
#include <iostream>
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/core/core.hpp"
#include "opencv2/opencv.hpp"
#include "opencv2/objdetect.hpp"
#include "opencv2/face.hpp"
#include "opencv2/face/facerec.hpp"
#include <vector>
#include <string>
#include "recognitionJNI.h"
#include <fstream>
#include <sstream>
using namespace cv;
using namespace std;
String face_cascade_name = "/Users/greg/Downloads/opencv-3.4.2/data/haarcascades/haarcascade_frontalface_alt.xml";
CascadeClassifier face_cascade;
String fn_csv = "/Users/greg/Desktop/faceBuild/faceRecognition/faceRecognition/csv.txt";
//User Defined Function for reading csv
static void read_csv(const string& filename, vector<Mat>& images, vector<int>& labels, char separator = ';') {
ifstream file(filename.c_str(), ifstream::in); //opens file for reading
if(!file) {
cout << "ERROR: There was a problem loading the csv file" << endl;
}
string line, path, classlabel;
while(getline(file,line)) {
stringstream liness(line);
getline(liness, path, separator); //read stream object up to the semicolon
getline(liness, classlabel); //read the rest of stream object up to null terminated character
//make sure that the filepath and userID are not empty
if(!path.empty() && !classlabel.empty()) {
images.push_back(imread(path,0)); //appends grayscale image to images vector
labels.push_back(atoi(classlabel.c_str())); //appends userID to labels vector
}
}
}
JNIEXPORT jstring JNICALL Java_testJNIString_userName(JNIEnv *env, jobject thisObj, jstring inJNIStr) {
const char *inCStr = env->GetStringUTFChars(inJNIStr, NULL);
if (NULL == inCStr) return NULL;
string outCppStr;
cout << "In C++, the received string is: " << inCStr << endl;
env->ReleaseStringUTFChars(inJNIStr, inCStr);
string printId;
vector<Mat> images; //This vector will hold the images
vector<int> labels; //This vector will hold the userID
//read the csv file contain image paths and userID's
try {
read_csv(fn_csv, images, labels);
} catch (Exception& e) {
cerr << "Error opening file"" << fn_csv << "". Reason: " << e.msg << endl;
exit(1);
}
//we'll need to resize the images to their origal size
//These two lines capture the length and width of the mat object
int im_width = images[0].cols;
int im_height = images[0].rows;
for(int j=0; j < images.size(); j++) {
resize(images[j],images[j],Size(im_width, im_height),1.0,1.0,INTER_CUBIC);
}
//int numComponents = 2;
//double threshold = 10.0;
//creats a faceRecognizer to train with given images
Ptr<cv::face::FisherFaceRecognizer> model = cv::face::FisherFaceRecognizer::create();
model->train(images, labels);
string camera_msg = "No camera found";
Mat webcam; // creates Mat object for to store frames
VideoCapture cap(0); // opens default webcam
if(!cap.isOpened()) {
return env->NewStringUTF(camera_msg.c_str());
}
face_cascade.load(face_cascade_name); //loads xml file into classifier
//load capture device into Mat object
while (cap.read(webcam)) {
vector<Rect> faces;
Mat frame_gray; //will be used to store grayscale copy of webcam
cvtColor(webcam, frame_gray, COLOR_BGR2GRAY); //coverts Mat object frames into grayscale
equalizeHist(frame_gray, frame_gray); //maps input distrubution to more uniform distribution
//locate the faces in the frame
face_cascade.detectMultiScale(frame_gray, faces, 1.1, 5, 0|CASCADE_SCALE_IMAGE,Size(30,30));
for(size_t i=0; i < faces.size(); i++) {
Rect face_i = faces[i]; //process faces by frame
Mat face = frame_gray(face_i); //takes the face from the live images
//resize faces for prediction
Mat face_resized;
resize(face,face_resized,Size(im_width, im_height),1.0,1.0,INTER_CUBIC);
int prediction = model->predict(face_resized); //predict based on resize faces
if(prediction == 1 ) {
printId = "Matthew";
}
else if (prediction == 2) {
printId = "Greg";
return env->NewStringUTF(printId.c_str());
}
else if(prediction != 1 || 2 ){
printId = "Unknown";
}
rectangle(webcam, face_i, CV_RGB(0,255,0), 1); //draws a rectangle around the face
string box_text = "Prediction = " + printId;
int pos_x = std::max(face_i.tl().x - 10, 0);
int pos_y = std::max(face_i.tl().y - 10, 0);
putText(webcam, box_text, Point(pos_x,pos_y), FONT_HERSHEY_PLAIN, 1.0, CV_RGB(0,255,0), 1);
}
imshow("Webcam", webcam);
waitKey(1);
destroyAllWindows();
}
return env->NewStringUTF(printId.c_str());
}
Here's my Java code:
public class recognitionJNI{
static {
System.loadLibrary("recogjni");
}
private native String userName(String msg);
public static void main(String args) {
String result = new recognitionJNI().userName("Pass arg from c++ function");
System.out.println(result);
}
}
java opencv c++11 jni
add a comment |
up vote
0
down vote
favorite
up vote
0
down vote
favorite
The picture linked below shows the specific exception I'm getting. I'm not quite sure why I'm having this particular issue as I've built everything in the same directory, so the library file is there. From what I understand this has something to do with what I'm returning to my main method from my c++ function.
What I'm essentially trying to do is pass the name (printId) of the recognized person, as a string, from my c++ function to java.
Picture of command line:

Here's my C++ code:
#include <jni.h>
#include <iostream>
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/core/core.hpp"
#include "opencv2/opencv.hpp"
#include "opencv2/objdetect.hpp"
#include "opencv2/face.hpp"
#include "opencv2/face/facerec.hpp"
#include <vector>
#include <string>
#include "recognitionJNI.h"
#include <fstream>
#include <sstream>
using namespace cv;
using namespace std;
String face_cascade_name = "/Users/greg/Downloads/opencv-3.4.2/data/haarcascades/haarcascade_frontalface_alt.xml";
CascadeClassifier face_cascade;
String fn_csv = "/Users/greg/Desktop/faceBuild/faceRecognition/faceRecognition/csv.txt";
//User Defined Function for reading csv
static void read_csv(const string& filename, vector<Mat>& images, vector<int>& labels, char separator = ';') {
ifstream file(filename.c_str(), ifstream::in); //opens file for reading
if(!file) {
cout << "ERROR: There was a problem loading the csv file" << endl;
}
string line, path, classlabel;
while(getline(file,line)) {
stringstream liness(line);
getline(liness, path, separator); //read stream object up to the semicolon
getline(liness, classlabel); //read the rest of stream object up to null terminated character
//make sure that the filepath and userID are not empty
if(!path.empty() && !classlabel.empty()) {
images.push_back(imread(path,0)); //appends grayscale image to images vector
labels.push_back(atoi(classlabel.c_str())); //appends userID to labels vector
}
}
}
JNIEXPORT jstring JNICALL Java_testJNIString_userName(JNIEnv *env, jobject thisObj, jstring inJNIStr) {
const char *inCStr = env->GetStringUTFChars(inJNIStr, NULL);
if (NULL == inCStr) return NULL;
string outCppStr;
cout << "In C++, the received string is: " << inCStr << endl;
env->ReleaseStringUTFChars(inJNIStr, inCStr);
string printId;
vector<Mat> images; //This vector will hold the images
vector<int> labels; //This vector will hold the userID
//read the csv file contain image paths and userID's
try {
read_csv(fn_csv, images, labels);
} catch (Exception& e) {
cerr << "Error opening file"" << fn_csv << "". Reason: " << e.msg << endl;
exit(1);
}
//we'll need to resize the images to their origal size
//These two lines capture the length and width of the mat object
int im_width = images[0].cols;
int im_height = images[0].rows;
for(int j=0; j < images.size(); j++) {
resize(images[j],images[j],Size(im_width, im_height),1.0,1.0,INTER_CUBIC);
}
//int numComponents = 2;
//double threshold = 10.0;
//creats a faceRecognizer to train with given images
Ptr<cv::face::FisherFaceRecognizer> model = cv::face::FisherFaceRecognizer::create();
model->train(images, labels);
string camera_msg = "No camera found";
Mat webcam; // creates Mat object for to store frames
VideoCapture cap(0); // opens default webcam
if(!cap.isOpened()) {
return env->NewStringUTF(camera_msg.c_str());
}
face_cascade.load(face_cascade_name); //loads xml file into classifier
//load capture device into Mat object
while (cap.read(webcam)) {
vector<Rect> faces;
Mat frame_gray; //will be used to store grayscale copy of webcam
cvtColor(webcam, frame_gray, COLOR_BGR2GRAY); //coverts Mat object frames into grayscale
equalizeHist(frame_gray, frame_gray); //maps input distrubution to more uniform distribution
//locate the faces in the frame
face_cascade.detectMultiScale(frame_gray, faces, 1.1, 5, 0|CASCADE_SCALE_IMAGE,Size(30,30));
for(size_t i=0; i < faces.size(); i++) {
Rect face_i = faces[i]; //process faces by frame
Mat face = frame_gray(face_i); //takes the face from the live images
//resize faces for prediction
Mat face_resized;
resize(face,face_resized,Size(im_width, im_height),1.0,1.0,INTER_CUBIC);
int prediction = model->predict(face_resized); //predict based on resize faces
if(prediction == 1 ) {
printId = "Matthew";
}
else if (prediction == 2) {
printId = "Greg";
return env->NewStringUTF(printId.c_str());
}
else if(prediction != 1 || 2 ){
printId = "Unknown";
}
rectangle(webcam, face_i, CV_RGB(0,255,0), 1); //draws a rectangle around the face
string box_text = "Prediction = " + printId;
int pos_x = std::max(face_i.tl().x - 10, 0);
int pos_y = std::max(face_i.tl().y - 10, 0);
putText(webcam, box_text, Point(pos_x,pos_y), FONT_HERSHEY_PLAIN, 1.0, CV_RGB(0,255,0), 1);
}
imshow("Webcam", webcam);
waitKey(1);
destroyAllWindows();
}
return env->NewStringUTF(printId.c_str());
}
Here's my Java code:
public class recognitionJNI{
static {
System.loadLibrary("recogjni");
}
private native String userName(String msg);
public static void main(String args) {
String result = new recognitionJNI().userName("Pass arg from c++ function");
System.out.println(result);
}
}
java opencv c++11 jni
The picture linked below shows the specific exception I'm getting. I'm not quite sure why I'm having this particular issue as I've built everything in the same directory, so the library file is there. From what I understand this has something to do with what I'm returning to my main method from my c++ function.
What I'm essentially trying to do is pass the name (printId) of the recognized person, as a string, from my c++ function to java.
Picture of command line:

Here's my C++ code:
#include <jni.h>
#include <iostream>
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/core/core.hpp"
#include "opencv2/opencv.hpp"
#include "opencv2/objdetect.hpp"
#include "opencv2/face.hpp"
#include "opencv2/face/facerec.hpp"
#include <vector>
#include <string>
#include "recognitionJNI.h"
#include <fstream>
#include <sstream>
using namespace cv;
using namespace std;
String face_cascade_name = "/Users/greg/Downloads/opencv-3.4.2/data/haarcascades/haarcascade_frontalface_alt.xml";
CascadeClassifier face_cascade;
String fn_csv = "/Users/greg/Desktop/faceBuild/faceRecognition/faceRecognition/csv.txt";
//User Defined Function for reading csv
static void read_csv(const string& filename, vector<Mat>& images, vector<int>& labels, char separator = ';') {
ifstream file(filename.c_str(), ifstream::in); //opens file for reading
if(!file) {
cout << "ERROR: There was a problem loading the csv file" << endl;
}
string line, path, classlabel;
while(getline(file,line)) {
stringstream liness(line);
getline(liness, path, separator); //read stream object up to the semicolon
getline(liness, classlabel); //read the rest of stream object up to null terminated character
//make sure that the filepath and userID are not empty
if(!path.empty() && !classlabel.empty()) {
images.push_back(imread(path,0)); //appends grayscale image to images vector
labels.push_back(atoi(classlabel.c_str())); //appends userID to labels vector
}
}
}
JNIEXPORT jstring JNICALL Java_testJNIString_userName(JNIEnv *env, jobject thisObj, jstring inJNIStr) {
const char *inCStr = env->GetStringUTFChars(inJNIStr, NULL);
if (NULL == inCStr) return NULL;
string outCppStr;
cout << "In C++, the received string is: " << inCStr << endl;
env->ReleaseStringUTFChars(inJNIStr, inCStr);
string printId;
vector<Mat> images; //This vector will hold the images
vector<int> labels; //This vector will hold the userID
//read the csv file contain image paths and userID's
try {
read_csv(fn_csv, images, labels);
} catch (Exception& e) {
cerr << "Error opening file"" << fn_csv << "". Reason: " << e.msg << endl;
exit(1);
}
//we'll need to resize the images to their origal size
//These two lines capture the length and width of the mat object
int im_width = images[0].cols;
int im_height = images[0].rows;
for(int j=0; j < images.size(); j++) {
resize(images[j],images[j],Size(im_width, im_height),1.0,1.0,INTER_CUBIC);
}
//int numComponents = 2;
//double threshold = 10.0;
//creats a faceRecognizer to train with given images
Ptr<cv::face::FisherFaceRecognizer> model = cv::face::FisherFaceRecognizer::create();
model->train(images, labels);
string camera_msg = "No camera found";
Mat webcam; // creates Mat object for to store frames
VideoCapture cap(0); // opens default webcam
if(!cap.isOpened()) {
return env->NewStringUTF(camera_msg.c_str());
}
face_cascade.load(face_cascade_name); //loads xml file into classifier
//load capture device into Mat object
while (cap.read(webcam)) {
vector<Rect> faces;
Mat frame_gray; //will be used to store grayscale copy of webcam
cvtColor(webcam, frame_gray, COLOR_BGR2GRAY); //coverts Mat object frames into grayscale
equalizeHist(frame_gray, frame_gray); //maps input distrubution to more uniform distribution
//locate the faces in the frame
face_cascade.detectMultiScale(frame_gray, faces, 1.1, 5, 0|CASCADE_SCALE_IMAGE,Size(30,30));
for(size_t i=0; i < faces.size(); i++) {
Rect face_i = faces[i]; //process faces by frame
Mat face = frame_gray(face_i); //takes the face from the live images
//resize faces for prediction
Mat face_resized;
resize(face,face_resized,Size(im_width, im_height),1.0,1.0,INTER_CUBIC);
int prediction = model->predict(face_resized); //predict based on resize faces
if(prediction == 1 ) {
printId = "Matthew";
}
else if (prediction == 2) {
printId = "Greg";
return env->NewStringUTF(printId.c_str());
}
else if(prediction != 1 || 2 ){
printId = "Unknown";
}
rectangle(webcam, face_i, CV_RGB(0,255,0), 1); //draws a rectangle around the face
string box_text = "Prediction = " + printId;
int pos_x = std::max(face_i.tl().x - 10, 0);
int pos_y = std::max(face_i.tl().y - 10, 0);
putText(webcam, box_text, Point(pos_x,pos_y), FONT_HERSHEY_PLAIN, 1.0, CV_RGB(0,255,0), 1);
}
imshow("Webcam", webcam);
waitKey(1);
destroyAllWindows();
}
return env->NewStringUTF(printId.c_str());
}
Here's my Java code:
public class recognitionJNI{
static {
System.loadLibrary("recogjni");
}
private native String userName(String msg);
public static void main(String args) {
String result = new recognitionJNI().userName("Pass arg from c++ function");
System.out.println(result);
}
}
java opencv c++11 jni
java opencv c++11 jni
edited Nov 18 at 11:01
Tobias Wilfert
3891417
3891417
asked Nov 18 at 10:57
Greg
32
32
add a comment |
add a comment |
1 Answer
1
active
oldest
votes
up vote
0
down vote
accepted
Try regenerating the header file, it looks like you changed your class name in the meantime and the name is no longer up to date. The name I get from that java class is:
Java_recognitionJNI_userName
But you have
Java_testJNIString_userName
Thank you so much, that did it! I can't believe I overlooked that.
– Greg
Nov 18 at 11:36
add a comment |
1 Answer
1
active
oldest
votes
1 Answer
1
active
oldest
votes
active
oldest
votes
active
oldest
votes
up vote
0
down vote
accepted
Try regenerating the header file, it looks like you changed your class name in the meantime and the name is no longer up to date. The name I get from that java class is:
Java_recognitionJNI_userName
But you have
Java_testJNIString_userName
Thank you so much, that did it! I can't believe I overlooked that.
– Greg
Nov 18 at 11:36
add a comment |
up vote
0
down vote
accepted
Try regenerating the header file, it looks like you changed your class name in the meantime and the name is no longer up to date. The name I get from that java class is:
Java_recognitionJNI_userName
But you have
Java_testJNIString_userName
Thank you so much, that did it! I can't believe I overlooked that.
– Greg
Nov 18 at 11:36
add a comment |
up vote
0
down vote
accepted
up vote
0
down vote
accepted
Try regenerating the header file, it looks like you changed your class name in the meantime and the name is no longer up to date. The name I get from that java class is:
Java_recognitionJNI_userName
But you have
Java_testJNIString_userName
Try regenerating the header file, it looks like you changed your class name in the meantime and the name is no longer up to date. The name I get from that java class is:
Java_recognitionJNI_userName
But you have
Java_testJNIString_userName
answered Nov 18 at 11:16
Jorn Vernee
19.1k33154
19.1k33154
Thank you so much, that did it! I can't believe I overlooked that.
– Greg
Nov 18 at 11:36
add a comment |
Thank you so much, that did it! I can't believe I overlooked that.
– Greg
Nov 18 at 11:36
Thank you so much, that did it! I can't believe I overlooked that.
– Greg
Nov 18 at 11:36
Thank you so much, that did it! I can't believe I overlooked that.
– Greg
Nov 18 at 11:36
add a comment |
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53360088%2fgetting-unsatisfiedlinkerror-using-jni-to-pass-string-from-c-to-java-w-openc%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown