├── .gitignore ├── Diagram.png ├── LICENSE ├── README.md ├── add_remote_Autodrive.sh ├── app ├── build.gradle ├── releasekey.jks └── src │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── pegasus │ │ │ └── bluetootharduino │ │ │ ├── AdvSettingsActivity.java │ │ │ ├── Autodrive.java │ │ │ ├── AutomaticCarDriver.java │ │ │ ├── BluetoothConnection.java │ │ │ ├── BluetoothPairing.java │ │ │ ├── CameraActivity.java │ │ │ ├── Data.java │ │ │ ├── MainActivity.java │ │ │ ├── ManualActivity.java │ │ │ ├── NativeCamResView.java │ │ │ ├── Netstrings.java │ │ │ ├── PairDeviceActivity.java │ │ │ ├── SensorData.java │ │ │ ├── Settings.java │ │ │ └── carConfiguration.java │ ├── jni │ │ ├── Application.mk │ │ ├── Autodrive │ │ │ ├── .gitignore │ │ │ ├── Diagram.png │ │ │ ├── Include │ │ │ │ ├── autodrive.hpp │ │ │ │ ├── command.hpp │ │ │ │ ├── imageprocessor │ │ │ │ │ ├── birdseyetransformer.hpp │ │ │ │ │ ├── imageprocessor.hpp │ │ │ │ │ ├── lightnormalizer.hpp │ │ │ │ │ ├── line.hpp │ │ │ │ │ ├── linefollower.hpp │ │ │ │ │ ├── roadfollower.hpp │ │ │ │ │ ├── roadline.hpp │ │ │ │ │ ├── roadlinebuilder.hpp │ │ │ │ │ └── util.hpp │ │ │ │ ├── maneuver.hpp │ │ │ │ ├── overtaking.hpp │ │ │ │ ├── parking.hpp │ │ │ │ ├── sensordata.hpp │ │ │ │ └── settings.hpp │ │ │ ├── README.md │ │ │ └── Sample │ │ │ │ ├── CMakeLists.txt │ │ │ │ ├── main.cpp │ │ │ │ ├── testdrive.mp4 │ │ │ │ └── testreal_small.mp4 │ │ └── AutodriveJavaFacade.cpp │ └── res │ │ ├── drawable-hdpi │ │ ├── rectangle_buttons.xml │ │ └── rounded_buttons.xml │ │ ├── drawable-mdpi │ │ └── ic_launcher.png │ │ ├── drawable-xhdpi │ │ └── ic_launcher.png │ │ ├── drawable-xxhdpi │ │ └── ic_launcher.png │ │ ├── drawable-xxxhdpi │ │ └── ic_launcher.png │ │ ├── layout │ │ ├── advanced_settings.xml │ │ ├── camera_activity.xml │ │ ├── custom_listview_item.xml │ │ ├── main_activity.xml │ │ ├── manual_activity.xml │ │ └── paired_devices.xml │ │ ├── menu │ │ └── main.xml │ │ ├── values-v11 │ │ └── styles.xml │ │ ├── values-v14 │ │ └── styles.xml │ │ ├── values-w820dp │ │ └── dimens.xml │ │ └── values │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── test │ └── AutodriveTest.java ├── build.gradle ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── openCVLibrary2411 ├── build.gradle └── src │ └── main │ ├── AndroidManifest.xml │ ├── aidl │ └── org │ │ └── opencv │ │ └── engine │ │ └── OpenCVEngineInterface.aidl │ ├── java │ └── org │ │ └── opencv │ │ ├── android │ │ ├── AsyncServiceHelper.java │ │ ├── BaseLoaderCallback.java │ │ ├── CameraBridgeViewBase.java │ │ ├── FpsMeter.java │ │ ├── InstallCallbackInterface.java │ │ ├── JavaCameraView.java │ │ ├── LoaderCallbackInterface.java │ │ ├── NativeCameraView.java │ │ ├── OpenCVLoader.java │ │ ├── StaticHelper.java │ │ └── Utils.java │ │ ├── calib3d │ │ ├── Calib3d.java │ │ ├── StereoBM.java │ │ └── StereoSGBM.java │ │ ├── contrib │ │ ├── Contrib.java │ │ ├── FaceRecognizer.java │ │ └── StereoVar.java │ │ ├── core │ │ ├── Algorithm.java │ │ ├── Core.java │ │ ├── CvException.java │ │ ├── CvType.java │ │ ├── Mat.java │ │ ├── MatOfByte.java │ │ ├── MatOfDMatch.java │ │ ├── MatOfDouble.java │ │ ├── MatOfFloat.java │ │ ├── MatOfFloat4.java │ │ ├── MatOfFloat6.java │ │ ├── MatOfInt.java │ │ ├── MatOfInt4.java │ │ ├── MatOfKeyPoint.java │ │ ├── MatOfPoint.java │ │ ├── MatOfPoint2f.java │ │ ├── MatOfPoint3.java │ │ ├── MatOfPoint3f.java │ │ ├── MatOfRect.java │ │ ├── Point.java │ │ ├── Point3.java │ │ ├── Range.java │ │ ├── Rect.java │ │ ├── RotatedRect.java │ │ ├── Scalar.java │ │ ├── Size.java │ │ └── TermCriteria.java │ │ ├── features2d │ │ ├── DMatch.java │ │ ├── DescriptorExtractor.java │ │ ├── DescriptorMatcher.java │ │ ├── FeatureDetector.java │ │ ├── Features2d.java │ │ ├── GenericDescriptorMatcher.java │ │ └── KeyPoint.java │ │ ├── gpu │ │ ├── DeviceInfo.java │ │ ├── Gpu.java │ │ └── TargetArchs.java │ │ ├── highgui │ │ ├── Highgui.java │ │ └── VideoCapture.java │ │ ├── imgproc │ │ ├── CLAHE.java │ │ ├── Imgproc.java │ │ ├── Moments.java │ │ └── Subdiv2D.java │ │ ├── ml │ │ ├── CvANN_MLP.java │ │ ├── CvANN_MLP_TrainParams.java │ │ ├── CvBoost.java │ │ ├── CvBoostParams.java │ │ ├── CvDTree.java │ │ ├── CvDTreeParams.java │ │ ├── CvERTrees.java │ │ ├── CvGBTrees.java │ │ ├── CvGBTreesParams.java │ │ ├── CvKNearest.java │ │ ├── CvNormalBayesClassifier.java │ │ ├── CvParamGrid.java │ │ ├── CvRTParams.java │ │ ├── CvRTrees.java │ │ ├── CvSVM.java │ │ ├── CvSVMParams.java │ │ ├── CvStatModel.java │ │ ├── EM.java │ │ └── Ml.java │ │ ├── objdetect │ │ ├── CascadeClassifier.java │ │ ├── HOGDescriptor.java │ │ └── Objdetect.java │ │ ├── photo │ │ └── Photo.java │ │ ├── utils │ │ └── Converters.java │ │ └── video │ │ ├── BackgroundSubtractor.java │ │ ├── BackgroundSubtractorMOG.java │ │ ├── BackgroundSubtractorMOG2.java │ │ ├── KalmanFilter.java │ │ └── Video.java │ └── res │ └── values │ └── attrs.xml ├── pull_Autodrive.sh ├── push_Autodrive.sh ├── push_Autodrive_forced.sh └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | #vim temp 2 | *~ 3 | *.swp 4 | *.swo 5 | 6 | #gedit backup 7 | *.orig 8 | 9 | # Gradle files 10 | .gradle/ 11 | build/ 12 | /*/build/ 13 | *.log 14 | 15 | # Local configuration file (sdk path, etc) 16 | local.properties 17 | 18 | ## Directory-based project format: 19 | .idea/ 20 | .DS_Store 21 | 22 | # IntelliJ 23 | /out/ 24 | 25 | #Build stuff 26 | /app/src/main/jni/Android.mk 27 | /app/src/main/obj 28 | /app/src/main/libs 29 | 30 | # Built application files 31 | *.apk 32 | *.ap_ 33 | 34 | # Java class files 35 | *.class 36 | 37 | ### C++ ### 38 | # Compiled Object files 39 | *.slo 40 | *.lo 41 | *.o 42 | *.obj 43 | 44 | # Precompiled Headers 45 | *.gch 46 | *.pch 47 | 48 | # Compiled Dynamic libraries 49 | *.so 50 | *.dylib 51 | *.dll 52 | 53 | # Compiled Static libraries 54 | *.lai 55 | *.la 56 | *.a 57 | *.lib 58 | 59 | #### Executables ### 60 | *.exe 61 | *.out 62 | *.app 63 | 64 | ### Eclipse ### 65 | *.pydevproject 66 | .metadata 67 | .gradle 68 | bin/ 69 | tmp/ 70 | *.tmp 71 | *.bak 72 | *.swp 73 | *~.nib 74 | local.properties 75 | .settings/ 76 | .loadpath 77 | 78 | # Eclipse Core 79 | .project 80 | 81 | # External tool builders 82 | .externalToolBuilders/ 83 | 84 | # Locally stored "Eclipse launch configurations" 85 | *.launch 86 | 87 | # CDT-specific 88 | .cproject 89 | 90 | # JDT-specific (Eclipse Java Development Tools) 91 | .classpath 92 | 93 | # PDT-specific 94 | .buildpath 95 | 96 | # sbteclipse plugin 97 | .target 98 | 99 | # TeXlipse plugin 100 | .texlipse 101 | 102 | ### Intellij ### 103 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm 104 | *.iml 105 | 106 | 107 | ### Visual studio ### 108 | 109 | # Visual studio project files 110 | *.sln 111 | *.vcxproj 112 | *.vcxproj.* 113 | 114 | 115 | # Visual C++ cache files 116 | ipch/ 117 | *.aps 118 | *.ncb 119 | *.opensdf 120 | *.sdf 121 | *.cachefile 122 | 123 | # Visual Studio profiler 124 | *.psess 125 | *.vsp 126 | *.vspx 127 | *.vsps 128 | 129 | # NuGet Packages 130 | *.nupkg 131 | **/packages.config 132 | # The packages folder can be ignored because of Package Restore 133 | **/packages/* 134 | # except build/, which is used as an MSBuild target. 135 | !**/packages/build/ 136 | # Uncomment if necessary however generally it will be regenerated when needed 137 | #!**/packages/repositories.config 138 | -------------------------------------------------------------------------------- /Diagram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Petroula/Android-Car-duino/9fe4e6916950fd38fd6d60f966d72ae733132539/Diagram.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ANDROID CAR DUINO 2 | 3 | ### Project overview 4 | [The world's first Autonomous android vehicle](https://platis.solutions/blog/2015/06/29/worlds-first-android-autonomous-vehicle/) 5 | 6 | ![Alt text](Diagram.png?raw=true "You are now looking at the android app") 7 | 8 | ###Related repos: 9 | [Autodrive](https://github.com/davidkron/Autodrive) 10 | 11 | # Building 12 | 13 | ###1. Get the android sdk 14 | 15 | Either bundled with your IDE or downloaded from [here](https://developer.android.com/sdk/index.html#Other) 16 | 17 | See \app\build.gradle for version information (subject to change). 18 | 19 | ###2. Download and extract the openCV for android sdk: 20 | [Download link](http://sourceforge.net/projects/opencvlibrary/files/opencv-android/) 21 | 22 | ###3. Download the Android-NDK 23 | [Download link](http://developer.android.com/tools/sdk/ndk/index.html) 24 | 25 | ##4. Set paths in the local.properties file 26 | 27 | * If you are using gradle from command line or terminal, you have to create the file manually. 28 | 29 | * If you are using android studio, first import the project, then add the ndk dir to the created file. 30 | 31 | The local.properties file should contain the following lines, and must NOT be added to git: 32 | ``` 33 | sdk.dir= **ANDROID SDK DIR** 34 | ndk.dir= **ANDROID NDK DIR** 35 | opencv.dir= **OPENCV ANDROID SDK DIR** 36 | ``` 37 | Windows file separators ('\') and drive letter separators (':') must be escaped ('\\\\') .The following is an example: 38 | ``` 39 | sdk.dir=F\:\\Java\\Android\\android-sdk 40 | ndk.dir=F\:\\Java\\Android\\android-ndk-r10d 41 | opencv.dir = F\:\\OpenCV\\OpenCV-android-sdk 42 | ``` 43 | 44 | ##5. If you are not using Android Studio 45 | 46 | #### Option1 building from terminal/cmd (Recommended) 47 | Run gradlew, or gradlew.bat (from cmd) if using windows. 48 | 49 | It will automatically download and extract gradle for you. 50 | 51 | You can then run: 52 | > gradlew build 53 | 54 | To build the APK's, they are generated to \app\build\outputs 55 | #### Option2 Import the gradle project to your IDE 56 | * The following topic shows how you can genarate a an ide specific project using gradle: 57 | [importing gradle project into eclipse](http://stackoverflow.com/questions/10722773/import-existing-gradle-git-project-into-eclipse-for-example-hibernate-orm) 58 | * Alternativly, check this out if you want the gradle toolchain integrated into eclipse: 59 | [Eclipse Gradle Integration](https://github.com/spring-projects/eclipse-integration-gradle/) 60 | -------------------------------------------------------------------------------- /add_remote_Autodrive.sh: -------------------------------------------------------------------------------- 1 | if ! git ls-remote --exit-code Autodrive 2 | then 3 | git remote add Autodrive https://github.com/davidkron/Autodrive.git 4 | fi -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | import com.android.build.gradle.tasks.PackageApplication 2 | import org.apache.tools.ant.taskdefs.condition.Os 3 | apply plugin: 'com.android.application' 4 | Properties properties = new Properties() 5 | properties.load(project.rootProject.file('local.properties').newDataInputStream()) 6 | def sdkDir = properties.getProperty('sdk.dir') 7 | def ndkDir = properties.getProperty('ndk.dir') 8 | def openCvDir = properties.getProperty('opencv.dir') 9 | def sources = "AutodriveJavaFacade.cpp" 10 | android { 11 | 12 | compileSdkVersion 21 13 | buildToolsVersion "21.1.2" 14 | defaultConfig { 15 | minSdkVersion 14 16 | targetSdkVersion 19 17 | versionCode 101 18 | versionName "1.0.1" 19 | applicationId "pegasus.bluetootharduino" 20 | } 21 | dependencies { 22 | testCompile 'junit:junit:4.12' 23 | } 24 | preBuild.doFirst { 25 | if (sdkDir == null) { 26 | throw new GradleException('"sdk.dir" not setup in local.properties') 27 | } 28 | 29 | if (ndkDir == null) { 30 | throw new GradleException('"ndk.dir" not setup in local.properties') 31 | } 32 | 33 | if (openCvDir == null) { 34 | throw new GradleException('"opencv.dir" not setup in local.properties') 35 | } 36 | } 37 | splits { 38 | abi { 39 | enable true // enable ABI split feature to create one APK per ABI 40 | universalApk true //generate an additional APK that targets all the ABIs 41 | } 42 | } 43 | 44 | sourceSets.main { 45 | jniLibs.srcDir 'src/main/libs' 46 | jni.srcDirs = [] //disable automatic ndk-build call 47 | } 48 | // call regular ndk-build(.cmd) script from app directory, with generated makefile using $sources and $openCvDir variable 49 | task ndkBuild(type: Exec) { 50 | 51 | def makefile = new File("${project.projectDir}/src/main/jni/Android.mk") 52 | if(!makefile.exists()){ 53 | String ANDROIDMK = "" + 54 | "LOCAL_PATH := \$(call my-dir)\n" + 55 | "LOCAL_ARM_NEON := true\n" + 56 | "include \$(CLEAR_VARS)\n" + 57 | "OPENCV_INSTALL_MODULES:=on\n" + 58 | "OPENCV_LIB_TYPE:=SHARED\n" + 59 | "OPENCV_CAMERA_MODULES:=on\n" + 60 | "include $openCvDir/sdk/native/jni/OpenCV.mk\n" + 61 | "LOCAL_MODULE := autodrive\n" + 62 | "LOCAL_SRC_FILES := $sources\n" + 63 | "LOCAL_LDLIBS += -llog -ldl -lstdc++\n" + 64 | "include \$(BUILD_SHARED_LIBRARY)" 65 | makefile.write(ANDROIDMK) 66 | } 67 | if (Os.isFamily(Os.FAMILY_WINDOWS)) { 68 | commandLine ndkDir + '/ndk-build.cmd', '-C', file('src/main').absolutePath 69 | } else { 70 | commandLine ndkDir + '/ndk-build', '-C', file('src/main').absolutePath 71 | } 72 | } 73 | 74 | signingConfigs { 75 | release { 76 | storeFile file("releasekey.jks") 77 | storePassword "password" 78 | keyAlias "releasekey" 79 | keyPassword "password" 80 | } 81 | } 82 | 83 | buildTypes { 84 | release { 85 | signingConfig signingConfigs.release 86 | } 87 | } 88 | 89 | tasks.withType(JavaCompile) { 90 | compileTask -> compileTask.dependsOn ndkBuild 91 | } 92 | } 93 | dependencies { 94 | compile project(':openCVLibrary2411') 95 | } -------------------------------------------------------------------------------- /app/releasekey.jks: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Petroula/Android-Car-duino/9fe4e6916950fd38fd6d60f966d72ae733132539/app/releasekey.jks -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 21 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /app/src/main/java/pegasus/bluetootharduino/Autodrive.java: -------------------------------------------------------------------------------- 1 | package pegasus.bluetootharduino; 2 | 3 | import org.opencv.core.Mat; 4 | 5 | /** 6 | * Created by David on 2015-04-07. 7 | */ 8 | public class Autodrive 9 | { 10 | static 11 | { 12 | System.loadLibrary("gnustl_shared"); 13 | System.loadLibrary("opencv_java"); 14 | System.loadLibrary("autodrive"); 15 | } 16 | 17 | public static native void reset(); 18 | public static native void drive(); 19 | public static native void setParkingMode(); 20 | 21 | public static native void setCarLength(int carLength); 22 | public static native int getCarLength(); 23 | 24 | public static native void resetParking(); 25 | 26 | /*----- DEBUGDATA -----*/ 27 | public static native int gapLength(); 28 | public static native int getManeuver(); 29 | public static native int getManeuverState(); 30 | 31 | public static native boolean isInitialGap(); 32 | public static native boolean isGapDepthOk(); 33 | 34 | public static native int angleTurned(); 35 | 36 | public static String maneuver() 37 | { 38 | switch(getManeuver()) 39 | { 40 | case 0: 41 | return "NO_MANEUVER"; 42 | case 1: 43 | return "PARALLEL_STANDARD"; 44 | case 2: 45 | return "PARALLEL_WIDE"; 46 | case 3: 47 | return "PERPENDICULAR_STANDARD"; 48 | default: 49 | return "ERR"; 50 | } 51 | } 52 | 53 | public static String maneuverstate() 54 | { 55 | switch(getManeuverState()) 56 | { 57 | case 0: 58 | return "NOT_MOVING"; 59 | case 1: 60 | return "FORWARD"; 61 | case 2: 62 | return "BACKWARD"; 63 | case 3: 64 | return "FORWARD_RIGHT"; 65 | case 4: 66 | return "BACKWARD_RIGHT"; 67 | case 5: 68 | return "FORWARD_LEFT"; 69 | case 6: 70 | return "BACKWARD_LEFT"; 71 | case 7: 72 | return "DONE"; 73 | default: 74 | return "ERR"; 75 | } 76 | } 77 | 78 | /*----- SENSORDATA -----*/ 79 | // getters - for debuging purposes 80 | public static native int usFront(); 81 | public static native int usFrontRight(); 82 | public static native int usRear(); 83 | public static native int irFrontRight(); 84 | public static native int irRearRight(); 85 | public static native int irRear(); 86 | public static native int gyroHeading(); 87 | public static native int razorHeading(); 88 | 89 | public static native void setImage(long matAddrRgba); 90 | public static native void setUltrasound(int sensor, int value); 91 | public static native void setInfrared(int sensor, int value); 92 | public static native void setEncoderPulses(long value); 93 | public static native void setGyroHeading(int value); 94 | public static native void setRazorHeading(int value); 95 | public static native void lineLeftFound(); 96 | public static native void lineRightFound(); 97 | 98 | /*----- RESULTING AUTODRIVE DATA -----*/ 99 | public static native boolean speedChanged(); 100 | public static native boolean angleChanged(); 101 | private static native double getTargetSpeed(); 102 | private static native double getTargetAngle(); 103 | 104 | public static int getConvertedSpeed(){ 105 | double targetSpeed = getTargetSpeed(); 106 | if(targetSpeed <= 0.0) 107 | return (int)(targetSpeed * carConfiguration.maxSpeed * 2.0); 108 | else 109 | return (int)(targetSpeed * carConfiguration.maxSpeed); 110 | } 111 | 112 | public static int getConvertedAngle(){ 113 | return (int)(getTargetAngle() * carConfiguration.maxAngle); 114 | } 115 | 116 | /*---- SETTINGS -----*/ 117 | public static native void setSettingLightNormalization(boolean on); 118 | public static native void setSettingUseLeftLine(boolean on); 119 | 120 | public static native void setLeftLane(boolean on); 121 | 122 | // N Frames to take the mean value from, value should be between 0 - 8 123 | public static native void setSettingSmoothening(int value); 124 | 125 | // Maximum vertical distance to the first pixel from carY, value should be between 15-60 126 | public static native void setSettingFirstFragmentMaxDist(int value); 127 | 128 | // How many pixels to iterate to the left, for each pixel, value should be between 1-15 129 | public static native void setSettingLeftIterationLength(int value); 130 | 131 | // How many pixels to iterate to the right, for each pixel, value should be between 1-15 132 | public static native void setSettingRightIterationLength(int value); 133 | 134 | // Every pixel in a line can not have an angle from the previous pixel that deviates more than this, 135 | // value should be between 0.4-1.4 136 | public static native void setSettingMaxAngleDiff(float value); 137 | 138 | public static native void setPIDkp(float value); 139 | 140 | public static native void setPIDki(float value); 141 | 142 | public static native void setPIDkd(float value); 143 | } 144 | -------------------------------------------------------------------------------- /app/src/main/java/pegasus/bluetootharduino/AutomaticCarDriver.java: -------------------------------------------------------------------------------- 1 | package pegasus.bluetootharduino; 2 | 3 | import org.opencv.core.Mat; 4 | import org.opencv.core.Size; 5 | import org.opencv.imgproc.Imgproc; 6 | 7 | public class AutomaticCarDriver{ 8 | 9 | AutomaticCarDriver(){ 10 | Autodrive.reset(); 11 | } 12 | 13 | public Mat processImage(Mat image) { 14 | Mat resized = new Mat(); 15 | Size prevSize = image.size(); 16 | Size size = new Size(240,135); 17 | Imgproc.resize(image, resized, size,0,0,Imgproc.INTER_NEAREST); 18 | Autodrive.setImage(resized.getNativeObjAddr()); 19 | Autodrive.drive(); 20 | BluetoothConnection.send(); 21 | if(Settings.DisplayDebugInformation) 22 | Imgproc.resize(resized, image, prevSize,0,0,Imgproc.INTER_NEAREST); 23 | 24 | return image; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/java/pegasus/bluetootharduino/BluetoothPairing.java: -------------------------------------------------------------------------------- 1 | package pegasus.bluetootharduino; 2 | 3 | import android.bluetooth.BluetoothAdapter; 4 | import android.bluetooth.BluetoothDevice; 5 | import java.util.ArrayList; 6 | import java.util.Set; 7 | 8 | public class BluetoothPairing { 9 | 10 | BluetoothAdapter adapter; 11 | static BluetoothDevice MiDevice; 12 | boolean btEnabled = true; 13 | String listNames; 14 | String listAddress; 15 | String listNamesAddress; 16 | ArrayList listDevices = new ArrayList<>(); 17 | 18 | BluetoothPairing(){ 19 | adapter = BluetoothAdapter.getDefaultAdapter(); 20 | 21 | if (adapter == null || !adapter.isEnabled()) { 22 | btEnabled = false; 23 | } 24 | } 25 | 26 | //find list of paired devices 27 | public void BTsearch() throws NullPointerException{ 28 | if(btEnabled) { 29 | Set pairedDevices = adapter.getBondedDevices(); 30 | if (pairedDevices.size() > 0) { 31 | for (BluetoothDevice device : pairedDevices) { 32 | listNames = device.getName(); 33 | listAddress = "\n" + device.getAddress(); 34 | listNamesAddress = listNames + listAddress; 35 | listDevices.add(listNamesAddress); 36 | } 37 | } 38 | } 39 | } 40 | 41 | // pair user's choice 42 | public void BTpair() { 43 | MiDevice = adapter.getRemoteDevice(PairDeviceActivity.pair); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /app/src/main/java/pegasus/bluetootharduino/Data.java: -------------------------------------------------------------------------------- 1 | package pegasus.bluetootharduino; 2 | 3 | public class Data { 4 | 5 | public String getData() { 6 | 7 | String text = "hi"; 8 | 9 | return text; 10 | } 11 | 12 | } 13 | -------------------------------------------------------------------------------- /app/src/main/java/pegasus/bluetootharduino/MainActivity.java: -------------------------------------------------------------------------------- 1 | package pegasus.bluetootharduino; 2 | 3 | import android.app.Activity; 4 | import android.os.Bundle; 5 | import android.text.Editable; 6 | import android.text.TextWatcher; 7 | import android.view.View; 8 | import android.view.View.OnClickListener; 9 | import android.content.Intent; 10 | import android.widget.CompoundButton; 11 | import android.widget.Switch; 12 | import android.widget.TextView; 13 | 14 | public class MainActivity extends Activity implements OnClickListener, CompoundButton.OnCheckedChangeListener, TextWatcher { 15 | 16 | @Override 17 | protected void onCreate(Bundle savedInstanceState) { 18 | super.onCreate(savedInstanceState); 19 | setContentView(R.layout.main_activity); 20 | 21 | /* BUTTONS */ 22 | findViewById(R.id.auto).setOnClickListener(this); 23 | findViewById(R.id.parking).setOnClickListener(this); 24 | findViewById(R.id.manual).setOnClickListener(this); 25 | findViewById(R.id.advanced).setOnClickListener(this); 26 | findViewById(R.id.bluetooth).setOnClickListener(this); 27 | 28 | //DISPLAY DEBUG INFORMATION SWITCH 29 | ((Switch)findViewById(R.id.LeftLaneSwitch)).setOnCheckedChangeListener(this); 30 | //USE LIGHT NORMALIZATION SWITCH 31 | ((Switch)findViewById(R.id.LightNormalizationSwitch)).setOnCheckedChangeListener(this); 32 | //USE LEFT LINE SWITCH 33 | ((Switch)findViewById(R.id.LeftLineSwitch)).setOnCheckedChangeListener(this); 34 | 35 | /* TEXT INPUTS */ 36 | TextView carLength = (TextView) findViewById(R.id.carLength); 37 | carLength.setText(String.valueOf(Autodrive.getCarLength())); 38 | carLength.addTextChangedListener(this); 39 | } 40 | 41 | /* BUTTONS */ 42 | @Override 43 | public void onClick(View v) { 44 | switch (v.getId()){ 45 | case R.id.manual: 46 | Intent changeToManual = new Intent(getApplicationContext(), 47 | ManualActivity.class); 48 | startActivity(changeToManual); 49 | break; 50 | case R.id.parking: 51 | Autodrive.resetParking(); // reset values used for parking 52 | Autodrive.setParkingMode(); 53 | case R.id.auto: 54 | Intent changeToCamera = new Intent(getApplicationContext(), 55 | CameraActivity.class); 56 | startActivity(changeToCamera); 57 | break; 58 | case R.id.advanced: 59 | Intent changeToSettings= new Intent(getApplicationContext(), 60 | AdvSettingsActivity.class); 61 | startActivity(changeToSettings); 62 | break; 63 | case R.id.bluetooth: 64 | Intent changeToPair= new Intent(getApplicationContext(), 65 | PairDeviceActivity.class); 66 | startActivity(changeToPair); 67 | break; 68 | } 69 | } 70 | 71 | /* SWITCHES */ 72 | @Override 73 | public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { 74 | switch (buttonView.getId()) { 75 | case R.id.LightNormalizationSwitch: 76 | Autodrive.setSettingLightNormalization(isChecked); 77 | break; 78 | case R.id.LeftLaneSwitch: 79 | Autodrive.setLeftLane(isChecked); 80 | break; 81 | case R.id.LeftLineSwitch: 82 | Autodrive.setSettingUseLeftLine(isChecked); 83 | break; 84 | } 85 | } 86 | 87 | @Override 88 | public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) { 89 | 90 | } 91 | 92 | @Override 93 | public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) { 94 | String string = charSequence.toString().trim(); 95 | if (string.equals("")) string = "0"; 96 | int integer = Integer.parseInt(string); 97 | Autodrive.setCarLength(integer); 98 | } 99 | 100 | @Override 101 | public void afterTextChanged(Editable editable) { 102 | 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /app/src/main/java/pegasus/bluetootharduino/ManualActivity.java: -------------------------------------------------------------------------------- 1 | package pegasus.bluetootharduino; 2 | 3 | import android.app.Activity; 4 | import android.content.Intent; 5 | import android.os.Bundle; 6 | import android.view.GestureDetector; 7 | import android.view.GestureDetector.OnGestureListener; 8 | import android.view.MotionEvent; 9 | import android.view.View; 10 | import android.widget.Button; 11 | import android.view.View.OnClickListener; 12 | 13 | import java.io.IOException; 14 | 15 | 16 | public class ManualActivity extends Activity implements OnClickListener, OnGestureListener { 17 | 18 | BluetoothConnection btc = new BluetoothConnection(); 19 | BluetoothPairing bluepair = new BluetoothPairing(); 20 | GestureDetector detector; 21 | 22 | @Override 23 | protected void onCreate(Bundle savedInstanceState) { 24 | super.onCreate(savedInstanceState); 25 | setContentView(R.layout.manual_activity); 26 | 27 | detector = new GestureDetector(this); 28 | 29 | ((Button)findViewById(R.id.front)).setOnClickListener(this); 30 | ((Button)findViewById(R.id.back)).setOnClickListener(this); 31 | ((Button)findViewById(R.id.right)).setOnClickListener(this); 32 | ((Button)findViewById(R.id.left)).setOnClickListener(this); 33 | ((Button)findViewById(R.id.stop)).setOnClickListener(this); 34 | 35 | if(bluepair.btEnabled) { 36 | try { 37 | btc.runBT(); 38 | } catch (IOException e) { 39 | e.printStackTrace(); 40 | } 41 | } 42 | } 43 | 44 | @Override 45 | public void onClick(View v) { 46 | switch (v.getId()){ 47 | case R.id.front: 48 | btc.sendToManualMode("front"); 49 | break; 50 | case R.id.back: 51 | btc.sendToManualMode("back"); 52 | break; 53 | case R.id.right: 54 | btc.sendToManualMode("right"); 55 | break; 56 | case R.id.left: 57 | btc.sendToManualMode("left"); 58 | break; 59 | case R.id.stop: 60 | btc.sendToManualMode("stop"); 61 | break; 62 | } 63 | } 64 | 65 | /** Uses swipe to change to the main activity*/ 66 | 67 | @Override 68 | public boolean onDown(MotionEvent e) { 69 | return false; 70 | } 71 | 72 | @Override 73 | public void onShowPress(MotionEvent e) {} 74 | 75 | @Override 76 | public boolean onSingleTapUp(MotionEvent e) { 77 | return false; 78 | } 79 | 80 | @Override 81 | public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { 82 | return false; 83 | } 84 | 85 | @Override 86 | public void onLongPress(MotionEvent e) {} 87 | 88 | @Override 89 | public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { 90 | 91 | if(e1.getX() getResolutionList() { 27 | return mCamera.getSupportedPreviewSizes(); 28 | } 29 | 30 | public void setResolution(Size resolution) { 31 | disconnectCamera(); 32 | connectCamera((int)resolution.width, (int)resolution.height); 33 | } 34 | 35 | public Size getResolution() { 36 | Size s = new Size(mCamera.get(Highgui.CV_CAP_PROP_FRAME_WIDTH),mCamera.get(Highgui.CV_CAP_PROP_FRAME_HEIGHT)); 37 | return s; 38 | } 39 | } -------------------------------------------------------------------------------- /app/src/main/java/pegasus/bluetootharduino/Netstrings.java: -------------------------------------------------------------------------------- 1 | package pegasus.bluetootharduino; 2 | 3 | public class Netstrings { 4 | 5 | Netstrings(){} 6 | 7 | String encodedNetstring(String decodedInput){ 8 | int len = decodedInput.length(); 9 | if (len == 0) return "error"; //if the input string is empty, return "error" 10 | return len + ":" + decodedInput + ","; //return a Netstring in the form of [len]":"[string]"," 11 | } 12 | 13 | String decodedNetstring(String encodedInput){ 14 | if (encodedInput.contains(",")) encodedInput = encodedInput.replace(",", ""); //remove trailing commas 15 | if (encodedInput.length() < 3) return "error1"; //if the Netstring is less than 3 characters, it's either an invalid one or contains an empty string 16 | int semicolonIndex = encodedInput.indexOf(':'); 17 | if (semicolonIndex < 0) return "error2"; // if there's no semicolon, then it's an invalid Netstring 18 | String parsedDigits = encodedInput.substring(0, semicolonIndex); //parse until the semicolon. Those should be the control digits 19 | int controlDigits = parseInt(parsedDigits); 20 | if (controlDigits < 0) return "error3"; //if the control digit is smaller than 1, then it's either not a digit or the Netstring is invalid 21 | String command = encodedInput.substring(semicolonIndex+1); //parse after the semicolon until the end of the string 22 | if (command.length() == 0) return "error4"; // if it's an empty string, return "error" 23 | if (command.trim().length() != controlDigits) return "error5"; //if string's length isn't equal with the control digits, it's an invalid Netstring 24 | return command; 25 | } 26 | 27 | private int parseInt(String string) { 28 | int parsedInteger; 29 | try { 30 | parsedInteger = Integer.parseInt(string); 31 | } catch (NumberFormatException e) { 32 | return -1; 33 | } 34 | return parsedInteger; 35 | } 36 | 37 | 38 | } 39 | -------------------------------------------------------------------------------- /app/src/main/java/pegasus/bluetootharduino/PairDeviceActivity.java: -------------------------------------------------------------------------------- 1 | package pegasus.bluetootharduino; 2 | 3 | import android.app.Activity; 4 | import android.app.AlertDialog; 5 | import android.content.DialogInterface; 6 | import android.content.Intent; 7 | import android.os.Bundle; 8 | import android.view.GestureDetector; 9 | import android.view.MotionEvent; 10 | import android.view.View; 11 | import android.widget.AdapterView; 12 | import android.widget.ArrayAdapter; 13 | import android.widget.ListView; 14 | import android.app.ProgressDialog; 15 | 16 | 17 | public class PairDeviceActivity extends Activity implements AdapterView.OnItemClickListener, GestureDetector.OnGestureListener { 18 | 19 | BluetoothPairing blue = new BluetoothPairing(); 20 | ListView pairedList; 21 | static String pair; 22 | ProgressDialog progress; 23 | GestureDetector detector; 24 | 25 | @Override 26 | protected void onCreate(Bundle savedInstanceState) { 27 | super.onCreate(savedInstanceState); 28 | setContentView(R.layout.paired_devices); 29 | 30 | detector = new GestureDetector(this); 31 | 32 | /* Show a progress dialog before presenting the results */ 33 | progress = new ProgressDialog(PairDeviceActivity.this); 34 | progress.setMessage("Fetching paired devices..."); 35 | progress.show(); 36 | 37 | pairedList = (ListView)findViewById(R.id.pairedList); 38 | 39 | blue.BTsearch(); 40 | if(!blue.btEnabled) { 41 | final AlertDialog alert = new AlertDialog.Builder(this).create(); 42 | alert.setTitle("Ooops..."); 43 | alert.setMessage("No bluetooth available..."); 44 | alert.setButton("Ok", new DialogInterface.OnClickListener() { 45 | public void onClick(DialogInterface dialog, int which) { 46 | alert.dismiss(); 47 | } 48 | }); 49 | alert.setCanceledOnTouchOutside(false); 50 | alert.show(); 51 | } 52 | ArrayAdapter listAdapter = new ArrayAdapter<>(PairDeviceActivity.this, R.layout.custom_listview_item, android.R.id.text1, blue.listDevices); 53 | 54 | if(blue.listDevices.size() > 0) { 55 | progress.dismiss(); 56 | pairedList.setAdapter(listAdapter); 57 | } else { 58 | final AlertDialog alert = new AlertDialog.Builder(this).create(); 59 | alert.setTitle("Paired Devices:"); 60 | alert.setMessage("No paired devices found..."); 61 | alert.setButton("Ok", new DialogInterface.OnClickListener() { 62 | public void onClick(DialogInterface dialog, int which) { 63 | alert.dismiss(); 64 | } 65 | }); 66 | alert.setCanceledOnTouchOutside(false); 67 | alert.show(); 68 | } 69 | pairedList.setOnItemClickListener(this); 70 | } 71 | 72 | @Override 73 | public void onItemClick(AdapterView parent, View view, int position, long id) { 74 | final AlertDialog alert = new AlertDialog.Builder(this).create(); 75 | alert.setTitle("You chose to pair with..."); 76 | pair = parent.getItemAtPosition(position).toString(); 77 | pair = pair.split("\n")[pair.split("\n").length - 1]; 78 | blue.BTpair(); 79 | alert.setMessage(parent.getItemAtPosition(position).toString().replaceAll("\n.*", "")); 80 | alert.setButton("Ok", new DialogInterface.OnClickListener() { 81 | public void onClick(DialogInterface dialog, int which) { 82 | alert.dismiss(); 83 | } 84 | }); 85 | alert.setCanceledOnTouchOutside(false); 86 | alert.show(); 87 | } 88 | 89 | /** Uses swipe to change to the main activity*/ 90 | 91 | @Override 92 | public boolean onDown(MotionEvent e) { 93 | return false; 94 | } 95 | 96 | @Override 97 | public void onShowPress(MotionEvent e) {} 98 | 99 | @Override 100 | public boolean onSingleTapUp(MotionEvent e) { 101 | return false; 102 | } 103 | 104 | @Override 105 | public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { 106 | return false; 107 | } 108 | 109 | @Override 110 | public void onLongPress(MotionEvent e) {} 111 | 112 | @Override 113 | public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { 114 | if(e1.getX() 3 | 4 | namespace Autodrive { 5 | 6 | struct command{ 7 | bool changedAngle = false; 8 | bool changedSpeed = false; 9 | double angle = 0; 10 | double speed = 0; 11 | inline void setSpeed(double newSpeed) 12 | { 13 | assert(newSpeed >= -1.0 && newSpeed <= 1.0); 14 | changedSpeed = true; 15 | speed = newSpeed; 16 | } 17 | inline void setAngle(double newAngle) 18 | { 19 | assert(newAngle >= -1.0 && newAngle <= 1.0); 20 | changedAngle = true; 21 | angle = newAngle; 22 | } 23 | }; 24 | 25 | } 26 | -------------------------------------------------------------------------------- /app/src/main/jni/Autodrive/Include/imageprocessor/imageprocessor.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | #include "../command.hpp" 6 | #include "util.hpp" 7 | #include "lightnormalizer.hpp" 8 | #include "../settings.hpp" 9 | #include "roadfollower.hpp" 10 | 11 | using namespace std; 12 | 13 | int intersection_protect = 0; 14 | 15 | #define _AUTODRIVE_DILATE 16 | 17 | #define _DEBUG 18 | 19 | #include "birdseyetransformer.hpp" 20 | 21 | namespace Autodrive 22 | { 23 | namespace imageProcessor 24 | { 25 | unique_ptr roadFollower = nullptr; 26 | 27 | cv::Mat perspective; 28 | 29 | int thresh1 = 181; 30 | int thresh2 = 71; 31 | int intensity = 110; 32 | int blur_i = 11; 33 | 34 | POINT start_center; 35 | 36 | bool init_processing(cv::Mat* mat) 37 | { 38 | auto found_pespective = find_perspective(mat); 39 | if (found_pespective) 40 | { 41 | perspective = *found_pespective; 42 | birds_eye_transform(mat, perspective); 43 | if (Settings::normalizeLightning) 44 | normalizeLightning(mat, blur_i, intensity / 100.f); 45 | cv::Mat cannied_mat; 46 | cv::Canny(*mat, cannied_mat, thresh1, thresh2, 3); 47 | roadFollower = make_unique(cannied_mat, mat->size().width / 2.f + centerDiff); 48 | return true; 49 | } else{ 50 | cv::putText(*mat, "SEARCHING FOR STRAIGHT LANES...", POINT(50.f, mat->size().height / 3.f), cv::FONT_HERSHEY_PLAIN, 1, cv::Scalar(0, 255, 0), 2); 51 | return false; 52 | } 53 | } 54 | 55 | command continue_processing(cv::Mat& mat) 56 | { 57 | birds_eye_transform(&mat, perspective); 58 | 59 | if (Settings::normalizeLightning) 60 | normalizeLightning(&mat, blur_i, intensity / 100.f); 61 | 62 | cv::Mat cannied_mat; 63 | cv::Canny(mat, cannied_mat, thresh1, thresh2, 3); 64 | 65 | /* PAINT OVER BORDER ARTEFACTS FROM TRANSFORM*/ 66 | leftImageBorder.draw(cannied_mat, cv::Scalar(0, 0, 0), Settings::transformLineRemovalThreshold); 67 | rightImageBorder.draw(cannied_mat, cv::Scalar(0, 0, 0), Settings::transformLineRemovalThreshold); 68 | 69 | command cmnd = roadFollower->update(cannied_mat, mat); 70 | float angle = Direction::FORWARD; 71 | 72 | if (cmnd.changedAngle) 73 | { 74 | //TODO: *15 really needed 75 | angle = ((90 - cmnd.angle*15)* Autodrive::Mathf::PI) / 180.f ; 76 | } 77 | 78 | //leftImageBorder.draw(mat, cv::Scalar(0, 255, 255), 8); 79 | //rightImageBorder.draw(mat, cv::Scalar(0, 255, 255), 8); 80 | 81 | POINT center(mat.size().width / 2.f, (float) mat.size().height); 82 | Autodrive::linef(center, center + POINT(std::cos(angle) * 200, -sin(angle) * 200)).draw(mat, CV_RGB(0, 250, 0)); 83 | return cmnd; 84 | } 85 | 86 | bool leftLineFound() 87 | { 88 | return roadFollower->leftLineFound(); 89 | } 90 | 91 | bool rightLineFound() 92 | { 93 | return roadFollower->rightLineFound(); 94 | } 95 | 96 | /* 97 | Returns wether the car is on the left lane 98 | Currently only works if both roadlines are found by comparing their gaps 99 | */ 100 | bool isLeftLane() 101 | { 102 | if (! leftLineFound() || ! rightLineFound()) 103 | return false; 104 | else 105 | return roadFollower->isLeftLane(); 106 | } 107 | 108 | /* 109 | Returns wether the car is on the right lane 110 | */ 111 | bool isRightLane() 112 | { 113 | if (! leftLineFound() || ! rightLineFound()) 114 | return false; 115 | else 116 | return roadFollower->isRightLane(); 117 | } 118 | 119 | int dashedLineGaps() { 120 | return roadFollower->dashedLineGaps(); 121 | } 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /app/src/main/jni/Autodrive/Include/imageprocessor/lightnormalizer.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | namespace Autodrive 6 | { 7 | inline void normalizeLightning(cv::Mat* bgr_image,int blur = 20,float intensity = 0.5f) 8 | { 9 | cv::Mat light_mat; 10 | cv::blur(*bgr_image, light_mat, cv::Size(blur, blur)); 11 | cv::cvtColor(light_mat, light_mat, CV_BGR2GRAY); 12 | 13 | cv::Mat lab_image; 14 | cv::cvtColor(*bgr_image, *bgr_image, CV_BGR2Lab); 15 | 16 | // Extract the L channel 17 | std::vector lab_planes(3); 18 | cv::split(*bgr_image, lab_planes); // now we have the L image in lab_planes[0] 19 | 20 | lab_planes[0] = lab_planes[0] - light_mat*intensity; 21 | 22 | // Merge the the color planes back into an Lab image 23 | cv::merge(lab_planes, *bgr_image); 24 | 25 | // convert back to RGB 26 | cv::cvtColor(*bgr_image, *bgr_image, CV_Lab2BGR); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /app/src/main/jni/Autodrive/Include/imageprocessor/line.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | namespace Autodrive 5 | { 6 | 7 | template 8 | struct line 9 | { 10 | using pointT = cv::Point_ < numeric_t >; 11 | using vecT = cv::Vec < numeric_t, 4 >; 12 | using limitsT = std::numeric_limits < numeric_t >; 13 | 14 | pointT begin; 15 | pointT end; 16 | numeric_t leftMost_x() 17 | { 18 | return std::min(begin.x, end.x); 19 | } 20 | numeric_t rightMost_x() 21 | { 22 | return std::max(begin.x, end.x); 23 | } 24 | 25 | line(vecT pointVector) : begin(pointT(pointVector[0], pointVector[1])), 26 | end(pointT(pointVector[2], pointVector[3])) 27 | { 28 | compute(); 29 | } 30 | 31 | line(pointT pointBegin, pointT pointEnd) : begin(pointBegin), end(pointEnd) 32 | { 33 | compute(); 34 | } 35 | 36 | line() 37 | { 38 | 39 | } 40 | 41 | void compute() 42 | { 43 | if (int(begin.x) == int(end.x)) 44 | { 45 | k = limitsT::has_infinity ? limitsT::infinity() : limitsT::max(); 46 | } else 47 | { 48 | k = (begin.y - end.y) / (begin.x - end.x); 49 | } 50 | 51 | m = begin.y - begin.x * k; 52 | } 53 | 54 | float length() 55 | { 56 | return std::sqrt(length2()); 57 | } 58 | 59 | float length2() 60 | { 61 | auto d = begin - end; 62 | return d.x *d.x + d.y *d.y; 63 | } 64 | 65 | void draw(cv::Mat& mat, cv::Scalar color = cv::Scalar(0, 255, 0), int thickness = 1) 66 | { 67 | cv::line(mat, begin, end, color, thickness); 68 | } 69 | 70 | float direction() 71 | { 72 | POINT diff = end - begin; 73 | return atan2(-diff.y, diff.x); 74 | } 75 | 76 | /* 77 | 78 | RETURNS A DIRECTION THAT IS ALLWAYS BETWEEN 0 - PI , (0-180 in degrees) 79 | 80 | */ 81 | float direction_fixed_half() 82 | { 83 | float dirr = direction(); 84 | if (dirr < 0.f) 85 | { 86 | dirr = Mathf::PI + dirr; 87 | } 88 | 89 | if (dirr > Mathf::PI) 90 | { 91 | dirr = dirr - Mathf::PI; 92 | } 93 | 94 | return dirr; 95 | } 96 | 97 | bool differs_less_than_from(line line2, numeric_t m_diff, numeric_t k_diff) 98 | { 99 | return abs(line2.k - k) < k_diff && abs(line2.m - m < m_diff); 100 | } 101 | 102 | void stretchY(numeric_t bottom, numeric_t top) 103 | { 104 | numeric_t ychangeBegin = begin.y - bottom; 105 | begin = pointT(begin.x - ychangeBegin / k, bottom); 106 | 107 | numeric_t ychangeTop = end.y - top; 108 | end = pointT(end.x - ychangeTop / k, top); 109 | } 110 | 111 | float k = 1; 112 | float m = 0; 113 | }; 114 | 115 | typedef line linei; 116 | typedef line linef; 117 | } 118 | -------------------------------------------------------------------------------- /app/src/main/jni/Autodrive/Include/imageprocessor/linefollower.hpp: -------------------------------------------------------------------------------- 1 | #include "line.hpp" 2 | #include "roadlinebuilder.hpp" 3 | #include "lightnormalizer.hpp" 4 | #include 5 | 6 | namespace Autodrive 7 | { 8 | class linefollower 9 | { 10 | RoadLine roadLine; 11 | int roadsize = 40; 12 | bool isfound = false; 13 | public: 14 | float targetRoadDistance = 0; 15 | std::unique_ptr roadBuilder = nullptr; 16 | 17 | void draw(cv::Mat* colorCopy, int centerX) 18 | { 19 | roadLine.draw(colorCopy); 20 | 21 | /* DRAW RECTANGLE FOR POSSIBLE FIRST HITS*/ 22 | POINT upperLeft = roadBuilder->last_start + POINT(-Settings::leftIterationLength, Settings::firstFragmentMaxDist); 23 | POINT lowerRight = roadBuilder->last_start + POINT(Settings::rightIterationLength, 0); 24 | cv::rectangle(*colorCopy,upperLeft , lowerRight,cv::Scalar(255,0,255)); 25 | 26 | //linef(roadBuilder->last_start, roadBuilder->last_start + POINT(8, -20)).draw(colorCopy, cv::Scalar(0, 255, 255), 1); 27 | 28 | /* DRAW VERTICAL LINE DISPLAYING DISTANCE TO ROAD AND TARGETED DISTANCE TO ROAD*/ 29 | POINT offsetX = POINT(targetRoadDistance,0); 30 | POINT bottom_center = POINT(centerX, colorCopy->size().height); 31 | linef(bottom_center + offsetX, POINT(centerX, 0) + offsetX).draw(*colorCopy); 32 | offsetX.x = roadLine.getMeanStartDistance(5); 33 | if (int(offsetX.x) != 0) 34 | linef(bottom_center + offsetX, POINT(centerX, 0) + offsetX).draw(*colorCopy, cv::Scalar(255, 255, 0)); 35 | } 36 | 37 | linefollower(const cv::Mat& cannied, POINT laneStartPoint, int center_x,int carY) 38 | { 39 | roadBuilder = make_unique(laneStartPoint, center_x,carY); 40 | 41 | roadLine = roadBuilder->build(cannied, roadsize); 42 | 43 | targetRoadDistance = roadLine.getMeanStartDistance(5); 44 | } 45 | 46 | 47 | // Prerequicite for wheter a road is found or not 48 | bool isFound() 49 | { 50 | return isfound; 51 | } 52 | 53 | float distanceDeviation() 54 | { 55 | if(!isFound()) 56 | return targetRoadDistance; 57 | float startDistance = roadLine.getMeanStartDistance(4); 58 | return (startDistance - targetRoadDistance) * 1.1f; 59 | } 60 | 61 | int totalGap() 62 | { 63 | return roadLine.totalGap / roadLine.points.size(); 64 | } 65 | 66 | optional getPreferedAngle() 67 | { 68 | if (isFound()) 69 | { 70 | /* Start by setting the target angle to the mean road angle*/ 71 | int degrees = Mathf::toDegrees(roadLine.getMeanAngle(4)) - 90; 72 | degrees = int((degrees / 65.f) * 25); 73 | degrees *= -1; 74 | 75 | degrees+=distanceDeviation(); 76 | 77 | degrees = std::min(degrees, 25); 78 | degrees = std::max(degrees, -25); 79 | return degrees; 80 | } 81 | 82 | return nullptr; 83 | } 84 | 85 | void update(cv::Mat& cannied) 86 | { 87 | roadLine = roadBuilder->build(cannied, roadsize); 88 | isfound = (roadLine.points.size() > 5 && fabs(roadLine.getMeanAngle() - Direction::FORWARD) < Mathf::PI_2); 89 | } 90 | 91 | }; 92 | 93 | } 94 | 95 | -------------------------------------------------------------------------------- /app/src/main/jni/Autodrive/Include/imageprocessor/roadline.hpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include "line.hpp" 3 | #include "util.hpp" 4 | #include 5 | #include "../settings.hpp" 6 | 7 | namespace Autodrive 8 | { 9 | struct RoadLine 10 | { 11 | std::vector points; 12 | std::vector distances; 13 | std::vector angles; 14 | std::vector angleDiffs; 15 | int totalGap = 0; 16 | 17 | int centerX; 18 | 19 | void draw(cv::Mat* drawMat) 20 | { 21 | for (unsigned int i = 0; i < points.size() - 1; i++) 22 | { 23 | linef(points[i], points[i + 1]).draw(*drawMat, cv::Scalar(255, 0, 0), 4); 24 | } 25 | } 26 | 27 | bool addPoint(POINT p) 28 | { 29 | float prevAngle = angles.back(); 30 | float newAngle = Autodrive::linef(points.back(), p).direction_fixed_half(); 31 | 32 | float mean = getMeanAngle(0); 33 | //Almost never happens - ? 34 | if (fabs(newAngle - mean) > Settings::maxAngleDiff)//Mathf::PI_2 / 2.0f) 35 | return false; 36 | 37 | angles.push_back(newAngle); 38 | angleDiffs.push_back(newAngle - prevAngle); 39 | points.push_back(p); 40 | distances.push_back(p.x - centerX); 41 | 42 | return true; 43 | } 44 | 45 | RoadLine(int center_x, POINT start_point) : centerX(center_x) 46 | { 47 | angles.push_back(Direction::FORWARD); 48 | points.push_back(start_point); 49 | angleDiffs.push_back(0); 50 | } 51 | 52 | RoadLine() 53 | {} 54 | 55 | float getMeanAngle(unsigned int lastSize = 0) 56 | { 57 | unsigned int offset = 0; 58 | if (lastSize > 0 && angles.size() > lastSize) 59 | offset = angles.size() - lastSize; 60 | return std::accumulate(angles.begin() + offset, angles.end(), 0.f) / (float) (angles.size() - offset); 61 | } 62 | 63 | float getMeanAngleDiffs(unsigned int lastSize = 0) 64 | { 65 | unsigned int offset = 0; 66 | if (lastSize > 0 && angles.size() > lastSize) 67 | offset = angles.size() - lastSize; 68 | return std::accumulate(angleDiffs.begin() + offset, angleDiffs.end(), 0.f) / ((float) angleDiffs.size() - offset); 69 | } 70 | 71 | float getEstimatedAngle(int n = 20) 72 | { 73 | return angles.back() + getMeanAngleDiffs(n); 74 | } 75 | 76 | float getMeanStartDistance(unsigned int nDistancesFromBegin) 77 | { 78 | if (distances.size() == 0) 79 | return 0; 80 | 81 | if (nDistancesFromBegin >= distances.size()) 82 | nDistancesFromBegin = int(distances.size()); 83 | return std::accumulate(distances.begin(), distances.begin() + nDistancesFromBegin, 0.f) / float(nDistancesFromBegin); 84 | } 85 | }; 86 | } 87 | -------------------------------------------------------------------------------- /app/src/main/jni/Autodrive/Include/imageprocessor/roadlinebuilder.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "line.hpp" 3 | #include "util.hpp" 4 | #include "../settings.hpp" 5 | #include "roadline.hpp" 6 | 7 | namespace Autodrive 8 | { 9 | class roadlinebuilder 10 | { 11 | static const int pointDist = 4; 12 | static const int maxDistFromStart = 22; 13 | static const int maxUpwardsIteration = 100; 14 | int totalGap = 0; 15 | int carY = 0; 16 | 17 | static SearchResult FindPoint(const cv::Mat& cannied, POINT start, float leftAngle, float rightAngle,float iterationReduction = 0) 18 | { 19 | SearchResult rightSearch = firstnonzero_direction(cannied, start, rightAngle, Settings::rightIterationLength - iterationReduction); 20 | SearchResult leftSearch = firstnonzero_direction(cannied, start, leftAngle, Settings::leftIterationLength -iterationReduction); 21 | if (leftSearch.found && rightSearch.found) 22 | { 23 | if (leftSearch.distance <= rightSearch.distance + 15) 24 | { 25 | { 26 | return leftSearch; 27 | } 28 | } else 29 | { 30 | return rightSearch; 31 | } 32 | } else if (leftSearch.found) 33 | { 34 | return leftSearch; 35 | } 36 | 37 | return rightSearch; 38 | } 39 | 40 | 41 | POINT GetFirstPoint(const cv::Mat& cannied) 42 | { 43 | SearchResult searchRes; 44 | int unfound = 0; 45 | 46 | POINT start_point = last_start; 47 | 48 | //SEARCH UPWARDS UNTIL HIT 49 | while (!searchRes.found && unfound++ < Settings::firstFragmentMaxDist) 50 | { 51 | searchRes = FindPoint(cannied, start_point, Direction::LEFT, Direction::RIGHT,Settings::iterateReduceOnStart); 52 | if (!searchRes.found) 53 | start_point.y--; 54 | } 55 | 56 | // SEARCH DOWNWARDS IF HIT ON FIRST Y AXIS 57 | SearchResult new_hit; 58 | new_hit = searchRes; 59 | int yStart = 0; 60 | while (new_hit.found && unfound == 1 && yStart++ < 5 && searchRes.point.y < carY) 61 | { 62 | new_hit.point.y++; 63 | new_hit = FindPoint(cannied, new_hit.point, Direction::LEFT, Direction::RIGHT); 64 | if (new_hit.found && int(new_hit.point.x) == int(searchRes.point.x)) 65 | { 66 | searchRes = new_hit; 67 | } else 68 | break; 69 | } 70 | 71 | if (linef(first_start, searchRes.point).length2() <= maxDistFromStart*maxDistFromStart) 72 | last_start = searchRes.point; 73 | 74 | return searchRes.point; 75 | } 76 | 77 | 78 | optional GetNextPoint(const cv::Mat& cannied, float est_angle, const POINT& prevPoint,int delta = 2) 79 | { 80 | 81 | POINT it = prevPoint; 82 | SearchResult searchResult; 83 | int unfound = 0; 84 | while (!searchResult.found && unfound < maxUpwardsIteration && it.y > 0) 85 | { 86 | it.y-=delta; 87 | it.x += cosf(est_angle)*delta; 88 | searchResult = FindPoint(cannied, it, Direction::LEFT, Direction::RIGHT); 89 | totalGap++; 90 | unfound++; 91 | } 92 | 93 | if (searchResult.found) 94 | return searchResult.point; 95 | else 96 | return nullptr; 97 | } 98 | 99 | public: 100 | POINT first_start; 101 | POINT last_start; 102 | float centerX; 103 | 104 | roadlinebuilder(POINT startPoint, float center_x,int car_y) : 105 | carY(car_y),first_start(startPoint), last_start(startPoint), centerX(center_x) 106 | { 107 | 108 | } 109 | 110 | RoadLine build(const cv::Mat& cannied, size_t maxsize) 111 | { 112 | RoadLine road(centerX, GetFirstPoint(cannied)); 113 | optional newPoint; 114 | totalGap = 0; 115 | while ((newPoint = GetNextPoint(cannied, road.getEstimatedAngle(), road.points.back(),pointDist)).valid && road.points.size() < maxsize) 116 | { 117 | if (!road.addPoint(*newPoint)) 118 | break; 119 | } 120 | road.totalGap = totalGap; 121 | 122 | return road; 123 | } 124 | }; 125 | 126 | 127 | } 128 | -------------------------------------------------------------------------------- /app/src/main/jni/Autodrive/Include/imageprocessor/util.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | namespace Autodrive 6 | { 7 | using POINT = cv::Point2f; 8 | using UINT = unsigned int; 9 | 10 | struct Mathf 11 | { 12 | static const float PI; 13 | static const float PI_2; 14 | static int toDegrees(float radians) 15 | { 16 | return int(radians * 180.f / PI); 17 | } 18 | }; 19 | 20 | const float Mathf::PI = acosf(-1); 21 | const float Mathf::PI_2 = PI / 2.f; 22 | 23 | struct Direction 24 | { 25 | static const float RIGHT; 26 | static const float LEFT; 27 | static const float FORWARD; 28 | }; 29 | 30 | const float Direction::RIGHT = 0.f; 31 | const float Direction::LEFT = Mathf::PI; 32 | const float Direction::FORWARD = Mathf::PI_2; 33 | 34 | 35 | #ifndef __ANDROID__ 36 | void show_image(cv::Mat mat, int resize, std::string wName) 37 | { 38 | cv::resize(mat, mat, mat.size() * resize);//resize image 39 | cv::imshow(wName, mat); 40 | } 41 | #endif 42 | 43 | template 44 | std::unique_ptr make_unique(Args&&... args) 45 | { 46 | return std::unique_ptr(new T(std::forward(args)...)); 47 | } 48 | 49 | template 50 | struct optional 51 | { 52 | optional(TYPE value) : valid(true), val(value) 53 | { 54 | 55 | } 56 | optional() : valid(false) 57 | { 58 | 59 | } 60 | optional(::std::nullptr_t) : valid(false) 61 | { 62 | 63 | } 64 | explicit operator bool() const 65 | { 66 | return valid; 67 | } 68 | TYPE* operator->() 69 | { 70 | return &val; 71 | } 72 | TYPE operator* () const 73 | { 74 | return val; 75 | } 76 | bool valid; 77 | private: 78 | TYPE val; 79 | }; 80 | 81 | struct SearchResult 82 | { 83 | POINT point; 84 | int distance; 85 | bool found = false; 86 | }; 87 | 88 | SearchResult firstnonzero_direction(const cv::Mat& mat, cv::Point_ < float > start, float direction, int maxDist) 89 | { 90 | SearchResult res; 91 | POINT new_pos = start + POINT(::std::cos(direction) * maxDist, -::std::sin(direction) * maxDist); 92 | cv::LineIterator it(mat, start, new_pos); 93 | for (int i = 0; i < it.count; i++, it++) 94 | { 95 | if (mat.at(it.pos())) 96 | { 97 | res.found = true; 98 | res.distance = i; 99 | res.point = it.pos(); 100 | break; 101 | } 102 | } 103 | return res; 104 | } 105 | 106 | optional firstnonzero_horizontal(const cv::Mat& mat, cv::Point iterator) 107 | { 108 | while (iterator.x < mat.size().width - 1) 109 | { 110 | if (mat.at(iterator) != 0) 111 | { 112 | return iterator; 113 | } 114 | iterator.x++; 115 | } 116 | return nullptr; 117 | } 118 | 119 | template 120 | numeric_t weighted_average(numeric_t val1, numeric_t val2, numeric_t val1_multiplier) 121 | { 122 | return (val1*val1_multiplier + val2) / (val1_multiplier + 1); 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /app/src/main/jni/Autodrive/Include/parking.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * 3 | * 4 | * - - - - -- - - Parking , parking logic for Autodrive 5 | * 6 | * 7 | */ 8 | 9 | #pragma once 10 | #include 11 | #include 12 | #include "command.hpp" 13 | #include "sensordata.hpp" 14 | #include "maneuver.hpp" 15 | 16 | namespace Autodrive { 17 | 18 | namespace Parking { 19 | 20 | maneuver currentManeuver = maneuver(NO_MANEUVER); 21 | 22 | int gapLength = 0; 23 | int gapStart = 0; 24 | bool gapDepthOk = false; 25 | 26 | bool initialGap = true; 27 | 28 | // measure the length of a gap 29 | void SetGapLength(){ 30 | if(SensorData::infrared.rearright < 1){ 31 | gapLength = SensorData::encoderDistance() - gapStart; 32 | }else{ 33 | gapStart = SensorData::encoderDistance(); 34 | } 35 | } 36 | 37 | bool GapDepthOk(){ 38 | if(SensorData::ultrasound.rear < 1 || SensorData::ultrasound.rear > 10){ 39 | return true; 40 | }else{ 41 | return false; 42 | } 43 | } 44 | 45 | void Reset(){ 46 | gapLength = 0; 47 | gapStart = 0; 48 | gapDepthOk = false; 49 | initialGap = true; 50 | currentManeuver = maneuver(NO_MANEUVER); 51 | } 52 | 53 | // the maneuver to engage depending on the size of a gap 54 | void SetParkingManeuver() { 55 | 56 | SetGapLength(); 57 | 58 | // perpendicular standard 59 | // if the gap length is between the size of the car and double the size of the car 60 | if (gapLength > (0.5 * SensorData::carLength) && gapLength < (1.1 * SensorData::carLength) && SensorData::infrared.rearright > 0) { 61 | if(initialGap){ 62 | gapLength = 0; 63 | initialGap = false; 64 | }else{ 65 | //if(GapDepthOk()){ 66 | currentManeuver = maneuver(PERPENDICULAR_STANDARD); 67 | //} 68 | } 69 | // parallel wide // this is dangerous without a front infrared 70 | // if there is enought space for the car to park front 71 | // }else if(SensorData::ultrasound.frontright < 1){ 72 | // currentManeuver = maneuver(PARALLEL_WIDE); 73 | 74 | // parallel standard 75 | // if there is not enought space for the car to park front on 76 | }else if(gapLength > (1 * SensorData::carLength) && SensorData::infrared.rearright > 0){ 77 | 78 | // workarround to avoid the initial gap 79 | if(initialGap){ 80 | gapLength = 0; 81 | initialGap = false; 82 | }else{ 83 | //if(GapDepthOk()){ 84 | currentManeuver = maneuver(PARALLEL_STANDARD); 85 | //} 86 | } 87 | 88 | // no matching maneuver 89 | }else{ 90 | if(SensorData::infrared.rearright > 0 && initialGap){ 91 | initialGap = false; 92 | } 93 | currentManeuver = maneuver(NO_MANEUVER); 94 | } 95 | } 96 | 97 | // returns the command related to the current maneuver 98 | command Park(){ 99 | return currentManeuver.GetCommand(); 100 | } 101 | } // Parking 102 | } // Autodrive 103 | 104 | -------------------------------------------------------------------------------- /app/src/main/jni/Autodrive/Include/sensordata.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | namespace Autodrive 5 | { 6 | namespace SensorData 7 | { 8 | struct ultrasound_t { 9 | int front; 10 | int frontright; 11 | int rear; 12 | } ultrasound = { 0, 0, 0 }; 13 | 14 | struct infrared_t { 15 | int frontright; 16 | int rearright; 17 | int rear; 18 | } infrared = { 0, 0, 0 }; 19 | 20 | double PULSES_PER_CM = 1; 21 | long encoderPulses = 0; 22 | 23 | int razorHeading = 0; // from -180 to 180 24 | int gyroHeading = 0; // NOT from 0 to 360 25 | 26 | bool lineLeftFound = false; 27 | bool lineRightFound = false; 28 | 29 | int currentSpeed = 0; 30 | int currentAngle = 0; 31 | 32 | int carLength = 1; 33 | 34 | void setCarLength(int length) 35 | { 36 | carLength = length; 37 | } 38 | 39 | double encoderDistance() 40 | { 41 | return encoderPulses / PULSES_PER_CM; 42 | } 43 | cv::Mat* image = 0; 44 | 45 | }; 46 | 47 | } 48 | -------------------------------------------------------------------------------- /app/src/main/jni/Autodrive/Include/settings.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | namespace Autodrive 3 | { 4 | namespace Settings 5 | { 6 | bool normalizeLightning = true; 7 | //Maximum vertical distance to the first pixel from carY 8 | int firstFragmentMaxDist = 30;//15-60 9 | //How many pixels to iterate to the left, for each pixel 10 | int leftIterationLength = 5;//1-15 11 | //How many pixels to iterate to the right, for each pixel 12 | int rightIterationLength = 6;//1-15 13 | //How many pixels of the transform border to remove from the canny 14 | int transformLineRemovalThreshold = 18; 15 | // If the middle line should be taken into consideration or not 16 | bool useLeftLine = true; 17 | // How much less to iterate right and left when finding the first point 18 | float iterateReduceOnStart = -2.f; 19 | // Every pixel in a line can not have an angle from the previous pixel that deviates more than this 20 | float maxAngleDiff = 0.7f; // 0.4 - 1.4 21 | // N Frames to take the mean value from 22 | unsigned int smoothening = 0; // 0 - 8v 23 | 24 | // PID SETTINGS 25 | float kp = 0.5; 26 | float ki = 0.0; 27 | float kd = 0.0; 28 | } 29 | } 30 | 31 | -------------------------------------------------------------------------------- /app/src/main/jni/Autodrive/README.md: -------------------------------------------------------------------------------- 1 | # AUTODRIVE 2 | Core logic of Pegasus autonomous android car 3 | 4 | ![Alt text](Diagram.png?raw=true) 5 | 6 | ### Related repos: 7 | [Android Car Duino - ANDROID APP](https://github.com/Petroula/Android-Car-duino) 8 | -------------------------------------------------------------------------------- /app/src/main/jni/Autodrive/Sample/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8) 2 | project( Sample ) 3 | SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wno-deprecated -Wall -Werror -Wextra -Wfloat-equal -Wshadow -Wpointer-arith -Wwrite-strings -Wpacked -pipe") 4 | find_package(OpenCV REQUIRED) 5 | include_directories(${OpenCV_INCLUDE_DIRS}) 6 | add_executable(Sample main.cpp) 7 | target_link_libraries( Sample ${OpenCV_LIBS} ) 8 | -------------------------------------------------------------------------------- /app/src/main/jni/Autodrive/Sample/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #define _USE_MATH_DEFINES 5 | #include 6 | 7 | #define _AUTODRIVE_SHOWCANNY 8 | #define _AUTODRIVE_SHOWHOUGH 9 | 10 | #undef _DEBUG 11 | #include "../Include/imageprocessor/imageprocessor.hpp" 12 | 13 | using namespace cv; 14 | using namespace std; 15 | 16 | int main() 17 | { 18 | std::cout<<"main"; 19 | string filename = "testreal_small.mp4"; 20 | //string filename = "vid1.mp4"; 21 | //string filename = "Test4-1.m4v"; 22 | VideoCapture capture(filename); 23 | Mat frame; 24 | 25 | if (!capture.isOpened()) 26 | throw "Error when opening test4.avi"; 27 | string window = "w"; 28 | namedWindow(window, 1); 29 | 30 | capture >> frame; 31 | while (!Autodrive::imageProcessor::init_processing(&frame)){ 32 | capture >> frame; 33 | } 34 | for (;;) 35 | { 36 | capture >> frame; 37 | if (frame.empty()){ 38 | capture.open(filename); 39 | continue; 40 | } 41 | 42 | Autodrive::imageProcessor::continue_processing(frame); 43 | 44 | Autodrive::show_image(frame, 3, "w"); 45 | waitKey(10); // waits to display frame 46 | } 47 | return 0; 48 | } 49 | -------------------------------------------------------------------------------- /app/src/main/jni/Autodrive/Sample/testdrive.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Petroula/Android-Car-duino/9fe4e6916950fd38fd6d60f966d72ae733132539/app/src/main/jni/Autodrive/Sample/testdrive.mp4 -------------------------------------------------------------------------------- /app/src/main/jni/Autodrive/Sample/testreal_small.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Petroula/Android-Car-duino/9fe4e6916950fd38fd6d60f966d72ae733132539/app/src/main/jni/Autodrive/Sample/testreal_small.mp4 -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/rectangle_buttons.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/rounded_buttons.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Petroula/Android-Car-duino/9fe4e6916950fd38fd6d60f966d72ae733132539/app/src/main/res/drawable-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Petroula/Android-Car-duino/9fe4e6916950fd38fd6d60f966d72ae733132539/app/src/main/res/drawable-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Petroula/Android-Car-duino/9fe4e6916950fd38fd6d60f966d72ae733132539/app/src/main/res/drawable-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Petroula/Android-Car-duino/9fe4e6916950fd38fd6d60f966d72ae733132539/app/src/main/res/drawable-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/layout/camera_activity.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 18 | 19 | 29 | 30 | -------------------------------------------------------------------------------- /app/src/main/res/layout/custom_listview_item.xml: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/src/main/res/layout/manual_activity.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 |