├── .github
├── FUNDING.yml
├── ISSUE_TEMPLATE.md
├── PULL_REQUEST_TEMPLATE.md
└── workflows
│ └── build.yml
├── .gitignore
├── .travis.yml
├── LICENSE.txt
├── README.md
├── app
├── .gitignore
├── build.gradle
├── proguard-rules.pro
├── screenshots
│ └── debug
│ │ ├── com.github.pedrovgs.deeppanel.app.ExtractPanelsActivityTest_analyzesPagesInformationForPage0.png
│ │ ├── com.github.pedrovgs.deeppanel.app.ExtractPanelsActivityTest_analyzesPagesInformationForPage1.png
│ │ ├── com.github.pedrovgs.deeppanel.app.ExtractPanelsActivityTest_analyzesPagesInformationForPage10.png
│ │ ├── com.github.pedrovgs.deeppanel.app.ExtractPanelsActivityTest_analyzesPagesInformationForPage2.png
│ │ ├── com.github.pedrovgs.deeppanel.app.ExtractPanelsActivityTest_analyzesPagesInformationForPage3.png
│ │ ├── com.github.pedrovgs.deeppanel.app.ExtractPanelsActivityTest_analyzesPagesInformationForPage4.png
│ │ ├── com.github.pedrovgs.deeppanel.app.ExtractPanelsActivityTest_analyzesPagesInformationForPage5.png
│ │ ├── com.github.pedrovgs.deeppanel.app.ExtractPanelsActivityTest_analyzesPagesInformationForPage6.png
│ │ ├── com.github.pedrovgs.deeppanel.app.ExtractPanelsActivityTest_analyzesPagesInformationForPage7.png
│ │ ├── com.github.pedrovgs.deeppanel.app.ExtractPanelsActivityTest_analyzesPagesInformationForPage8.png
│ │ ├── com.github.pedrovgs.deeppanel.app.ExtractPanelsActivityTest_analyzesPagesInformationForPage9.png
│ │ ├── com.github.pedrovgs.deeppanel.app.MainActivityTest_Panels analysis for page 0.png
│ │ ├── com.github.pedrovgs.deeppanel.app.MainActivityTest_Panels analysis for page 1.png
│ │ ├── com.github.pedrovgs.deeppanel.app.MainActivityTest_Panels analysis for page 10.png
│ │ ├── com.github.pedrovgs.deeppanel.app.MainActivityTest_Panels analysis for page 2.png
│ │ ├── com.github.pedrovgs.deeppanel.app.MainActivityTest_Panels analysis for page 3.png
│ │ ├── com.github.pedrovgs.deeppanel.app.MainActivityTest_Panels analysis for page 4.png
│ │ ├── com.github.pedrovgs.deeppanel.app.MainActivityTest_Panels analysis for page 5.png
│ │ ├── com.github.pedrovgs.deeppanel.app.MainActivityTest_Panels analysis for page 6.png
│ │ ├── com.github.pedrovgs.deeppanel.app.MainActivityTest_Panels analysis for page 7.png
│ │ ├── com.github.pedrovgs.deeppanel.app.MainActivityTest_Panels analysis for page 8.png
│ │ └── com.github.pedrovgs.deeppanel.app.MainActivityTest_Panels analysis for page 9.png
└── src
│ ├── androidTest
│ ├── AndroidManifest.xml
│ └── java
│ │ └── com
│ │ └── github
│ │ └── pedrovgs
│ │ └── deeppanel
│ │ └── app
│ │ ├── ExtractPanelsActivityTest.kt
│ │ └── MainActivityTest.kt
│ └── main
│ ├── AndroidManifest.xml
│ ├── java
│ └── com
│ │ └── github
│ │ └── pedrovgs
│ │ └── deeppanel
│ │ └── app
│ │ ├── DeepPanelApplication.kt
│ │ ├── ExtractPanelsActivity.kt
│ │ ├── FullScreenImageActivity.kt
│ │ ├── MainActivity.kt
│ │ └── Pages.kt
│ └── res
│ ├── drawable-v24
│ └── ic_launcher_foreground.xml
│ ├── drawable-xhdpi
│ ├── sample_page_0.jpg
│ ├── sample_page_1.jpg
│ ├── sample_page_10.jpg
│ ├── sample_page_2.jpg
│ ├── sample_page_3.jpg
│ ├── sample_page_4.jpg
│ ├── sample_page_5.jpg
│ ├── sample_page_6.jpg
│ ├── sample_page_7.jpg
│ ├── sample_page_8.jpg
│ └── sample_page_9.jpg
│ ├── drawable
│ └── ic_launcher_background.xml
│ ├── layout
│ ├── activity_extract_panels.xml
│ ├── activity_full_screen_image.xml
│ └── activity_main.xml
│ ├── mipmap-anydpi-v26
│ ├── ic_launcher.xml
│ └── ic_launcher_round.xml
│ ├── mipmap-hdpi
│ ├── ic_launcher.png
│ └── ic_launcher_round.png
│ ├── mipmap-mdpi
│ ├── ic_launcher.png
│ └── ic_launcher_round.png
│ ├── mipmap-xhdpi
│ ├── ic_launcher.png
│ └── ic_launcher_round.png
│ ├── mipmap-xxhdpi
│ ├── ic_launcher.png
│ └── ic_launcher_round.png
│ ├── mipmap-xxxhdpi
│ ├── ic_launcher.png
│ └── ic_launcher_round.png
│ └── values
│ ├── colors.xml
│ ├── dimens.xml
│ ├── strings.xml
│ └── styles.xml
├── art
├── panelsInfo1.png
├── panelsInfo2.png
├── rawPage1.png
├── rawPage2.png
└── screencast.gif
├── build.gradle
├── deeppanel
├── .gitignore
├── build.gradle
├── consumer-rules.pro
├── proguard-rules.pro
└── src
│ ├── main
│ ├── AndroidManifest.xml
│ ├── cpp
│ │ ├── CMakeLists.txt
│ │ ├── ConnectedComponentResult.hpp
│ │ ├── DeepPanelResult.hpp
│ │ ├── Panel.hpp
│ │ ├── connected-components.cpp
│ │ ├── connected-components.hpp
│ │ ├── deep-panel-android.cpp
│ │ ├── deep-panel.cpp
│ │ └── deep-panel.hpp
│ ├── java
│ │ └── com
│ │ │ └── github
│ │ │ └── pedrovgs
│ │ │ └── deeppanel
│ │ │ ├── Bitmaps.kt
│ │ │ ├── DeepPanel.kt
│ │ │ ├── NativeDeepPanel.java
│ │ │ ├── Performance.kt
│ │ │ └── model.kt
│ └── res
│ │ └── raw
│ │ └── model.tflite
│ └── test
│ └── java
│ └── com
│ └── github
│ └── pedrovgs
│ └── deeppanel
│ └── ConnectedComponentLabelingTest.kt
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── release.gradle
├── scripts
├── config.ini
├── create_emulator.sh
├── disable_animations.sh
├── enable_animations.sh
├── start_emulator.sh
├── stop_emulator.sh
└── wait_for_emulator.sh
└── settings.gradle
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | github: [pedrovgs]
4 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | ### Expected behaviour
2 |
3 |
4 | ### Actual behaviour
5 |
6 |
7 | ### Steps to reproduce
8 |
9 |
10 | ### Version of the app
11 |
12 |
--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | ### :pushpin: References
2 | * **Issue:** _your issue goes here_
3 | * **Related pull-requests:** _list of related pull-requests (comma-separated): #1, #2_
4 |
5 | ### :tophat: What is the goal?
6 |
7 | _Provide a description of the overall goal (you can usually copy the one from the issue)_
8 |
9 | ### How is it being implemented?
10 |
11 | _Provide a description of the implementation_
12 |
13 | ### How can it be tested?
14 |
15 | :robot:
16 | _If it cannot be tested explain why._
17 |
18 | - [ ] **Use case 1:** _A brief description of the use case that should be tested_
19 | - [ ] **Use case 2:** _If the use case requires some complex steps, increase indentation_
20 | - [ ] _Step 1_
21 | - [ ] _Step 2_
22 |
--------------------------------------------------------------------------------
/.github/workflows/build.yml:
--------------------------------------------------------------------------------
1 | name: "Build, lint, and test"
2 | on: [pull_request, push]
3 | env:
4 | GRADLE_OPTS: "-Dorg.gradle.jvmargs=-Xmx4g -Dorg.gradle.daemon=false -Dkotlin.incremental=false"
5 | jobs:
6 | test:
7 | name: Build
8 | runs-on: macos-latest
9 |
10 | steps:
11 | - uses: actions/checkout@v2
12 | - name: Set up JDK 1.8
13 | uses: actions/setup-java@v1
14 | with:
15 | java-version: 1.8
16 | - name: Start emulator
17 | run: scripts/start_emulator.sh
18 | - name: Evaluate ktlint
19 | run: ./gradlew ktlintCheck
20 | - name: Run screenshot tests tests
21 | run: ./gradlew debugExecuteScreenshotTests
22 | - uses: actions/upload-artifact@v2
23 | with:
24 | name: reports
25 | path: app/build/reports/
26 |
27 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Built application files
2 | *.apk
3 | *.ap_
4 | *.aab
5 | *output.json
6 |
7 | # Files for the ART/Dalvik VM
8 | *.dex
9 |
10 | # Java class files
11 | *.class
12 |
13 | # Generated files
14 | bin/
15 | gen/
16 | out/
17 |
18 | # Gradle files
19 | .gradle/
20 | build/
21 |
22 | # Local configuration file (sdk path, etc)
23 | local.properties
24 |
25 | # Proguard folder generated by Eclipse
26 | proguard/
27 |
28 | # Log Files
29 | *.log
30 |
31 | # Android Studio Navigation editor temp files
32 | .navigation/
33 |
34 | # Android Studio captures folder
35 | captures/
36 |
37 | # IntelliJ
38 | *.iml
39 | .idea
40 |
41 | # Keystore files
42 | # Uncomment the following lines if you do not want to check your keystore files in.
43 | #*.jks
44 | #*.keystore
45 |
46 | # External native build folder generated in Android Studio 2.2 and later
47 | .externalNativeBuild
48 |
49 | # Google Services (e.g. APIs or Firebase)
50 | # google-services.json
51 |
52 | # Freeline
53 | freeline.py
54 | freeline/
55 | freeline_project_description.json
56 |
57 | # fastlane
58 | fastlane/report.xml
59 | fastlane/Preview.html
60 | fastlane/screenshots
61 | fastlane/test_output
62 | fastlane/readme.md
63 |
64 | # Version control
65 | vcs.xml
66 |
67 | # lint
68 | lint/intermediates/
69 | lint/generated/
70 | lint/outputs/
71 | lint/tmp/
72 | # lint/reports/
73 |
74 | # Andorid SD cards
75 |
76 | sdcard.img
77 | sdcard.img.qcow2
78 |
79 | # VSCode
80 |
81 | *.project
82 | *.classpath
83 | *.settings
84 |
85 | # NDK
86 |
87 | /deeppanel/.cxx/
88 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: android
2 | sudo: required
3 | jdk:
4 | - oraclejdk8
5 |
6 | android:
7 | components:
8 | - tools
9 | - platform-tools
10 | - build-tools-29.0.3
11 | - android-22
12 | - android-29
13 | - extra-android-support
14 | - extra-google-m2repository
15 | - extra-android-m2repository
16 | - sys-img-armeabi-v7a-android-22
17 |
18 | before_script:
19 | - echo no | android create avd --force -n test -t android-22 --abi armeabi-v7a
20 | - mksdcard -l sdcard 100M sdcard
21 | - emulator -avd test -no-audio -no-window -sdcard sdcard &
22 | - android-wait-for-emulator
23 | - adb shell settings put global window_animation_scale 0 &
24 | - adb shell settings put global transition_animation_scale 0 &
25 | - adb shell settings put global animator_duration_scale 0 &
26 | - adb shell input keyevent 82 &
27 |
28 | script:
29 | - ./gradlew ktlintCheck check
30 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 |
2 | Apache License
3 | Version 2.0, January 2004
4 | http://www.apache.org/licenses/
5 |
6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
7 |
8 | 1. Definitions.
9 |
10 | "License" shall mean the terms and conditions for use, reproduction,
11 | and distribution as defined by Sections 1 through 9 of this document.
12 |
13 | "Licensor" shall mean the copyright owner or entity authorized by
14 | the copyright owner that is granting the License.
15 |
16 | "Legal Entity" shall mean the union of the acting entity and all
17 | other entities that control, are controlled by, or are under common
18 | control with that entity. For the purposes of this definition,
19 | "control" means (i) the power, direct or indirect, to cause the
20 | direction or management of such entity, whether by contract or
21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
22 | outstanding shares, or (iii) beneficial ownership of such entity.
23 |
24 | "You" (or "Your") shall mean an individual or Legal Entity
25 | exercising permissions granted by this License.
26 |
27 | "Source" form shall mean the preferred form for making modifications,
28 | including but not limited to software source code, documentation
29 | source, and configuration files.
30 |
31 | "Object" form shall mean any form resulting from mechanical
32 | transformation or translation of a Source form, including but
33 | not limited to compiled object code, generated documentation,
34 | and conversions to other media types.
35 |
36 | "Work" shall mean the work of authorship, whether in Source or
37 | Object form, made available under the License, as indicated by a
38 | copyright notice that is included in or attached to the work
39 | (an example is provided in the Appendix below).
40 |
41 | "Derivative Works" shall mean any work, whether in Source or Object
42 | form, that is based on (or derived from) the Work and for which the
43 | editorial revisions, annotations, elaborations, or other modifications
44 | represent, as a whole, an original work of authorship. For the purposes
45 | of this License, Derivative Works shall not include works that remain
46 | separable from, or merely link (or bind by name) to the interfaces of,
47 | the Work and Derivative Works thereof.
48 |
49 | "Contribution" shall mean any work of authorship, including
50 | the original version of the Work and any modifications or additions
51 | to that Work or Derivative Works thereof, that is intentionally
52 | submitted to Licensor for inclusion in the Work by the copyright owner
53 | or by an individual or Legal Entity authorized to submit on behalf of
54 | the copyright owner. For the purposes of this definition, "submitted"
55 | means any form of electronic, verbal, or written communication sent
56 | to the Licensor or its representatives, including but not limited to
57 | communication on electronic mailing lists, source code control systems,
58 | and issue tracking systems that are managed by, or on behalf of, the
59 | Licensor for the purpose of discussing and improving the Work, but
60 | excluding communication that is conspicuously marked or otherwise
61 | designated in writing by the copyright owner as "Not a Contribution."
62 |
63 | "Contributor" shall mean Licensor and any individual or Legal Entity
64 | on behalf of whom a Contribution has been received by Licensor and
65 | subsequently incorporated within the Work.
66 |
67 | 2. Grant of Copyright License. Subject to the terms and conditions of
68 | this License, each Contributor hereby grants to You a perpetual,
69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
70 | copyright license to reproduce, prepare Derivative Works of,
71 | publicly display, publicly perform, sublicense, and distribute the
72 | Work and such Derivative Works in Source or Object form.
73 |
74 | 3. Grant of Patent License. Subject to the terms and conditions of
75 | this License, each Contributor hereby grants to You a perpetual,
76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
77 | (except as stated in this section) patent license to make, have made,
78 | use, offer to sell, sell, import, and otherwise transfer the Work,
79 | where such license applies only to those patent claims licensable
80 | by such Contributor that are necessarily infringed by their
81 | Contribution(s) alone or by combination of their Contribution(s)
82 | with the Work to which such Contribution(s) was submitted. If You
83 | institute patent litigation against any entity (including a
84 | cross-claim or counterclaim in a lawsuit) alleging that the Work
85 | or a Contribution incorporated within the Work constitutes direct
86 | or contributory patent infringement, then any patent licenses
87 | granted to You under this License for that Work shall terminate
88 | as of the date such litigation is filed.
89 |
90 | 4. Redistribution. You may reproduce and distribute copies of the
91 | Work or Derivative Works thereof in any medium, with or without
92 | modifications, and in Source or Object form, provided that You
93 | meet the following conditions:
94 |
95 | (a) You must give any other recipients of the Work or
96 | Derivative Works a copy of this License; and
97 |
98 | (b) You must cause any modified files to carry prominent notices
99 | stating that You changed the files; and
100 |
101 | (c) You must retain, in the Source form of any Derivative Works
102 | that You distribute, all copyright, patent, trademark, and
103 | attribution notices from the Source form of the Work,
104 | excluding those notices that do not pertain to any part of
105 | the Derivative Works; and
106 |
107 | (d) If the Work includes a "NOTICE" text file as part of its
108 | distribution, then any Derivative Works that You distribute must
109 | include a readable copy of the attribution notices contained
110 | within such NOTICE file, excluding those notices that do not
111 | pertain to any part of the Derivative Works, in at least one
112 | of the following places: within a NOTICE text file distributed
113 | as part of the Derivative Works; within the Source form or
114 | documentation, if provided along with the Derivative Works; or,
115 | within a display generated by the Derivative Works, if and
116 | wherever such third-party notices normally appear. The contents
117 | of the NOTICE file are for informational purposes only and
118 | do not modify the License. You may add Your own attribution
119 | notices within Derivative Works that You distribute, alongside
120 | or as an addendum to the NOTICE text from the Work, provided
121 | that such additional attribution notices cannot be construed
122 | as modifying the License.
123 |
124 | You may add Your own copyright statement to Your modifications and
125 | may provide additional or different license terms and conditions
126 | for use, reproduction, or distribution of Your modifications, or
127 | for any such Derivative Works as a whole, provided Your use,
128 | reproduction, and distribution of the Work otherwise complies with
129 | the conditions stated in this License.
130 |
131 | 5. Submission of Contributions. Unless You explicitly state otherwise,
132 | any Contribution intentionally submitted for inclusion in the Work
133 | by You to the Licensor shall be under the terms and conditions of
134 | this License, without any additional terms or conditions.
135 | Notwithstanding the above, nothing herein shall supersede or modify
136 | the terms of any separate license agreement you may have executed
137 | with Licensor regarding such Contributions.
138 |
139 | 6. Trademarks. This License does not grant permission to use the trade
140 | names, trademarks, service marks, or product names of the Licensor,
141 | except as required for reasonable and customary use in describing the
142 | origin of the Work and reproducing the content of the NOTICE file.
143 |
144 | 7. Disclaimer of Warranty. Unless required by applicable law or
145 | agreed to in writing, Licensor provides the Work (and each
146 | Contributor provides its Contributions) on an "AS IS" BASIS,
147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
148 | implied, including, without limitation, any warranties or conditions
149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
150 | PARTICULAR PURPOSE. You are solely responsible for determining the
151 | appropriateness of using or redistributing the Work and assume any
152 | risks associated with Your exercise of permissions under this License.
153 |
154 | 8. Limitation of Liability. In no event and under no legal theory,
155 | whether in tort (including negligence), contract, or otherwise,
156 | unless required by applicable law (such as deliberate and grossly
157 | negligent acts) or agreed to in writing, shall any Contributor be
158 | liable to You for damages, including any direct, indirect, special,
159 | incidental, or consequential damages of any character arising as a
160 | result of this License or out of the use or inability to use the
161 | Work (including but not limited to damages for loss of goodwill,
162 | work stoppage, computer failure or malfunction, or any and all
163 | other commercial damages or losses), even if such Contributor
164 | has been advised of the possibility of such damages.
165 |
166 | 9. Accepting Warranty or Additional Liability. While redistributing
167 | the Work or Derivative Works thereof, You may choose to offer,
168 | and charge a fee for, acceptance of support, warranty, indemnity,
169 | or other liability obligations and/or rights consistent with this
170 | License. However, in accepting such obligations, You may act only
171 | on Your own behalf and on Your sole responsibility, not on behalf
172 | of any other Contributor, and only if You agree to indemnify,
173 | defend, and hold each Contributor harmless for any liability
174 | incurred by, or claims asserted against, such Contributor by reason
175 | of your accepting any such warranty or additional liability.
176 |
177 | END OF TERMS AND CONDITIONS
178 |
179 | APPENDIX: How to apply the Apache License to your work.
180 |
181 | To apply the Apache License to your work, attach the following
182 | boilerplate notice, with the fields enclosed by brackets "[]"
183 | replaced with your own identifying information. (Don't include
184 | the brackets!) The text should be enclosed in the appropriate
185 | comment syntax for the file format. We also recommend that a
186 | file or class name and description of purpose be included on the
187 | same "printed page" as the copyright notice for easier
188 | identification within third-party archives.
189 |
190 | Copyright [yyyy] [name of copyright owner]
191 |
192 | Licensed under the Apache License, Version 2.0 (the "License");
193 | you may not use this file except in compliance with the License.
194 | You may obtain a copy of the License at
195 |
196 | http://www.apache.org/licenses/LICENSE-2.0
197 |
198 | Unless required by applicable law or agreed to in writing, software
199 | distributed under the License is distributed on an "AS IS" BASIS,
200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
201 | See the License for the specific language governing permissions and
202 | limitations under the License.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | DeepPanelAndroid 
2 | ==============
3 |
4 | Android library used to implement **comic vignettes segmentation using a machine learning method named deep learning**.
5 |
6 | DeepPanel let's you extract all the panels' location from a comic page **using a machine learning model trained [here](https://github.com/pedrovgs/DeepPanel). This Andorid library does not use Open CV at all, this means the size of the final app you generates will be as small as possible. We've optimized our model in terms of size and performance and implemented the computer vision algorithms we needed without any third party library to optimize the final library size and performance**. Powered by [TensorFlow lite](https://www.tensorflow.org/lite), our already trained panels' segmentation model and some native code, DeepPanel is able to find all the panels' location in less than a second.
7 |
8 |
9 |
10 | Keep in mind, **this artificial intelligence solution is designed to simulate the human reading behavior and it will group panels that are related as a human would do while reading a comic book page.** When analyzing a page we do not use image processing algorithms in order to find the panels. Our solution is based on deep learning. We do not use contour detection algorithms at all even when the samples analysis can look similar. If you are looking for a panels contouring solution you can use Open CV or any other image analysis tools like [this project](https://github.com/njean42/kumiko) does.
11 |
12 |
13 | | Original | Analyzed |
14 | | ----------------|--------------------|
15 | |||
16 | |||
17 |
18 | In case you are looking for the iOS version of this project, you can find it [here](https://github.com/pedrovgs/DeepPaneliOS).
19 |
20 | All the pages used in our sample project are created by [Peper & Carrot](https://www.peppercarrot.com) which is a free comic book you should read right now :heart:
21 |
22 | Usage
23 | -----
24 |
25 | ### Dependency
26 |
27 | Include the library in your ``build.gradle``
28 |
29 | ```groovy
30 | dependencies{
31 | implementation 'com.github.pedrovgs:deeppanel:0.0.1'
32 | }
33 | ```
34 |
35 | To start using the library you just need to call `DeepPanel.initialize` with a valid `Context`. You can initialize this library from any ``Activity`` or from your ``Application`` class:
36 |
37 | ```kotlin
38 | class MyApplication : Application() {
39 | override fun onCreate() {
40 | ....
41 | DeepPanel.initialize(this)
42 | ....
43 | }
44 | }
45 | ```
46 |
47 | Once you've initialized the library get any comic book page, transform it into a ``Bitmap`` instance and extract the panels' information using ``extractPanelsInfo`` method as follows:
48 |
49 | ```kotlin
50 | val deepPanel = DeepPanel()
51 | val result = deepPanel.extractPanelsInfo(bitmapSamplePage)
52 | result.panels.panelsInfo.forEach { panel ->
53 | Log.d("DeepPanel", """Left: ${panel.left}, Top: ${panel.top}
54 | |Right: ${panel.right}, Bottom: ${panel.bottom}
55 | |Width: ${panel.width}, Height: ${panel.height}
56 | """.trimMargin())
57 | }
58 | ```
59 |
60 | ``PredictionResult`` contains a list of panels with the position of every panel on the page and also a 2d matrix of integers with the following labels inside:
61 |
62 | * 0 - Label associated to the page of the content.
63 | * N - Content related to the same panel on the page.
64 |
65 | Do not invoke DeepPanel ``extractPanelsInfo`` from the main app thread. Even when we can analyze a page in less than 400ms using a Pixel 4 as a reference device, you should not block your app UI at all. Our recommendation is to extract the analysis computation out of the UI thread using any threading mechanism.
66 |
67 | Keep in mind for the first version of this library we consider every panel as a rectangle even if the content panel inside has a different shape. We will improve this in the future, for our very first release we've decided to use this approach because we will always represent the information of the panel on a rectangular screen.
68 |
69 | ### Do you want to contribute?
70 |
71 | Feel free to contribute, we will be glad to improve this project with your help. Keep in mind that your PRs must be validated by GitHub actions before being reviewed by any core contributor.
72 |
73 | ### Acknowledgement
74 |
75 | Special thanks to [Asun](https://github.com/asuncionjc), who never doubt we could publish this project :heart:
76 |
77 | Developed By
78 | ------------
79 |
80 | * Pedro Vicente Gómez Sánchez -
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 | License
90 | -------
91 |
92 | Copyright 2020 Pedro Vicente Gómez Sánchez
93 |
94 | Licensed under the Apache License, Version 2.0 (the "License");
95 | you may not use this file except in compliance with the License.
96 | You may obtain a copy of the License at
97 |
98 | http://www.apache.org/licenses/LICENSE-2.0
99 |
100 | Unless required by applicable law or agreed to in writing, software
101 | distributed under the License is distributed on an "AS IS" BASIS,
102 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
103 | See the License for the specific language governing permissions and
104 | limitations under the License.
105 |
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: "com.android.application"
2 | apply plugin: "kotlin-android"
3 | apply plugin: "kotlin-android-extensions"
4 | apply plugin: "shot"
5 |
6 | android {
7 | compileSdkVersion 29
8 | buildToolsVersion "29.0.3"
9 |
10 | defaultConfig {
11 | applicationId "com.github.pedrovgs.deeppanel.app"
12 | minSdkVersion 21
13 | targetSdkVersion 29
14 | versionCode 1
15 | versionName "1.0"
16 |
17 | testInstrumentationRunner "com.karumi.shot.ShotTestRunner"
18 | }
19 |
20 | buildTypes {
21 | release {
22 | minifyEnabled false
23 | proguardFiles getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro"
24 | }
25 | }
26 | compileOptions {
27 | sourceCompatibility JavaVersion.VERSION_1_8
28 | targetCompatibility JavaVersion.VERSION_1_8
29 | }
30 | kotlinOptions {
31 | jvmTarget = "1.8"
32 | }
33 | }
34 |
35 | dependencies {
36 | implementation fileTree(dir: "libs", include: ["*.jar"])
37 | implementation project(":deeppanel")
38 | implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
39 | implementation "androidx.core:core-ktx:1.3.1"
40 | implementation "androidx.appcompat:appcompat:1.2.0"
41 | implementation "com.google.android.material:material:1.2.1"
42 | implementation "androidx.constraintlayout:constraintlayout:2.0.1"
43 |
44 | testImplementation "junit:junit:4.13"
45 |
46 | androidTestImplementation "androidx.test:runner:1.3.0"
47 | androidTestImplementation "androidx.test.espresso:espresso-core:3.3.0"
48 | androidTestImplementation "androidx.test.espresso:espresso-intents:3.3.0"
49 | androidTestImplementation "androidx.test.espresso:espresso-contrib:3.3.0"
50 | androidTestImplementation "androidx.test:rules:1.3.0"
51 | androidTestImplementation "androidx.test.ext:junit:1.1.2"
52 | }
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
--------------------------------------------------------------------------------
/app/screenshots/debug/com.github.pedrovgs.deeppanel.app.ExtractPanelsActivityTest_analyzesPagesInformationForPage0.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pedrovgs/DeepPanelAndroid/75133803b9615cc37f744f998459d883789fd247/app/screenshots/debug/com.github.pedrovgs.deeppanel.app.ExtractPanelsActivityTest_analyzesPagesInformationForPage0.png
--------------------------------------------------------------------------------
/app/screenshots/debug/com.github.pedrovgs.deeppanel.app.ExtractPanelsActivityTest_analyzesPagesInformationForPage1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pedrovgs/DeepPanelAndroid/75133803b9615cc37f744f998459d883789fd247/app/screenshots/debug/com.github.pedrovgs.deeppanel.app.ExtractPanelsActivityTest_analyzesPagesInformationForPage1.png
--------------------------------------------------------------------------------
/app/screenshots/debug/com.github.pedrovgs.deeppanel.app.ExtractPanelsActivityTest_analyzesPagesInformationForPage10.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pedrovgs/DeepPanelAndroid/75133803b9615cc37f744f998459d883789fd247/app/screenshots/debug/com.github.pedrovgs.deeppanel.app.ExtractPanelsActivityTest_analyzesPagesInformationForPage10.png
--------------------------------------------------------------------------------
/app/screenshots/debug/com.github.pedrovgs.deeppanel.app.ExtractPanelsActivityTest_analyzesPagesInformationForPage2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pedrovgs/DeepPanelAndroid/75133803b9615cc37f744f998459d883789fd247/app/screenshots/debug/com.github.pedrovgs.deeppanel.app.ExtractPanelsActivityTest_analyzesPagesInformationForPage2.png
--------------------------------------------------------------------------------
/app/screenshots/debug/com.github.pedrovgs.deeppanel.app.ExtractPanelsActivityTest_analyzesPagesInformationForPage3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pedrovgs/DeepPanelAndroid/75133803b9615cc37f744f998459d883789fd247/app/screenshots/debug/com.github.pedrovgs.deeppanel.app.ExtractPanelsActivityTest_analyzesPagesInformationForPage3.png
--------------------------------------------------------------------------------
/app/screenshots/debug/com.github.pedrovgs.deeppanel.app.ExtractPanelsActivityTest_analyzesPagesInformationForPage4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pedrovgs/DeepPanelAndroid/75133803b9615cc37f744f998459d883789fd247/app/screenshots/debug/com.github.pedrovgs.deeppanel.app.ExtractPanelsActivityTest_analyzesPagesInformationForPage4.png
--------------------------------------------------------------------------------
/app/screenshots/debug/com.github.pedrovgs.deeppanel.app.ExtractPanelsActivityTest_analyzesPagesInformationForPage5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pedrovgs/DeepPanelAndroid/75133803b9615cc37f744f998459d883789fd247/app/screenshots/debug/com.github.pedrovgs.deeppanel.app.ExtractPanelsActivityTest_analyzesPagesInformationForPage5.png
--------------------------------------------------------------------------------
/app/screenshots/debug/com.github.pedrovgs.deeppanel.app.ExtractPanelsActivityTest_analyzesPagesInformationForPage6.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pedrovgs/DeepPanelAndroid/75133803b9615cc37f744f998459d883789fd247/app/screenshots/debug/com.github.pedrovgs.deeppanel.app.ExtractPanelsActivityTest_analyzesPagesInformationForPage6.png
--------------------------------------------------------------------------------
/app/screenshots/debug/com.github.pedrovgs.deeppanel.app.ExtractPanelsActivityTest_analyzesPagesInformationForPage7.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pedrovgs/DeepPanelAndroid/75133803b9615cc37f744f998459d883789fd247/app/screenshots/debug/com.github.pedrovgs.deeppanel.app.ExtractPanelsActivityTest_analyzesPagesInformationForPage7.png
--------------------------------------------------------------------------------
/app/screenshots/debug/com.github.pedrovgs.deeppanel.app.ExtractPanelsActivityTest_analyzesPagesInformationForPage8.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pedrovgs/DeepPanelAndroid/75133803b9615cc37f744f998459d883789fd247/app/screenshots/debug/com.github.pedrovgs.deeppanel.app.ExtractPanelsActivityTest_analyzesPagesInformationForPage8.png
--------------------------------------------------------------------------------
/app/screenshots/debug/com.github.pedrovgs.deeppanel.app.ExtractPanelsActivityTest_analyzesPagesInformationForPage9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pedrovgs/DeepPanelAndroid/75133803b9615cc37f744f998459d883789fd247/app/screenshots/debug/com.github.pedrovgs.deeppanel.app.ExtractPanelsActivityTest_analyzesPagesInformationForPage9.png
--------------------------------------------------------------------------------
/app/screenshots/debug/com.github.pedrovgs.deeppanel.app.MainActivityTest_Panels analysis for page 0.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pedrovgs/DeepPanelAndroid/75133803b9615cc37f744f998459d883789fd247/app/screenshots/debug/com.github.pedrovgs.deeppanel.app.MainActivityTest_Panels analysis for page 0.png
--------------------------------------------------------------------------------
/app/screenshots/debug/com.github.pedrovgs.deeppanel.app.MainActivityTest_Panels analysis for page 1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pedrovgs/DeepPanelAndroid/75133803b9615cc37f744f998459d883789fd247/app/screenshots/debug/com.github.pedrovgs.deeppanel.app.MainActivityTest_Panels analysis for page 1.png
--------------------------------------------------------------------------------
/app/screenshots/debug/com.github.pedrovgs.deeppanel.app.MainActivityTest_Panels analysis for page 10.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pedrovgs/DeepPanelAndroid/75133803b9615cc37f744f998459d883789fd247/app/screenshots/debug/com.github.pedrovgs.deeppanel.app.MainActivityTest_Panels analysis for page 10.png
--------------------------------------------------------------------------------
/app/screenshots/debug/com.github.pedrovgs.deeppanel.app.MainActivityTest_Panels analysis for page 2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pedrovgs/DeepPanelAndroid/75133803b9615cc37f744f998459d883789fd247/app/screenshots/debug/com.github.pedrovgs.deeppanel.app.MainActivityTest_Panels analysis for page 2.png
--------------------------------------------------------------------------------
/app/screenshots/debug/com.github.pedrovgs.deeppanel.app.MainActivityTest_Panels analysis for page 3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pedrovgs/DeepPanelAndroid/75133803b9615cc37f744f998459d883789fd247/app/screenshots/debug/com.github.pedrovgs.deeppanel.app.MainActivityTest_Panels analysis for page 3.png
--------------------------------------------------------------------------------
/app/screenshots/debug/com.github.pedrovgs.deeppanel.app.MainActivityTest_Panels analysis for page 4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pedrovgs/DeepPanelAndroid/75133803b9615cc37f744f998459d883789fd247/app/screenshots/debug/com.github.pedrovgs.deeppanel.app.MainActivityTest_Panels analysis for page 4.png
--------------------------------------------------------------------------------
/app/screenshots/debug/com.github.pedrovgs.deeppanel.app.MainActivityTest_Panels analysis for page 5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pedrovgs/DeepPanelAndroid/75133803b9615cc37f744f998459d883789fd247/app/screenshots/debug/com.github.pedrovgs.deeppanel.app.MainActivityTest_Panels analysis for page 5.png
--------------------------------------------------------------------------------
/app/screenshots/debug/com.github.pedrovgs.deeppanel.app.MainActivityTest_Panels analysis for page 6.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pedrovgs/DeepPanelAndroid/75133803b9615cc37f744f998459d883789fd247/app/screenshots/debug/com.github.pedrovgs.deeppanel.app.MainActivityTest_Panels analysis for page 6.png
--------------------------------------------------------------------------------
/app/screenshots/debug/com.github.pedrovgs.deeppanel.app.MainActivityTest_Panels analysis for page 7.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pedrovgs/DeepPanelAndroid/75133803b9615cc37f744f998459d883789fd247/app/screenshots/debug/com.github.pedrovgs.deeppanel.app.MainActivityTest_Panels analysis for page 7.png
--------------------------------------------------------------------------------
/app/screenshots/debug/com.github.pedrovgs.deeppanel.app.MainActivityTest_Panels analysis for page 8.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pedrovgs/DeepPanelAndroid/75133803b9615cc37f744f998459d883789fd247/app/screenshots/debug/com.github.pedrovgs.deeppanel.app.MainActivityTest_Panels analysis for page 8.png
--------------------------------------------------------------------------------
/app/screenshots/debug/com.github.pedrovgs.deeppanel.app.MainActivityTest_Panels analysis for page 9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pedrovgs/DeepPanelAndroid/75133803b9615cc37f744f998459d883789fd247/app/screenshots/debug/com.github.pedrovgs.deeppanel.app.MainActivityTest_Panels analysis for page 9.png
--------------------------------------------------------------------------------
/app/src/androidTest/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/github/pedrovgs/deeppanel/app/ExtractPanelsActivityTest.kt:
--------------------------------------------------------------------------------
1 | package com.github.pedrovgs.deeppanel.app
2 |
3 | import android.content.Intent
4 | import android.view.View
5 | import androidx.test.core.app.ActivityScenario
6 | import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
7 | import com.github.pedrovgs.deeppanel.app.ExtractPanelsActivity.Companion.resource_id_extra
8 | import com.karumi.shot.ScreenshotTest
9 | import com.karumi.shot.waitForActivity
10 | import org.junit.After
11 | import org.junit.Test
12 |
13 | class ExtractPanelsActivityTest : ScreenshotTest {
14 |
15 | private lateinit var scenario: ActivityScenario
16 |
17 | @After
18 | fun tearDown() {
19 | scenario.close()
20 | }
21 |
22 | @Test
23 | fun analyzesPagesInformationForPage0() {
24 | val activity = startActivity(R.drawable.sample_page_0)
25 |
26 | compareExtractPanelsActivityScreenshot(activity)
27 | }
28 |
29 | @Test
30 | fun analyzesPagesInformationForPage1() {
31 | val activity = startActivity(R.drawable.sample_page_1)
32 |
33 | compareExtractPanelsActivityScreenshot(activity)
34 | }
35 |
36 | @Test
37 | fun analyzesPagesInformationForPage2() {
38 | val activity = startActivity(R.drawable.sample_page_2)
39 |
40 | compareExtractPanelsActivityScreenshot(activity)
41 | }
42 |
43 | @Test
44 | fun analyzesPagesInformationForPage3() {
45 | val activity = startActivity(R.drawable.sample_page_3)
46 |
47 | compareExtractPanelsActivityScreenshot(activity)
48 | }
49 |
50 | @Test
51 | fun analyzesPagesInformationForPage4() {
52 | val activity = startActivity(R.drawable.sample_page_4)
53 |
54 | compareExtractPanelsActivityScreenshot(activity)
55 | }
56 |
57 | @Test
58 | fun analyzesPagesInformationForPage5() {
59 | val activity = startActivity(R.drawable.sample_page_5)
60 |
61 | compareExtractPanelsActivityScreenshot(activity)
62 | }
63 |
64 | @Test
65 | fun analyzesPagesInformationForPage6() {
66 | val activity = startActivity(R.drawable.sample_page_6)
67 |
68 | compareExtractPanelsActivityScreenshot(activity)
69 | }
70 |
71 | @Test
72 | fun analyzesPagesInformationForPage7() {
73 | val activity = startActivity(R.drawable.sample_page_7)
74 |
75 | compareExtractPanelsActivityScreenshot(activity)
76 | }
77 |
78 | @Test
79 | fun analyzesPagesInformationForPage8() {
80 | val activity = startActivity(R.drawable.sample_page_8)
81 |
82 | compareExtractPanelsActivityScreenshot(activity)
83 | }
84 |
85 | @Test
86 | fun analyzesPagesInformationForPage9() {
87 | val activity = startActivity(R.drawable.sample_page_9)
88 |
89 | compareExtractPanelsActivityScreenshot(activity)
90 | }
91 |
92 | @Test
93 | fun analyzesPagesInformationForPage10() {
94 | val activity = startActivity(R.drawable.sample_page_10)
95 |
96 | compareExtractPanelsActivityScreenshot(activity)
97 | }
98 |
99 | private fun compareExtractPanelsActivityScreenshot(activity: ExtractPanelsActivity) {
100 | val root = activity.findViewById(android.R.id.content)
101 | runOnUi {
102 | root.setBackgroundResource(android.R.color.white)
103 | }
104 | return compareScreenshot(
105 | view = root,
106 | heightInPx = 2000
107 | )
108 | }
109 |
110 | private fun startActivity(resource: Int): ExtractPanelsActivity {
111 | val intent = Intent(getInstrumentation().targetContext, ExtractPanelsActivity::class.java)
112 | intent.putExtra(resource_id_extra, resource)
113 | scenario = ActivityScenario.launch(intent)
114 | waitForIdle()
115 | return scenario.waitForActivity()
116 | }
117 |
118 | private fun waitForIdle() {
119 | waitForAnimationsToFinish()
120 | }
121 | }
122 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/github/pedrovgs/deeppanel/app/MainActivityTest.kt:
--------------------------------------------------------------------------------
1 | package com.github.pedrovgs.deeppanel.app
2 |
3 | import androidx.test.core.app.ActivityScenario
4 | import androidx.test.espresso.Espresso.onView
5 | import androidx.test.espresso.action.ViewActions.click
6 | import androidx.test.espresso.matcher.ViewMatchers.withId
7 | import com.karumi.shot.ScreenshotTest
8 | import com.karumi.shot.waitForActivity
9 | import org.junit.After
10 | import org.junit.Test
11 |
12 | class MainActivityTest : ScreenshotTest {
13 |
14 | private lateinit var scenario: ActivityScenario
15 |
16 | @After
17 | fun tearDown() {
18 | scenario.close()
19 | }
20 |
21 | @Test
22 | fun analyzesEveryPageImageAndGeneratesThePanelsOverlays() {
23 | val activity = startActivity()
24 |
25 | Pages.resList.forEachIndexed { index, _ ->
26 | compareScreenshot(activity = activity, name = "Panels analysis for page $index")
27 |
28 | clickOnGoToNextPage()
29 | }
30 | }
31 |
32 | private fun clickOnGoToNextPage() {
33 | onView(withId(R.id.nextPageButton)).perform(click())
34 | waitForIdle()
35 | }
36 |
37 | private fun startActivity(): MainActivity {
38 | scenario = ActivityScenario.launch(MainActivity::class.java)
39 | val activity = scenario.waitForActivity()
40 | waitForIdle()
41 | return activity
42 | }
43 |
44 | private fun waitForIdle() {
45 | waitForAnimationsToFinish()
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
14 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
29 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/app/src/main/java/com/github/pedrovgs/deeppanel/app/DeepPanelApplication.kt:
--------------------------------------------------------------------------------
1 | package com.github.pedrovgs.deeppanel.app
2 |
3 | import android.app.Application
4 | import com.github.pedrovgs.deeppanel.DeepPanel
5 |
6 | class DeepPanelApplication : Application() {
7 | override fun onCreate() {
8 | super.onCreate()
9 | DeepPanel.initialize(this)
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/app/src/main/java/com/github/pedrovgs/deeppanel/app/ExtractPanelsActivity.kt:
--------------------------------------------------------------------------------
1 | package com.github.pedrovgs.deeppanel.app
2 |
3 | import android.annotation.SuppressLint
4 | import android.app.Activity
5 | import android.content.Intent
6 | import android.os.Bundle
7 | import android.widget.Toast
8 | import androidx.appcompat.app.AppCompatActivity
9 | import androidx.core.graphics.drawable.toBitmap
10 | import com.github.pedrovgs.deeppanel.Bitmaps
11 | import com.github.pedrovgs.deeppanel.DeepPanel
12 | import com.github.pedrovgs.deeppanel.Panels
13 | import kotlinx.android.synthetic.main.activity_extract_panels.*
14 |
15 | class ExtractPanelsActivity : AppCompatActivity() {
16 |
17 | companion object {
18 | const val resource_id_extra = "resource_id_extra"
19 | fun open(activity: Activity, resId: Int) {
20 | val intent = Intent(activity, ExtractPanelsActivity::class.java)
21 | intent.putExtra(resource_id_extra, resId)
22 | activity.startActivity(intent)
23 | }
24 | }
25 |
26 | override fun onCreate(savedInstanceState: Bundle?) {
27 | super.onCreate(savedInstanceState)
28 | setContentView(R.layout.activity_extract_panels)
29 | val defaultRes = R.drawable.sample_page_0
30 | val resId = intent?.extras?.getInt(resource_id_extra, defaultRes) ?: defaultRes
31 | extractPanelsFromResource(resId)
32 | }
33 |
34 | private fun extractPanelsFromResource(resId: Int) {
35 | val bitmapResImage = resources.getDrawable(resId, null).toBitmap()
36 | val initialTime = System.currentTimeMillis()
37 | val result = DeepPanel().extractPanelsInfo(bitmapResImage)
38 | val now = System.currentTimeMillis()
39 | val timeElapsed = now - initialTime
40 | val message = "Page analyzed in $timeElapsed ms"
41 | Toast.makeText(this, message, Toast.LENGTH_SHORT).show()
42 | val generatePanelsBitmap = Bitmaps.generatePanelsBitmap(bitmapResImage, result.panels)
43 | image.setImageBitmap(generatePanelsBitmap)
44 | renderPanelsInfo(result.panels, timeElapsed)
45 | image.setOnClickListener {
46 | FullScreenImageActivity.open(this, generatePanelsBitmap)
47 | }
48 | }
49 |
50 | @SuppressLint("SetTextI18n")
51 | private fun renderPanelsInfo(panels: Panels, timeElapsed: Long) {
52 | val runningTests = checkIfRunningTests()
53 | numberOfPanelsView.text = if (runningTests) {
54 | "Number of panels: ${panels.numberOfPanels} found"
55 | } else {
56 | "Number of panels: ${panels.numberOfPanels} found in ${timeElapsed}ms"
57 | }
58 | panelsInfoView.text = panels.panelsInfo.joinToString("\n---------\n") { panel ->
59 | """Left: ${panel.left}, Top: ${panel.top}
60 | |Right: ${panel.right}, Bottom: ${panel.bottom}
61 | |Width: ${panel.width}, Height: ${panel.height}
62 | """.trimMargin()
63 | }
64 | }
65 |
66 | private fun checkIfRunningTests(): Boolean {
67 | return try {
68 | Class.forName("com.karumi.shot.ShotTestRunner")
69 | true
70 | } catch (e: Throwable) {
71 | false
72 | }
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/app/src/main/java/com/github/pedrovgs/deeppanel/app/FullScreenImageActivity.kt:
--------------------------------------------------------------------------------
1 | package com.github.pedrovgs.deeppanel.app
2 |
3 | import android.app.Activity
4 | import android.content.Intent
5 | import android.graphics.Bitmap
6 | import android.os.Bundle
7 | import androidx.appcompat.app.AppCompatActivity
8 | import kotlinx.android.synthetic.main.activity_full_screen_image.*
9 |
10 | class FullScreenImageActivity : AppCompatActivity() {
11 | companion object {
12 | lateinit var extraImage: Bitmap
13 | fun open(activity: Activity, bitmap: Bitmap) {
14 | extraImage = bitmap
15 | val intent = Intent(activity, FullScreenImageActivity::class.java)
16 | activity.startActivity(intent)
17 | }
18 | }
19 |
20 | override fun onCreate(savedInstanceState: Bundle?) {
21 | super.onCreate(savedInstanceState)
22 | setContentView(R.layout.activity_full_screen_image)
23 | val bitmap: Bitmap = extraImage
24 | image.setImageBitmap(bitmap)
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/app/src/main/java/com/github/pedrovgs/deeppanel/app/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package com.github.pedrovgs.deeppanel.app
2 |
3 | import android.os.Bundle
4 | import android.util.Log
5 | import android.widget.Toast
6 | import androidx.appcompat.app.AppCompatActivity
7 | import androidx.core.graphics.drawable.toBitmap
8 | import com.github.pedrovgs.deeppanel.DeepPanel
9 | import com.github.pedrovgs.deeppanel.app.Pages.resList
10 | import kotlinx.android.synthetic.main.activity_main.*
11 |
12 | class MainActivity : AppCompatActivity() {
13 |
14 | override fun onCreate(savedInstanceState: Bundle?) {
15 | super.onCreate(savedInstanceState)
16 | setContentView(R.layout.activity_main)
17 | val deepPanel = DeepPanel()
18 | var currentPage = 0
19 | val size: Int = resList.size
20 | showPredictionForPage(deepPanel, resList[currentPage % size])
21 | nextPageButton.setOnClickListener {
22 | currentPage = (currentPage + 1) % resList.count()
23 | showPredictionForPage(deepPanel, resList[currentPage % size])
24 | }
25 | detailPageButton.setOnClickListener {
26 | ExtractPanelsActivity.open(this, resList[currentPage])
27 | }
28 | }
29 |
30 | private fun showPredictionForPage(deepPanel: DeepPanel, pageResource: Int) {
31 | val bitmapSamplePage = resources.getDrawable(pageResource, null).toBitmap()
32 | val initialTime = System.currentTimeMillis()
33 | val result = deepPanel.extractDetailedPanelsInfo(bitmapSamplePage)
34 | val now = System.currentTimeMillis()
35 | val timeElapsed = now - initialTime
36 | image.post {
37 | val message = "Page analyzed in $timeElapsed ms"
38 | Log.d("DeepPanel", message)
39 | Toast.makeText(this, message, Toast.LENGTH_SHORT).show()
40 | image.setImageBitmap(result.imageInput)
41 | mask.setImageBitmap(result.labeledAreasBitmap)
42 | panelsInfo.setImageBitmap(result.panelsBitmap)
43 | image.setOnClickListener {
44 | FullScreenImageActivity.open(this, result.resizedImage)
45 | }
46 | mask.setOnClickListener {
47 | FullScreenImageActivity.open(this, result.labeledAreasBitmap)
48 | }
49 | panelsInfo.setOnClickListener {
50 | FullScreenImageActivity.open(this, result.panelsBitmap)
51 | }
52 | }
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/app/src/main/java/com/github/pedrovgs/deeppanel/app/Pages.kt:
--------------------------------------------------------------------------------
1 | package com.github.pedrovgs.deeppanel.app
2 |
3 | object Pages {
4 | val resList = listOf(
5 | R.drawable.sample_page_0,
6 | R.drawable.sample_page_1,
7 | R.drawable.sample_page_2,
8 | R.drawable.sample_page_3,
9 | R.drawable.sample_page_4,
10 | R.drawable.sample_page_5,
11 | R.drawable.sample_page_6,
12 | R.drawable.sample_page_7,
13 | R.drawable.sample_page_8,
14 | R.drawable.sample_page_9,
15 | R.drawable.sample_page_10
16 | ).reversed()
17 | }
18 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
15 |
18 |
21 |
22 |
23 |
24 |
30 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/sample_page_0.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pedrovgs/DeepPanelAndroid/75133803b9615cc37f744f998459d883789fd247/app/src/main/res/drawable-xhdpi/sample_page_0.jpg
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/sample_page_1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pedrovgs/DeepPanelAndroid/75133803b9615cc37f744f998459d883789fd247/app/src/main/res/drawable-xhdpi/sample_page_1.jpg
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/sample_page_10.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pedrovgs/DeepPanelAndroid/75133803b9615cc37f744f998459d883789fd247/app/src/main/res/drawable-xhdpi/sample_page_10.jpg
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/sample_page_2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pedrovgs/DeepPanelAndroid/75133803b9615cc37f744f998459d883789fd247/app/src/main/res/drawable-xhdpi/sample_page_2.jpg
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/sample_page_3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pedrovgs/DeepPanelAndroid/75133803b9615cc37f744f998459d883789fd247/app/src/main/res/drawable-xhdpi/sample_page_3.jpg
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/sample_page_4.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pedrovgs/DeepPanelAndroid/75133803b9615cc37f744f998459d883789fd247/app/src/main/res/drawable-xhdpi/sample_page_4.jpg
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/sample_page_5.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pedrovgs/DeepPanelAndroid/75133803b9615cc37f744f998459d883789fd247/app/src/main/res/drawable-xhdpi/sample_page_5.jpg
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/sample_page_6.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pedrovgs/DeepPanelAndroid/75133803b9615cc37f744f998459d883789fd247/app/src/main/res/drawable-xhdpi/sample_page_6.jpg
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/sample_page_7.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pedrovgs/DeepPanelAndroid/75133803b9615cc37f744f998459d883789fd247/app/src/main/res/drawable-xhdpi/sample_page_7.jpg
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/sample_page_8.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pedrovgs/DeepPanelAndroid/75133803b9615cc37f744f998459d883789fd247/app/src/main/res/drawable-xhdpi/sample_page_8.jpg
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/sample_page_9.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pedrovgs/DeepPanelAndroid/75133803b9615cc37f744f998459d883789fd247/app/src/main/res/drawable-xhdpi/sample_page_9.jpg
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
10 |
15 |
20 |
25 |
30 |
35 |
40 |
45 |
50 |
55 |
60 |
65 |
70 |
75 |
80 |
85 |
90 |
95 |
100 |
105 |
110 |
115 |
120 |
125 |
130 |
135 |
140 |
145 |
150 |
155 |
160 |
165 |
170 |
171 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_extract_panels.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
17 |
18 |
25 |
26 |
30 |
31 |
36 |
37 |
42 |
43 |
44 |
45 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_full_screen_image.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 |
18 |
19 |
28 |
29 |
35 |
36 |
42 |
43 |
44 |
50 |
51 |
52 |
53 |
61 |
62 |
70 |
71 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pedrovgs/DeepPanelAndroid/75133803b9615cc37f744f998459d883789fd247/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pedrovgs/DeepPanelAndroid/75133803b9615cc37f744f998459d883789fd247/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pedrovgs/DeepPanelAndroid/75133803b9615cc37f744f998459d883789fd247/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pedrovgs/DeepPanelAndroid/75133803b9615cc37f744f998459d883789fd247/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pedrovgs/DeepPanelAndroid/75133803b9615cc37f744f998459d883789fd247/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pedrovgs/DeepPanelAndroid/75133803b9615cc37f744f998459d883789fd247/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pedrovgs/DeepPanelAndroid/75133803b9615cc37f744f998459d883789fd247/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pedrovgs/DeepPanelAndroid/75133803b9615cc37f744f998459d883789fd247/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pedrovgs/DeepPanelAndroid/75133803b9615cc37f744f998459d883789fd247/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pedrovgs/DeepPanelAndroid/75133803b9615cc37f744f998459d883789fd247/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #6200EE
4 | #3700B3
5 | #03DAC5
6 |
--------------------------------------------------------------------------------
/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 | 16dp
3 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | DeepPanelAndroid
3 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
9 |
10 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/art/panelsInfo1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pedrovgs/DeepPanelAndroid/75133803b9615cc37f744f998459d883789fd247/art/panelsInfo1.png
--------------------------------------------------------------------------------
/art/panelsInfo2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pedrovgs/DeepPanelAndroid/75133803b9615cc37f744f998459d883789fd247/art/panelsInfo2.png
--------------------------------------------------------------------------------
/art/rawPage1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pedrovgs/DeepPanelAndroid/75133803b9615cc37f744f998459d883789fd247/art/rawPage1.png
--------------------------------------------------------------------------------
/art/rawPage2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pedrovgs/DeepPanelAndroid/75133803b9615cc37f744f998459d883789fd247/art/rawPage2.png
--------------------------------------------------------------------------------
/art/screencast.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pedrovgs/DeepPanelAndroid/75133803b9615cc37f744f998459d883789fd247/art/screencast.gif
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 | buildscript {
3 | ext.kotlin_version = "1.3.72"
4 | repositories {
5 | google()
6 | jcenter()
7 | mavenCentral()
8 | maven { url("https://plugins.gradle.org/m2/") }
9 | }
10 | dependencies {
11 | classpath 'com.android.tools.build:gradle:4.0.1'
12 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
13 | classpath("org.jlleitschuh.gradle:ktlint-gradle:9.2.1")
14 | classpath "com.karumi:shot:5.1.0"
15 | }
16 | }
17 |
18 | allprojects {
19 | repositories {
20 | google()
21 | jcenter()
22 | mavenCentral()
23 | }
24 | }
25 |
26 | subprojects {
27 | apply plugin: "org.jlleitschuh.gradle.ktlint"
28 | }
29 |
30 | task clean(type: Delete) {
31 | delete rootProject.buildDir
32 | }
--------------------------------------------------------------------------------
/deeppanel/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/deeppanel/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: "com.android.library"
2 | apply plugin: "kotlin-android"
3 | apply plugin: "kotlin-android-extensions"
4 | apply from: "../release.gradle"
5 |
6 | android {
7 | compileSdkVersion 29
8 | buildToolsVersion "29.0.3"
9 | ndkVersion "21.3.6528147"
10 | defaultConfig {
11 | testApplicationId "com.github.pedrovgs.deeppanel.lib"
12 | minSdkVersion 21
13 | targetSdkVersion 29
14 | versionCode 1
15 | versionName "1.0"
16 |
17 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
18 | consumerProguardFiles "consumer-rules.pro"
19 | testOptions {
20 | unitTests.returnDefaultValues = true
21 | }
22 | externalNativeBuild {
23 | cmake {
24 | cppFlags ""
25 | }
26 | }
27 | }
28 |
29 | buildTypes {
30 | release {
31 | minifyEnabled false
32 | proguardFiles getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro"
33 | }
34 | }
35 | externalNativeBuild {
36 | cmake {
37 | path "src/main/cpp/CMakeLists.txt"
38 | version "3.10.2"
39 | }
40 | }
41 | }
42 |
43 | dependencies {
44 | implementation fileTree(dir: "libs", include: ["*.jar"])
45 | implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
46 | implementation "androidx.core:core-ktx:1.3.1"
47 | implementation "org.tensorflow:tensorflow-lite:2.2.0"
48 | testImplementation "junit:junit:4.13"
49 | androidTestImplementation "androidx.test.ext:junit:1.1.2"
50 | androidTestImplementation "androidx.test.espresso:espresso-core:3.3.0"
51 | }
--------------------------------------------------------------------------------
/deeppanel/consumer-rules.pro:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pedrovgs/DeepPanelAndroid/75133803b9615cc37f744f998459d883789fd247/deeppanel/consumer-rules.pro
--------------------------------------------------------------------------------
/deeppanel/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
--------------------------------------------------------------------------------
/deeppanel/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
--------------------------------------------------------------------------------
/deeppanel/src/main/cpp/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | # For more information about using CMake with Android Studio, read the
2 | # documentation: https://d.android.com/studio/projects/add-native-code.html
3 |
4 | # Sets the minimum version of CMake required to build the native library.
5 |
6 | cmake_minimum_required(VERSION 3.4.1)
7 |
8 | # Creates and names a library, sets it as either STATIC
9 | # or SHARED, and provides the relative paths to its source code.
10 | # You can define multiple libraries, and CMake builds them for you.
11 | # Gradle automatically packages shared libraries with your APK.
12 |
13 | add_library( # Sets the name of the library.
14 | deep-panel
15 |
16 | # Sets the library as a shared library.
17 | SHARED
18 |
19 | # Provides a relative path to your source file(s).
20 | deep-panel-android.cpp)
21 |
22 | # Searches for a specified prebuilt library and stores the path as a
23 | # variable. Because CMake includes system libraries in the search path by
24 | # default, you only need to specify the name of the public NDK library
25 | # you want to add. CMake verifies that the library exists before
26 | # completing its build.
27 |
28 | find_library( # Sets the name of the path variable.
29 | log-lib
30 |
31 | # Specifies the name of the NDK library that
32 | # you want CMake to locate.
33 | log)
34 |
35 | # Specifies libraries CMake should link to your target library. You
36 | # can link multiple libraries, such as libraries you define in this
37 | # build script, prebuilt third-party libraries, or system libraries.
38 |
39 | target_link_libraries( # Specifies the target library.
40 | deep-panel
41 | # Links the target library to the log library
42 | # included in the NDK.
43 | ${log-lib})
--------------------------------------------------------------------------------
/deeppanel/src/main/cpp/ConnectedComponentResult.hpp:
--------------------------------------------------------------------------------
1 | #ifndef ConnectedComponentResult_h
2 | #define ConnectedComponentResult_h
3 |
4 | class ConnectedComponentResult {
5 | public:
6 | int total_clusters;
7 | int **clusters_matrix;
8 | int *pixels_per_labels;
9 | };
10 |
11 | #endif /* ConnectedComponentResult_h */
12 |
--------------------------------------------------------------------------------
/deeppanel/src/main/cpp/DeepPanelResult.hpp:
--------------------------------------------------------------------------------
1 | #ifndef DeepPanelResult_hpp
2 | #define DeepPanelResult_hpp
3 |
4 | #include
5 | #include "Panel.hpp"
6 | #include "ConnectedComponentResult.hpp"
7 |
8 | class DeepPanelResult {
9 | public:
10 | ConnectedComponentResult connected_components;
11 | Panel *panels;
12 | };
13 |
14 | #endif /* DeepPanelResult_hpp */
15 |
--------------------------------------------------------------------------------
/deeppanel/src/main/cpp/Panel.hpp:
--------------------------------------------------------------------------------
1 | //
2 | // Panel.hpp
3 | // DeepPanel
4 | //
5 | // Created by Pedro Gómez on 11/10/2020.
6 | // Copyright © 2020 Pedro Gómez. All rights reserved.
7 | //
8 |
9 | #ifndef Panel_hpp
10 | #define Panel_hpp
11 |
12 | #include
13 |
14 | class Panel {
15 | public:
16 | int left;
17 | int bottom;
18 | int right;
19 | int top;
20 | };
21 |
22 |
23 | #endif /* Panel_hpp */
24 |
--------------------------------------------------------------------------------
/deeppanel/src/main/cpp/connected-components.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include "connected-components.hpp"
5 |
6 | /* Implementation of Union-Find Algorithm and Hoshen Kopelman Algorithm (aka Connected Component Labeling).
7 | * You can find the reference implementation here: https://www.ocf.berkeley.edu/~fricke/projects/hoshenkopelman/hk.c.
8 | * Useful info about this algorithm can be found here: https://www.ocf.berkeley.edu/~fricke/projects/hoshenkopelman/hoshenkopelman.html*/
9 |
10 | /* The 'labels' array has the meaning that labels[x] is an alias for the label x; by
11 | following this chain until x == labels[x], you can find the canonical name of an
12 | equivalence class. The labels start at one; labels[0] is a special value indicating
13 | the highest label already used. */
14 |
15 | int *labels;
16 | int n_labels = 0; /* length of the labels array */
17 |
18 | /* uf_find returns the canonical label for the equivalence class containing x */
19 |
20 | int uf_find(int x) {
21 | int y = x;
22 | while (labels[y] != y)
23 | y = labels[y];
24 |
25 | while (labels[x] != x) {
26 | int z = labels[x];
27 | labels[x] = y;
28 | x = z;
29 | }
30 | return y;
31 | }
32 |
33 | /* uf_union joins two equivalence classes and returns the canonical label of the resulting class. */
34 |
35 | int uf_union(int x, int y) {
36 | return labels[uf_find(x)] = uf_find(y);
37 | }
38 |
39 | /* uf_make_set creates a new equivalence class and returns its label */
40 |
41 | int uf_make_set(void) {
42 | labels[0]++;
43 | assert(labels[0] < n_labels);
44 | labels[labels[0]] = labels[0];
45 | return labels[0];
46 | }
47 |
48 | /* uf_intitialize sets up the data structures needed by the union-find implementation. */
49 |
50 | void uf_initialize(int max_labels) {
51 | n_labels = max_labels;
52 | labels = static_cast(calloc(n_labels, sizeof(int)));
53 | labels[0] = 0;
54 | }
55 |
56 | /* uf_done frees the memory used by the union-find data structures */
57 |
58 | void uf_done(void) {
59 | n_labels = 0;
60 | free(labels);
61 | labels = 0;
62 | }
63 |
64 | /* End Union-Find implementation */
65 |
66 | #define max(a, b) (a>b?a:b)
67 |
68 | /* Hoshen Kopelman algorithm implementation. Label the clusters in "matrix". Return the total number of clusters found. */
69 |
70 | ConnectedComponentResult find_components(int **matrix, int m, int n) {
71 |
72 | uf_initialize(m * n / 2);
73 |
74 | /* scan the matrix */
75 |
76 | for (int j = 0; j < n; j++)
77 | for (int i = 0; i < m; i++)
78 | if (matrix[i][j]) { // if occupied ...
79 |
80 | int up = (i == 0 ? 0 : matrix[i - 1][j]); // look up
81 | int left = (j == 0 ? 0 : matrix[i][j - 1]); // look left
82 |
83 | switch (!!up + !!left) {
84 |
85 | case 0:
86 | matrix[i][j] = uf_make_set(); // a new cluster
87 | break;
88 |
89 | case 1: // part of an existing cluster
90 | matrix[i][j] = max(up, left); // whichever is nonzero is labelled
91 | break;
92 |
93 | case 2: // this site binds two clusters
94 | matrix[i][j] = uf_union(up, left);
95 | break;
96 | }
97 |
98 | }
99 |
100 | /* apply the relabeling to the matrix */
101 |
102 | /* This is a little bit sneaky.. we create a mapping from the canonical labels
103 | determined by union/find into a new set of canonical labels, which are
104 | guaranteed to be sequential. */
105 |
106 | int *new_labels = static_cast(calloc(n_labels,
107 | sizeof(int))); // allocate array, initialized to zero
108 | int *pixels_per_label = static_cast(calloc(n_labels,
109 | sizeof(int))); // allocate array, initialized to zero
110 | for (int j = 0; j < n; j++)
111 | for (int i = 0; i < m; i++)
112 | if (matrix[i][j]) {
113 | int x = uf_find(matrix[i][j]);
114 | if (new_labels[x] == 0) {
115 | new_labels[0]++;
116 | new_labels[x] = new_labels[0];
117 | }
118 | int new_label_to_assign = new_labels[x];
119 | matrix[i][j] = new_label_to_assign;
120 | pixels_per_label[new_label_to_assign]++;
121 | }
122 |
123 | int total_clusters = new_labels[0];
124 |
125 | free(new_labels);
126 | uf_done();
127 |
128 | ConnectedComponentResult result;
129 | result.total_clusters = total_clusters;
130 | result.clusters_matrix = matrix;
131 | result.pixels_per_labels = pixels_per_label;
132 | return result;
133 | }
134 |
--------------------------------------------------------------------------------
/deeppanel/src/main/cpp/connected-components.hpp:
--------------------------------------------------------------------------------
1 | #ifndef connected_components_h
2 | #define connected_components_h
3 | #import "ConnectedComponentResult.hpp"
4 |
5 | ConnectedComponentResult find_components(int **matrix, int m, int n);
6 |
7 | #endif /* connected_components_h */
8 |
--------------------------------------------------------------------------------
/deeppanel/src/main/cpp/deep-panel-android.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #import "connected-components.cpp"
3 | #import "deep-panel.cpp"
4 |
5 | jint map_predicted_row_to_label(JNIEnv *env, jobjectArray prediction, int i, int j) {
6 | auto x = (jobjectArray) env->GetObjectArrayElement(prediction, i);
7 | auto y = (jfloatArray) env->GetObjectArrayElement(x, j);
8 | jfloat *pixel_prediction = env->GetFloatArrayElements(y, 0);
9 | jfloat background = pixel_prediction[0];
10 | jfloat border = pixel_prediction[1];
11 | jfloat content = pixel_prediction[2];
12 | if (background >= content && background > border) {
13 | return 0;
14 | } else if (border >= background && border >= content) {
15 | return 0; // The original label should be 1 but we need this value to be 0
16 | // because the ccl algorithm uses 0 and 1 as values. 0 is used for the background.
17 | } else {
18 | return 1; // The original label should be 1 but we need this value to be 0
19 | // because the ccl algorithm uses 0 and 1 as values. 1 is used for the content.
20 | }
21 | }
22 |
23 | jobjectArray int_array_to_java_array(JNIEnv *env, int **matrix, int width, int height) {
24 | jclass int_array_class = env->FindClass("[I");
25 | jobjectArray labels_array = env->NewObjectArray(width, int_array_class, nullptr);
26 | for (int i = 0; i < width; i++) {
27 | jintArray int_array = env->NewIntArray(height);
28 | env->SetIntArrayRegion(int_array, 0, height, matrix[i]);
29 | env->SetObjectArrayElement(labels_array, i, int_array);
30 | }
31 | return labels_array;
32 | }
33 |
34 | jobject panel_to_java_raw_panel(JNIEnv *env, Panel panel) {
35 | jclass raw_panel_class = env->FindClass("com/github/pedrovgs/deeppanel/RawPanel");
36 | jmethodID constructor = env->GetMethodID(raw_panel_class, "", "()V");
37 | jobject result_objc = env->NewObject(raw_panel_class, constructor);
38 | jmethodID set_left = env->GetMethodID(raw_panel_class, "setLeft", "(I)V");
39 | env->CallVoidMethod(result_objc, set_left, panel.left);
40 | jmethodID set_top = env->GetMethodID(raw_panel_class, "setTop", "(I)V");
41 | env->CallVoidMethod(result_objc, set_top, panel.top);
42 | jmethodID set_right = env->GetMethodID(raw_panel_class, "setRight", "(I)V");
43 | env->CallVoidMethod(result_objc, set_right, panel.right);
44 | jmethodID set_bottom = env->GetMethodID(raw_panel_class, "setBottom", "(I)V");
45 | env->CallVoidMethod(result_objc, set_bottom, panel.bottom);
46 | return result_objc;
47 | }
48 |
49 | jobjectArray result_to_java_raw_panels_info(JNIEnv *env, DeepPanelResult result) {
50 | int number_of_panels = result.connected_components.total_clusters;
51 | jclass panel_class = env->FindClass("com/github/pedrovgs/deeppanel/RawPanel");
52 | jobjectArray panels_array = env->NewObjectArray(number_of_panels, panel_class, nullptr);
53 | for (int i = 0; i < number_of_panels; i++) {
54 | Panel panel = result.panels[i];
55 | jobject raw_panel = panel_to_java_raw_panel(env, panel);
56 | env->SetObjectArrayElement(panels_array, i, raw_panel);
57 | }
58 | return panels_array;
59 | }
60 |
61 | jobject compose_java_result(JNIEnv *env, DeepPanelResult result, int width, int height) {
62 | int **connectedComponentsMatrix = result.connected_components.clusters_matrix;
63 | jobjectArray java_ints_array = int_array_to_java_array(env, connectedComponentsMatrix, width,
64 | height);
65 | jobjectArray panels = result_to_java_raw_panels_info(env, result);
66 |
67 | jclass result_class = env->FindClass("com/github/pedrovgs/deeppanel/RawPanelsInfo");
68 | jmethodID constructor = env->GetMethodID(result_class, "", "()V");
69 | jobject result_objc = env->NewObject(result_class, constructor);
70 | jmethodID set_int_arrays_method = env->GetMethodID(result_class, "setConnectedAreas", "([[I)V");
71 | env->CallVoidMethod(result_objc, set_int_arrays_method, java_ints_array);
72 |
73 | jmethodID set_panels_method = env->GetMethodID(result_class, "setPanels", "([Lcom/github/pedrovgs/deeppanel/RawPanel;)V");
74 | env->CallVoidMethod(result_objc, set_panels_method, panels);
75 | return result_objc;
76 | }
77 |
78 | extern "C" JNIEXPORT jobject JNICALL
79 | Java_com_github_pedrovgs_deeppanel_NativeDeepPanel_extractPanelsInfo
80 | (
81 | JNIEnv *env,
82 | jobject /* this */,
83 | jobjectArray prediction,
84 | jfloat scale,
85 | jint original_image_width,
86 | jint original_image_height) {
87 | auto first_item = (jobjectArray) env->GetObjectArrayElement(prediction, 0);
88 | jsize width = env->GetArrayLength(prediction);
89 | jsize height = env->GetArrayLength(first_item);
90 | int **labeled_matrix = new int *[height];
91 | for (int i = 0; i < width; i++) {
92 | labeled_matrix[i] = new int[width];
93 | for (int j = 0; j < height; j++) {
94 | // j and i indexes order is changed on purpose because the original matrix
95 | // is rotated when reading the values.
96 | labeled_matrix[i][j] = map_predicted_row_to_label(env, prediction, j, i);
97 | }
98 | }
99 | DeepPanelResult result = extract_panels_info(labeled_matrix, width, height, scale, original_image_width, original_image_height);
100 | return compose_java_result(env, result, width, height);
101 | }
102 |
--------------------------------------------------------------------------------
/deeppanel/src/main/cpp/deep-panel.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include "deep-panel.hpp"
3 | #include "connected-components.hpp"
4 |
5 | ConnectedComponentResult remove_small_areas_and_recover_border(
6 | ConnectedComponentResult connected_component_result,
7 | int width,
8 | int height) {
9 | int new_total_clusters = connected_component_result.total_clusters;
10 | int **clusters_matrix = connected_component_result.clusters_matrix;
11 | int *pixels_per_labels = connected_component_result.pixels_per_labels;
12 | int image_size = width * height;
13 | bool label_removed[1000] = { false };
14 | int min_allowed_area = image_size * 0.03;
15 | for (int i = 0; i < width; i++)
16 | for (int j = 0; j < height; j++) {
17 | int label = clusters_matrix[i][j];
18 | if (label != 0) {
19 | int pixelsPerLabel = pixels_per_labels[label];
20 | if (pixelsPerLabel < min_allowed_area) {
21 | clusters_matrix[i][j] = 0;
22 | if (!label_removed[label]) {
23 | new_total_clusters--;
24 | label_removed[label] = true;
25 | }
26 | }
27 | }
28 | }
29 | ConnectedComponentResult result;
30 | result.clusters_matrix = clusters_matrix;
31 | result.total_clusters = new_total_clusters;
32 | result.pixels_per_labels = pixels_per_labels;
33 | return result;
34 | }
35 |
36 | #define max(a, b) (a>b?a:b)
37 | #define min(a, b) (a>b?b:a)
38 |
39 | int apply_scale_and_add_border(int position, float scale, int border) {
40 | float float_position = (float) position;
41 | float scaled_position = float_position * scale;
42 | return ((int) scaled_position) + border;
43 | }
44 |
45 | int clamp(int value, int min, int max) {
46 | if (value < min) {
47 | return min;
48 | } else if (value > max) {
49 | return max;
50 | } else {
51 | return value;
52 | }
53 | }
54 |
55 | int compute_border_size(int original_image_width, int original_image_height) {
56 | if (original_image_height > original_image_width) {
57 | return original_image_width * 30 / 3056;
58 | } else {
59 | return original_image_height * 30 / 1988;
60 | }
61 | }
62 |
63 | DeepPanelResult
64 | extract_panels_data(ConnectedComponentResult connected_components_result,
65 | int width,
66 | int height,
67 | float scale,
68 | int original_image_width,
69 | int original_image_height) {
70 | int number_of_panels = connected_components_result.total_clusters;
71 | int current_normalized_label = 0;
72 | int *normalized_labels = new int[width * height] { 0 };
73 | int *min_x_values = new int[number_of_panels + 1];
74 | int *max_x_values = new int[number_of_panels + 1];
75 | int *min_y_values = new int[number_of_panels + 1];
76 | int *max_y_values = new int[number_of_panels + 1];
77 | for (int i = 0; i < number_of_panels + 1; i++) {
78 | min_x_values[i] = INT_MAX;
79 | max_x_values[i] = INT_MIN;
80 | min_y_values[i] = INT_MAX;
81 | max_y_values[i] = INT_MIN;
82 | }
83 | int **cluster_matrix = connected_components_result.clusters_matrix;
84 | for (int i = 0; i < width; i++)
85 | for (int j = 0; j < height; j++) {
86 | int raw_label = cluster_matrix[i][j];
87 | if (raw_label != 0) {
88 | if (normalized_labels[raw_label] == 0) {
89 | current_normalized_label++;
90 | normalized_labels[raw_label] = current_normalized_label;
91 | }
92 | int normalized_label = normalized_labels[raw_label];
93 | min_x_values[normalized_label] = min(min_x_values[normalized_label], i);
94 | max_x_values[normalized_label] = max(max_x_values[normalized_label], i);
95 | min_y_values[normalized_label] = min(min_y_values[normalized_label], j);
96 | max_y_values[normalized_label] = max(max_y_values[normalized_label], j);
97 | }
98 | }
99 | Panel *panels = new Panel[number_of_panels];
100 | int horizontal_correction = 0;
101 | int vertical_correction = 0;
102 | if (original_image_width < original_image_height) {
103 | horizontal_correction = ((width * scale) - original_image_width) / 2;
104 | } else {
105 | vertical_correction = ((height * scale) - original_image_height) / 2;
106 | }
107 | int border = compute_border_size(original_image_width, original_image_height);
108 | for (int i = 1; i <= number_of_panels; i++) {
109 | Panel panel;
110 | int proposed_left =
111 | apply_scale_and_add_border(min_x_values[i], scale, -border) - horizontal_correction;
112 | int proposed_top =
113 | apply_scale_and_add_border(min_y_values[i], scale, -border) - vertical_correction;
114 | int proposed_right =
115 | apply_scale_and_add_border(max_x_values[i], scale, border) - horizontal_correction;
116 | int proposed_bottom =
117 | apply_scale_and_add_border(max_y_values[i], scale, border) - vertical_correction;
118 | panel.left = clamp(proposed_left, 0, original_image_width);
119 | panel.top = clamp(proposed_top, 0, original_image_height);
120 | panel.right = clamp(proposed_right, 0, original_image_width);
121 | panel.bottom = clamp(proposed_bottom, 0, original_image_height);
122 | panels[i - 1] = panel;
123 | }
124 | free(min_x_values);
125 | free(max_x_values);
126 | free(min_y_values);
127 | free(max_y_values);
128 | free(normalized_labels);
129 | DeepPanelResult deep_panel_result;
130 | deep_panel_result.connected_components = connected_components_result;
131 | deep_panel_result.panels = panels;
132 | return deep_panel_result;
133 | }
134 |
135 | DeepPanelResult extract_panels_info(int **labeled_matrix,
136 | int width,
137 | int height, float scale,
138 | int original_image_width,
139 | int original_image_height) {
140 | ConnectedComponentResult improved_areas_result = find_components(labeled_matrix, width, height);
141 | improved_areas_result = remove_small_areas_and_recover_border(improved_areas_result, width, height);
142 | return extract_panels_data(improved_areas_result, width, height, scale, original_image_width,
143 | original_image_height);
144 | }
145 |
--------------------------------------------------------------------------------
/deeppanel/src/main/cpp/deep-panel.hpp:
--------------------------------------------------------------------------------
1 | #ifndef deep_panel_hpp
2 | #define deep_panel_hpp
3 |
4 | #include "DeepPanelResult.hpp"
5 |
6 | DeepPanelResult extract_panels_info(int **labeled_matrix,
7 | int width,
8 | int height, float scale,
9 | int original_image_width,
10 | int original_image_height);
11 | #endif /* deep_panel_h */
12 |
--------------------------------------------------------------------------------
/deeppanel/src/main/java/com/github/pedrovgs/deeppanel/Bitmaps.kt:
--------------------------------------------------------------------------------
1 | package com.github.pedrovgs.deeppanel
2 |
3 | import android.graphics.Bitmap
4 | import android.graphics.Canvas
5 | import android.graphics.Color
6 | import android.graphics.Matrix
7 | import android.graphics.Paint
8 | import android.graphics.RectF
9 | import java.nio.ByteBuffer
10 | import java.nio.ByteOrder
11 | import kotlin.math.max
12 |
13 | object Bitmaps {
14 |
15 | fun computeResizeScale(bitmap: Bitmap): Float {
16 | val originalSize = max(bitmap.width, bitmap.height)
17 | return originalSize / DeepPanel.modelInputImageSize.toFloat()
18 | }
19 |
20 | fun resizeInput(bitmapToResize: Bitmap): Bitmap {
21 | val reqWidth = DeepPanel.modelInputImageSize.toFloat()
22 | val reqHeight = DeepPanel.modelInputImageSize.toFloat()
23 | val matrix = Matrix()
24 | matrix.setRectToRect(
25 | RectF(0f, 0f, bitmapToResize.width.toFloat(), bitmapToResize.height.toFloat()),
26 | RectF(0f, 0f, reqWidth, reqHeight),
27 | Matrix.ScaleToFit.CENTER
28 | )
29 | val blackBackgroundBitmap =
30 | Bitmap.createBitmap(reqWidth.toInt(), reqHeight.toInt(), Bitmap.Config.ARGB_8888)
31 | val canvas = Canvas(blackBackgroundBitmap)
32 | val paint = Paint()
33 | paint.color = Color.BLACK
34 | canvas.drawRect(0f, 0f, reqWidth, reqHeight, paint)
35 | canvas.drawBitmap(bitmapToResize, matrix, Paint())
36 | return blackBackgroundBitmap
37 | }
38 |
39 | fun convertBitmapToByteBuffer(bitmap: Bitmap): ByteBuffer {
40 | val floatTypeSizeInBytes = 4
41 | val numberOfChannels = 3
42 | val modelInputSize =
43 | floatTypeSizeInBytes * DeepPanel.modelInputImageSize * DeepPanel.modelInputImageSize * numberOfChannels
44 | val imgData = ByteBuffer.allocateDirect(modelInputSize)
45 | imgData.order(ByteOrder.nativeOrder())
46 | val pixels = IntArray(DeepPanel.modelInputImageSize * DeepPanel.modelInputImageSize)
47 | bitmap.getPixels(pixels, 0, bitmap.width, 0, 0, bitmap.width, bitmap.height)
48 | for (i in pixels.indices) {
49 | val pixelInfo: Int = pixels[i]
50 | val normalizedRedChannel = Color.red(pixelInfo) / 255f
51 | imgData.putFloat(normalizedRedChannel)
52 | val normalizedGreenChannel = Color.green(pixelInfo) / 255f
53 | imgData.putFloat(normalizedGreenChannel)
54 | val normalizedBlueChannel = Color.blue(pixelInfo) / 255f
55 | imgData.putFloat(normalizedBlueChannel)
56 | }
57 | return imgData
58 | }
59 |
60 | fun createBitmapFromPrediction(prediction: Prediction): Bitmap {
61 | val imageSize = 224
62 | val bitmap = Bitmap.createBitmap(imageSize, imageSize, Bitmap.Config.ARGB_8888)
63 | for (x in 0 until imageSize) {
64 | for (y in 0 until imageSize) {
65 | val color = colorForLabel(prediction[x][y])
66 | bitmap.setPixel(x, y, color)
67 | }
68 | }
69 | return bitmap
70 | }
71 |
72 | fun generatePanelsBitmap(sourceBitmap: Bitmap, panels: Panels): Bitmap {
73 | val tempBitmap = sourceBitmap.copy(Bitmap.Config.ARGB_8888, true)
74 | val canvas = Canvas(tempBitmap)
75 | panels.panelsInfo.forEach { panel ->
76 | val paint = Paint()
77 | paint.style = Paint.Style.FILL_AND_STROKE
78 | paint.isAntiAlias = true
79 | paint.isFilterBitmap = true
80 | paint.isDither = true
81 | paint.color = colorForLabel(panel.panelNumberInPage + 2)
82 | paint.alpha = 120
83 | canvas.drawRect(
84 | panel.left.toFloat(),
85 | panel.top.toFloat(),
86 | panel.right.toFloat(),
87 | panel.bottom.toFloat(),
88 | paint
89 | )
90 | }
91 | return tempBitmap
92 | }
93 |
94 | private fun colorForLabel(
95 | label: Int
96 | ): Int = when (label) {
97 | -1 -> Color.BLACK
98 | 0 -> Color.BLUE
99 | 1 -> Color.RED
100 | 2 -> Color.GREEN
101 | 3 -> Color.CYAN
102 | 4 -> Color.YELLOW
103 | 5 -> Color.MAGENTA
104 | 6 -> Color.parseColor("#4c004f")
105 | 7 -> Color.parseColor("#084518")
106 | 8 -> Color.parseColor("#288b8f")
107 | 9 -> Color.parseColor("#8f7928")
108 | 10 -> Color.parseColor("#d993d4")
109 | 11 -> Color.parseColor("#541b14")
110 | 12 -> Color.parseColor("#a3560d")
111 | else -> Color.WHITE
112 | }
113 | }
114 |
--------------------------------------------------------------------------------
/deeppanel/src/main/java/com/github/pedrovgs/deeppanel/DeepPanel.kt:
--------------------------------------------------------------------------------
1 | package com.github.pedrovgs.deeppanel
2 |
3 | import android.content.Context
4 | import android.graphics.Bitmap
5 | import com.github.pedrovgs.deeppanel.Bitmaps.computeResizeScale
6 | import com.github.pedrovgs.deeppanel.Bitmaps.convertBitmapToByteBuffer
7 | import com.github.pedrovgs.deeppanel.Bitmaps.createBitmapFromPrediction
8 | import com.github.pedrovgs.deeppanel.Bitmaps.generatePanelsBitmap
9 | import com.github.pedrovgs.deeppanel.Bitmaps.resizeInput
10 | import com.github.pedrovgs.deeppanel.Performance.logExecutionTime
11 | import java.nio.ByteBuffer
12 | import java.nio.ByteOrder
13 | import org.tensorflow.lite.Interpreter
14 |
15 | class DeepPanel {
16 | companion object {
17 | const val modelInputImageSize = 224
18 | private lateinit var nativeDeepPanel: NativeDeepPanel
19 | private lateinit var interpreter: Interpreter
20 |
21 | fun initialize(context: Context) {
22 | if (this::interpreter.isInitialized) {
23 | return
24 | }
25 | val model = loadModel(context)
26 | interpreter = Interpreter(model)
27 | nativeDeepPanel = NativeDeepPanel()
28 | nativeDeepPanel.initialize()
29 | }
30 |
31 | private fun loadModel(context: Context): ByteBuffer {
32 | val inputStream = context.resources.openRawResource(R.raw.model)
33 | val rawModelBytes = inputStream.readBytes()
34 | val byteBuffer = ByteBuffer.allocateDirect(rawModelBytes.size)
35 | byteBuffer.order(ByteOrder.nativeOrder())
36 | byteBuffer.put(rawModelBytes)
37 | return byteBuffer
38 | }
39 | }
40 |
41 | fun extractPanelsInfo(bitmap: Bitmap): PredictionResult {
42 | val resizedImage = resizeInput(bitmap)
43 | val modelInput = convertBitmapToByteBuffer(resizedImage)
44 | val prediction =
45 | Array(1) { Array(modelInputImageSize) { Array(modelInputImageSize) { FloatArray(3) } } }
46 | interpreter.run(modelInput, prediction)
47 | val scale = computeResizeScale(bitmap)
48 | val panelsInfo = nativeDeepPanel.extractPanelsInfo(prediction[0], scale, bitmap.width, bitmap.height)
49 | val panels = composePanels(panelsInfo)
50 | return PredictionResult(panelsInfo.connectedAreas, panels)
51 | }
52 |
53 | fun extractDetailedPanelsInfo(bitmap: Bitmap): DetailedPredictionResult {
54 | val resizedImage = logExecutionTime("Resize input") { resizeInput(bitmap) }
55 | val modelInput = logExecutionTime("Resized bitmap to model input") {
56 | convertBitmapToByteBuffer(resizedImage)
57 | }
58 | val prediction =
59 | Array(1) { Array(modelInputImageSize) { Array(modelInputImageSize) { FloatArray(3) } } }
60 | logExecutionTime("Evaluate model") { interpreter.run(modelInput, prediction) }
61 | val scale = computeResizeScale(bitmap)
62 | val panelsInfo =
63 | logExecutionTime("C++ code") {
64 | nativeDeepPanel.extractPanelsInfo(
65 | prediction[0],
66 | scale,
67 | bitmap.width,
68 | bitmap.height
69 | )
70 | }
71 | val labeledAreasBitmap =
72 | logExecutionTime("Bitmap from areas") { createBitmapFromPrediction(panelsInfo.connectedAreas) }
73 | val panels = logExecutionTime("Extract panels info") { composePanels(panelsInfo) }
74 | val panelsBitmap =
75 | logExecutionTime("Bitmap from panels") { generatePanelsBitmap(bitmap, panels) }
76 | return DetailedPredictionResult(
77 | bitmap,
78 | resizedImage,
79 | labeledAreasBitmap,
80 | panelsBitmap,
81 | PredictionResult(panelsInfo.connectedAreas, panels)
82 | )
83 | }
84 |
85 | private fun composePanels(panelsInfo: RawPanelsInfo): Panels {
86 | val panels = panelsInfo.panels.mapIndexed { index, rawInfo ->
87 | Panel(
88 | panelNumberInPage = index,
89 | left = rawInfo.left,
90 | top = rawInfo.top,
91 | right = rawInfo.right,
92 | bottom = rawInfo.bottom
93 | )
94 | }
95 | return Panels(panels)
96 | }
97 | }
98 |
--------------------------------------------------------------------------------
/deeppanel/src/main/java/com/github/pedrovgs/deeppanel/NativeDeepPanel.java:
--------------------------------------------------------------------------------
1 | package com.github.pedrovgs.deeppanel;
2 |
3 | public class NativeDeepPanel {
4 |
5 | void initialize() {
6 | System.loadLibrary("deep-panel");
7 | }
8 |
9 | native RawPanelsInfo extractPanelsInfo(
10 | float[][][] prediction,
11 | float scale,
12 | int original_image_width,
13 | int original_image_height
14 | );
15 |
16 | }
17 |
18 |
19 |
--------------------------------------------------------------------------------
/deeppanel/src/main/java/com/github/pedrovgs/deeppanel/Performance.kt:
--------------------------------------------------------------------------------
1 | package com.github.pedrovgs.deeppanel
2 |
3 | import android.util.Log
4 |
5 | object Performance {
6 |
7 | fun logExecutionTime(name: String, lambda: () -> T): T {
8 | val init = System.currentTimeMillis()
9 | val result = lambda()
10 | val now = System.currentTimeMillis()
11 | val time = now - init
12 | Log.d("DeepPanel", "Time needed for $name = $time ms")
13 | return result
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/deeppanel/src/main/java/com/github/pedrovgs/deeppanel/model.kt:
--------------------------------------------------------------------------------
1 | package com.github.pedrovgs.deeppanel
2 |
3 | import android.graphics.Bitmap
4 |
5 | typealias Prediction = Array
6 |
7 | data class PredictionResult(
8 | val rawPrediction: Prediction,
9 | val panels: Panels
10 | ) {
11 | override fun equals(other: Any?): Boolean {
12 | if (this === other) return true
13 | if (javaClass != other?.javaClass) return false
14 |
15 | other as PredictionResult
16 |
17 | if (!rawPrediction.contentDeepEquals(other.rawPrediction)) return false
18 | if (panels != other.panels) return false
19 |
20 | return true
21 | }
22 |
23 | override fun hashCode(): Int {
24 | var result = rawPrediction.contentDeepHashCode()
25 | result = 31 * result + panels.hashCode()
26 | return result
27 | }
28 | }
29 |
30 | data class DetailedPredictionResult(
31 | val imageInput: Bitmap,
32 | val resizedImage: Bitmap,
33 | val labeledAreasBitmap: Bitmap,
34 | val panelsBitmap: Bitmap,
35 | val predictionResult: PredictionResult
36 | )
37 |
38 | data class Panels(val panelsInfo: List) {
39 | val numberOfPanels: Int = panelsInfo.count()
40 | }
41 |
42 | data class Panel(
43 | val panelNumberInPage: Int,
44 | val left: Int,
45 | val top: Int,
46 | val right: Int,
47 | val bottom: Int
48 | ) {
49 | val width: Int = right - left
50 | val height: Int = bottom - top
51 | }
52 |
53 | // Mutable code in data classes is really handy when initializing instances
54 | // from the JNI code.
55 | data class RawPanelsInfo(
56 | var connectedAreas: Prediction = emptyArray(),
57 | var panels: Array = emptyArray()
58 | )
59 |
60 | data class RawPanel(
61 | var left: Int = 0,
62 | var top: Int = 0,
63 | var right: Int = 0,
64 | var bottom: Int = 0
65 | )
66 |
--------------------------------------------------------------------------------
/deeppanel/src/main/res/raw/model.tflite:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pedrovgs/DeepPanelAndroid/75133803b9615cc37f744f998459d883789fd247/deeppanel/src/main/res/raw/model.tflite
--------------------------------------------------------------------------------
/deeppanel/src/test/java/com/github/pedrovgs/deeppanel/ConnectedComponentLabelingTest.kt:
--------------------------------------------------------------------------------
1 | package com.github.pedrovgs.deeppanel
2 |
3 | import org.junit.Test
4 |
5 | class ConnectedComponentLabelingTest {
6 | @Test
7 | fun test() {
8 | val input: Array> = arrayOf(
9 | arrayOf(0, 0, 0, 0, 0, 0, 0),
10 | arrayOf(0, 1, 1, 1, 1, 0, 0),
11 | arrayOf(0, 1, 2, 1, 2, 1, 0),
12 | arrayOf(0, 1, 1, 1, 2, 0, 1),
13 | arrayOf(0, 1, 2, 1, 2, 1, 0),
14 | arrayOf(0, 1, 1, 1, 1, 0, 0),
15 | arrayOf(0, 0, 0, 0, 0, 0, 0)
16 | )
17 |
18 | val result = ConnectedComponentLabeling.findAreas(input)
19 |
20 | printArray(result)
21 | }
22 |
23 | @Test
24 | fun test2() {
25 | val input: Array> = arrayOf(
26 | arrayOf(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0),
27 | arrayOf(0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0),
28 | arrayOf(0, 1, 2, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0),
29 | arrayOf(0, 1, 2, 2, 2, 1, 0, 0, 0, 0, 0, 0, 0),
30 | arrayOf(0, 1, 2, 2, 2, 1, 0, 0, 0, 0, 0, 0, 0),
31 | arrayOf(0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0),
32 | arrayOf(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0),
33 | arrayOf(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0),
34 | arrayOf(0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0),
35 | arrayOf(0, 0, 0, 0, 0, 0, 0, 1, 2, 2, 2, 2, 1),
36 | arrayOf(0, 0, 0, 0, 0, 0, 0, 1, 2, 2, 2, 1, 0),
37 | arrayOf(0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0),
38 | arrayOf(0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0)
39 | )
40 |
41 | val result = ConnectedComponentLabeling.findAreas(input)
42 |
43 | printArray(result)
44 | printArray(CCL.twoPass(input))
45 | }
46 |
47 | @Test
48 | fun test3() {
49 | val input: Array> = arrayOf(
50 | arrayOf(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0),
51 | arrayOf(0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0),
52 | arrayOf(0, 0, 1, 2, 2, 2, 1, 1, 1, 2, 1, 0, 0),
53 | arrayOf(0, 0, 1, 2, 2, 2, 1, 1, 1, 2, 1, 0, 0),
54 | arrayOf(0, 0, 1, 2, 2, 2, 2, 2, 2, 2, 1, 0, 0),
55 | arrayOf(0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0),
56 | arrayOf(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0),
57 | arrayOf(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0),
58 | arrayOf(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0),
59 | arrayOf(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0),
60 | arrayOf(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0),
61 | arrayOf(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0),
62 | arrayOf(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
63 | )
64 |
65 | val result = ConnectedComponentLabeling.findAreas(input)
66 |
67 | printArray(result)
68 | printArray(CCL.twoPass(input))
69 | }
70 |
71 | private fun printArray(result: Array>) {
72 | for (i in result.indices) {
73 | for (j in result.indices) {
74 | print(result[i][j])
75 | }
76 | println()
77 | }
78 | println()
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 | # IDE (e.g. Android Studio) users:
3 | # Gradle settings configured through the IDE *will override*
4 | # any settings specified in this file.
5 | # For more details on how to configure your build environment visit
6 | # http://www.gradle.org/docs/current/userguide/build_environment.html
7 | # Specifies the JVM arguments used for the daemon process.
8 | # The setting is particularly useful for tweaking memory settings.
9 | org.gradle.jvmargs=-Xmx2048m
10 | # When configured, Gradle will run in incubating parallel mode.
11 | # This option should only be used with decoupled projects. More details, visit
12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
13 | # org.gradle.parallel=true
14 | # AndroidX package structure to make it clearer which packages are bundled with the
15 | # Android operating system, and which are packaged with your app"s APK
16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn
17 | android.useAndroidX=true
18 | # Automatically convert third-party libraries to use AndroidX
19 | android.enableJetifier=true
20 | # Kotlin code style for this project: "official" or "obsolete":
21 | kotlin.code.style=official
22 |
23 | VERSION_NAME=0.0.1
24 | VERSION_CODE=000001
25 | GROUP=com.github.pedrovgs
26 | POM_NAME=DeepPanel for Android
27 | POM_ARTIFACT_ID=deeppanel
28 | POM_PACKAGING=aar
29 | POM_DESCRIPTION=Android library used to implement comic vignettes segmentation using a machine learning method named deep learning.
30 | POM_URL=https://github.com/pedrovgs/DeepPanelAndroid
31 | POM_SCM_URL=https://github.com/pedrovgs/DeepPanelAndroid
32 | POM_SCM_CONNECTION=scm:git@github.com:pedrovgs/DeepPanelAndroid.git
33 | POM_SCM_DEV_CONNECTION=scm:git@github.com:pedrovgs/DeepPanelAndroid.git
34 | POM_LICENCE_NAME=The Apache Software License, Version 2.0
35 | POM_LICENCE_URL=http://www.apache.org/licenses/LICENSE-2.0.txt
36 | POM_LICENCE_DIST=repo
37 | POM_DEVELOPER_ID=pedrovgs
38 | POM_DEVELOPER_NAME=Pedro Vicente Gómez Sánchez
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pedrovgs/DeepPanelAndroid/75133803b9615cc37f744f998459d883789fd247/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Sun Jun 14 18:43:55 CEST 2020
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-all.zip
7 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Attempt to set APP_HOME
10 | # Resolve links: $0 may be a link
11 | PRG="$0"
12 | # Need this for relative symlinks.
13 | while [ -h "$PRG" ] ; do
14 | ls=`ls -ld "$PRG"`
15 | link=`expr "$ls" : '.*-> \(.*\)$'`
16 | if expr "$link" : '/.*' > /dev/null; then
17 | PRG="$link"
18 | else
19 | PRG=`dirname "$PRG"`"/$link"
20 | fi
21 | done
22 | SAVED="`pwd`"
23 | cd "`dirname \"$PRG\"`/" >/dev/null
24 | APP_HOME="`pwd -P`"
25 | cd "$SAVED" >/dev/null
26 |
27 | APP_NAME="Gradle"
28 | APP_BASE_NAME=`basename "$0"`
29 |
30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
31 | DEFAULT_JVM_OPTS=""
32 |
33 | # Use the maximum available, or set MAX_FD != -1 to use that value.
34 | MAX_FD="maximum"
35 |
36 | warn () {
37 | echo "$*"
38 | }
39 |
40 | die () {
41 | echo
42 | echo "$*"
43 | echo
44 | exit 1
45 | }
46 |
47 | # OS specific support (must be 'true' or 'false').
48 | cygwin=false
49 | msys=false
50 | darwin=false
51 | nonstop=false
52 | case "`uname`" in
53 | CYGWIN* )
54 | cygwin=true
55 | ;;
56 | Darwin* )
57 | darwin=true
58 | ;;
59 | MINGW* )
60 | msys=true
61 | ;;
62 | NONSTOP* )
63 | nonstop=true
64 | ;;
65 | esac
66 |
67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
68 |
69 | # Determine the Java command to use to start the JVM.
70 | if [ -n "$JAVA_HOME" ] ; then
71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
72 | # IBM's JDK on AIX uses strange locations for the executables
73 | JAVACMD="$JAVA_HOME/jre/sh/java"
74 | else
75 | JAVACMD="$JAVA_HOME/bin/java"
76 | fi
77 | if [ ! -x "$JAVACMD" ] ; then
78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
79 |
80 | Please set the JAVA_HOME variable in your environment to match the
81 | location of your Java installation."
82 | fi
83 | else
84 | JAVACMD="java"
85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
86 |
87 | Please set the JAVA_HOME variable in your environment to match the
88 | location of your Java installation."
89 | fi
90 |
91 | # Increase the maximum file descriptors if we can.
92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
93 | MAX_FD_LIMIT=`ulimit -H -n`
94 | if [ $? -eq 0 ] ; then
95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
96 | MAX_FD="$MAX_FD_LIMIT"
97 | fi
98 | ulimit -n $MAX_FD
99 | if [ $? -ne 0 ] ; then
100 | warn "Could not set maximum file descriptor limit: $MAX_FD"
101 | fi
102 | else
103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
104 | fi
105 | fi
106 |
107 | # For Darwin, add options to specify how the application appears in the dock
108 | if $darwin; then
109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
110 | fi
111 |
112 | # For Cygwin, switch paths to Windows format before running java
113 | if $cygwin ; then
114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
116 | JAVACMD=`cygpath --unix "$JAVACMD"`
117 |
118 | # We build the pattern for arguments to be converted via cygpath
119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 | SEP=""
121 | for dir in $ROOTDIRSRAW ; do
122 | ROOTDIRS="$ROOTDIRS$SEP$dir"
123 | SEP="|"
124 | done
125 | OURCYGPATTERN="(^($ROOTDIRS))"
126 | # Add a user-defined pattern to the cygpath arguments
127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 | fi
130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 | i=0
132 | for arg in "$@" ; do
133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135 |
136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 | else
139 | eval `echo args$i`="\"$arg\""
140 | fi
141 | i=$((i+1))
142 | done
143 | case $i in
144 | (0) set -- ;;
145 | (1) set -- "$args0" ;;
146 | (2) set -- "$args0" "$args1" ;;
147 | (3) set -- "$args0" "$args1" "$args2" ;;
148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 | esac
155 | fi
156 |
157 | # Escape application args
158 | save () {
159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
160 | echo " "
161 | }
162 | APP_ARGS=$(save "$@")
163 |
164 | # Collect all arguments for the java command, following the shell quoting and substitution rules
165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
166 |
167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
169 | cd "$(dirname "$0")"
170 | fi
171 |
172 | exec "$JAVACMD" "$@"
173 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | set DIRNAME=%~dp0
12 | if "%DIRNAME%" == "" set DIRNAME=.
13 | set APP_BASE_NAME=%~n0
14 | set APP_HOME=%DIRNAME%
15 |
16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
17 | set DEFAULT_JVM_OPTS=
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windows variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 |
53 | :win9xME_args
54 | @rem Slurp the command line arguments.
55 | set CMD_LINE_ARGS=
56 | set _SKIP=2
57 |
58 | :win9xME_args_slurp
59 | if "x%~1" == "x" goto execute
60 |
61 | set CMD_LINE_ARGS=%*
62 |
63 | :execute
64 | @rem Setup the command line
65 |
66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
67 |
68 | @rem Execute Gradle
69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
70 |
71 | :end
72 | @rem End local scope for the variables with windows NT shell
73 | if "%ERRORLEVEL%"=="0" goto mainEnd
74 |
75 | :fail
76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
77 | rem the _cmd.exe /c_ return code!
78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
79 | exit /b 1
80 |
81 | :mainEnd
82 | if "%OS%"=="Windows_NT" endlocal
83 |
84 | :omega
85 |
--------------------------------------------------------------------------------
/release.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'maven'
2 | apply plugin: 'signing'
3 |
4 | def isReleaseBuild() {
5 | return VERSION_NAME.contains("SNAPSHOT") == false
6 | }
7 |
8 | def getReleaseRepositoryUrl() {
9 | return hasProperty('RELEASE_REPOSITORY_URL') ? RELEASE_REPOSITORY_URL
10 | : "https://oss.sonatype.org/service/local/staging/deploy/maven2/"
11 | }
12 |
13 | def getSnapshotRepositoryUrl() {
14 | return hasProperty('SNAPSHOT_REPOSITORY_URL') ? SNAPSHOT_REPOSITORY_URL
15 | : "https://oss.sonatype.org/content/repositories/snapshots/"
16 | }
17 |
18 | def getRepositoryUsername() {
19 | return hasProperty('NEXUS_USERNAME') ? NEXUS_USERNAME : ""
20 | }
21 |
22 | def getRepositoryPassword() {
23 | return hasProperty('NEXUS_PASSWORD') ? NEXUS_PASSWORD : ""
24 | }
25 |
26 | afterEvaluate { project ->
27 | uploadArchives {
28 | repositories {
29 | mavenDeployer {
30 | beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) }
31 |
32 | pom.groupId = GROUP
33 | pom.artifactId = POM_ARTIFACT_ID
34 | pom.version = VERSION_NAME
35 |
36 | if (isReleaseBuild()) {
37 | repository(url: getReleaseRepositoryUrl()) {
38 | authentication(userName: getRepositoryUsername(), password: getRepositoryPassword())
39 | }
40 | snapshotRepository(url: getSnapshotRepositoryUrl()) {
41 | authentication(userName: getRepositoryUsername(), password: getRepositoryPassword())
42 | }
43 | } else {
44 | repository(url: uri('../repo'))
45 | }
46 |
47 | pom.project {
48 | name POM_NAME
49 | packaging POM_PACKAGING
50 | description POM_DESCRIPTION
51 | url POM_URL
52 |
53 | scm {
54 | url POM_SCM_URL
55 | connection POM_SCM_CONNECTION
56 | developerConnection POM_SCM_DEV_CONNECTION
57 | }
58 |
59 | licenses {
60 | license {
61 | name POM_LICENCE_NAME
62 | url POM_LICENCE_URL
63 | distribution POM_LICENCE_DIST
64 | }
65 | }
66 |
67 | developers {
68 | developer {
69 | id POM_DEVELOPER_ID
70 | name POM_DEVELOPER_NAME
71 | }
72 | }
73 | }
74 | }
75 | }
76 | }
77 |
78 | signing {
79 | required { isReleaseBuild() && gradle.taskGraph.hasTask("uploadArchives") }
80 | sign configurations.archives
81 | }
82 |
83 | def isAndroidProject = project.getPlugins().hasPlugin('com.android.application') || project.getPlugins().hasPlugin('com.android.library')
84 | if (isAndroidProject) {
85 | task androidSourcesJar(type: Jar) {
86 | classifier = 'sources'
87 | from android.sourceSets.main.java.srcDirs
88 | }
89 |
90 | artifacts {
91 | archives androidSourcesJar
92 | }
93 | } else {
94 | task javadocJar(type: Jar) {
95 | classifier = 'javadoc'
96 | from javadoc
97 | }
98 |
99 | task sourcesJar(type: Jar) {
100 | classifier = 'sources'
101 | from sourceSets.main.allSource
102 | }
103 |
104 | artifacts {
105 | archives javadocJar, sourcesJar
106 | }
107 | }
108 |
109 | }
--------------------------------------------------------------------------------
/scripts/config.ini:
--------------------------------------------------------------------------------
1 | PlayStore.enabled=false
2 | abi.type=x86
3 | avd.ini.encoding=UTF-8
4 | hw.cpu.arch=x86
5 | hw.sdCard=yes
6 | image.sysdir.1=system-images/android-26/google_apis/x86/
7 | sdcard.path=sdcard.img
8 | tag.display=Google APIs
9 | tag.id=google_apis
10 | hw.lcd.density=320
11 | hw.lcd.width=768
12 | hw.lcd.height=1280
13 | hw.keyboard=yes
14 |
--------------------------------------------------------------------------------
/scripts/create_emulator.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | if $ANDROID_HOME/tools/android list avd | grep -q Nexus_5X_API_26; then
4 | echo "There is an existing an emulator to run screenshot tests"
5 | exit 0;
6 | fi
7 |
8 | echo "Creating a brand new SDCard..."
9 | rm -rf sdcard.img
10 | $ANDROID_HOME/emulator/mksdcard -l e 1G sdcard.img
11 | echo "SDCard created!"
12 |
13 | echo "Downloading the image to create the emulator..."
14 | echo no | $ANDROID_HOME/tools/bin/sdkmanager "system-images;android-26;google_apis;x86"
15 | echo "Image downloaded!"
16 |
17 | echo "Creating the emulator to run screenshot tests..."
18 | echo no | $ANDROID_HOME/tools/bin/avdmanager create avd -n Nexus_5X_API_26 -k "system-images;android-26;google_apis;x86" --force --sdcard sdcard.img
19 | echo "Emulator created!"
20 |
21 | DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
22 | cp $DIR/config.ini ~/.android/avd/Nexus_5X_API_26.avd/config.ini
23 | cp sdcard.img ~/.android/avd/Nexus_5X_API_26.avd/sdcard.img
24 | cp sdcard.img.qcow2 ~/.android/avd/Nexus_5X_API_26.avd/sdcard.img.qcow2
25 |
--------------------------------------------------------------------------------
/scripts/disable_animations.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | $ANDROID_HOME/platform-tools/adb wait-for-device
4 | $ANDROID_HOME/platform-tools/adb shell settings put global window_animation_scale 0
5 | $ANDROID_HOME/platform-tools/adb shell settings put global transition_animation_scale 0
6 | $ANDROID_HOME/platform-tools/adb shell settings put global animator_duration_scale 0
7 |
--------------------------------------------------------------------------------
/scripts/enable_animations.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | $ANDROID_HOME/platform-tools/adb wait-for-device
4 | $ANDROID_HOME/platform-tools/adb shell settings put global window_animation_scale 1
5 | $ANDROID_HOME/platform-tools/adb shell settings put global transition_animation_scale 1
6 | $ANDROID_HOME/platform-tools/adb shell settings put global animator_duration_scale 1
7 |
--------------------------------------------------------------------------------
/scripts/start_emulator.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
4 |
5 | if ! $ANDROID_HOME/tools/android list avd | grep -q Nexus_5X_API_26; then
6 | echo "No emulator for screenshot tests found, creating one..."
7 | $DIR/create_emulator.sh
8 | fi
9 |
10 | if $ANDROID_HOME/platform-tools/adb devices -l | grep -q emulator; then
11 | echo "Emulator already running"
12 | exit 0
13 | fi
14 |
15 | echo "Starting emulator..."
16 | echo "no" | $ANDROID_HOME/emulator/emulator "-avd" "Nexus_5X_API_26" "-no-audio" "-no-boot-anim" &
17 |
18 | $DIR/wait_for_emulator.sh
19 |
20 | echo "Emulator ready, disabling animations!"
21 |
22 | $DIR/disable_animations.sh
23 |
24 | echo "Emulator started and ready to rock!"
25 |
--------------------------------------------------------------------------------
/scripts/stop_emulator.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | echo "Stopping emulator..."
4 | $ANDROID_HOME/platform-tools/adb emu kill
5 | echo "Emulator stopped!"
6 |
--------------------------------------------------------------------------------
/scripts/wait_for_emulator.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | set +e
4 |
5 | bootanim=""
6 | failcounter=0
7 | until [[ "$bootanim" =~ "stopped" ]]; do
8 | bootanim=`$ANDROID_HOME/platform-tools/adb -e shell getprop init.svc.bootanim 2>&1`
9 | echo "$bootanim"
10 | if [[ "$bootanim" =~ "not found" ]]; then
11 | let "failcounter += 1"
12 | if [[ $failcounter -gt 15 ]]; then
13 | echo "Failed to start emulator"
14 | exit 1
15 | fi
16 | fi
17 | sleep 1
18 | done
19 | echo "Done"
20 |
21 |
22 | echo "Emulator is ready"
23 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':deeppanel'
2 | include ':app'
3 | rootProject.name = "DeepPanelAndroid"
--------------------------------------------------------------------------------