From 1d15036f899fc82640d979b17cc44ab8c957df79 Mon Sep 17 00:00:00 2001 From: nyatla Date: Sat, 7 Jun 2008 01:14:25 +0000 Subject: [PATCH] =?utf8?q?[=E3=83=AA=E3=83=AA=E3=83=BC=E3=82=B9]NyARToolki?= =?utf8?q?t=200.5?= MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit --- Data/320x240ABGR.raw | Bin 0 -> 307200 bytes Data/camera_para.dat | Bin 0 -> 136 bytes Data/patt.hiro | 196 +++++ Data/pattHiro.pdf | Bin 0 -> 1572 bytes LICENCE.txt | 35 + bin/readme.txt | 3 + readme.ja.txt | 117 +++ sample/jmf/.classpath | 8 + sample/jmf/.project | 17 + sample/jmf/JmfCaptureTest.java | 56 ++ sample/jmf/NyarToolkitLinkTest.java | 108 +++ .../nyatla/nyartoolkit/jmf/JmfCameraCapture.java | 175 +++++ .../nyatla/nyartoolkit/jmf/JmfCaptureListener.java | 14 + .../jmf/jp/nyatla/nyartoolkit/jmf/MonitorCDS.java | 136 ++++ .../jp/nyatla/nyartoolkit/jmf/MonitorStream.java | 196 +++++ sample/jogl/.classpath | 11 + sample/jogl/.project | 17 + sample/jogl/JavaSimpleLite.java | 224 ++++++ .../jp/nyatla/nyartoolkit/gutil/GLNyARParam.java | 99 +++ .../nyatla/nyartoolkit/gutil/GLNyARRaster_RGB.java | 118 +++ .../gutil/GLNyARSingleDetectMarker.java | 57 ++ src/.classpath | 6 + src/.project | 17 + src/jp/nyatla/nyartoolkit/NyARException.java | 49 ++ src/jp/nyatla/nyartoolkit/base/Config.java | 75 ++ src/jp/nyatla/nyartoolkit/base/Param.java | 366 +++++++++ src/jp/nyatla/nyartoolkit/core/NyARCode.java | 165 ++++ src/jp/nyatla/nyartoolkit/core/NyARColorPatt.java | 227 ++++++ .../nyatla/nyartoolkit/core/NyARDetectMarker.java | 403 ++++++++++ .../nyatla/nyartoolkit/core/NyARDetectSquare.java | 175 +++++ src/jp/nyatla/nyartoolkit/core/NyARLabeling.java | 383 +++++++++ src/jp/nyatla/nyartoolkit/core/NyARMarker.java | 60 ++ src/jp/nyatla/nyartoolkit/core/NyARMat.java | 861 +++++++++++++++++++++ src/jp/nyatla/nyartoolkit/core/NyARParam.java | 351 +++++++++ src/jp/nyatla/nyartoolkit/core/NyARSquare.java | 57 ++ src/jp/nyatla/nyartoolkit/core/NyARTransMat.java | 817 +++++++++++++++++++ src/jp/nyatla/nyartoolkit/core/NyARVec.java | 243 ++++++ src/jp/nyatla/nyartoolkit/core/NyARVersion.java | 66 ++ .../nyartoolkit/core/match/NyARMatchPatt.java | 59 ++ .../core/match/NyARMatchPatt_BlackWhite.java | 112 +++ .../match/NyARMatchPatt_Color_WITHOUT_PCA.java | 118 +++ .../core/match/NyARMatchPatt_Color_WITH_PCA.java | 146 ++++ .../nyatla/nyartoolkit/core/raster/NyARRaster.java | 43 + .../nyartoolkit/core/raster/NyARRaster_BGRA.java | 70 ++ .../nyartoolkit/core/raster/NyARRaster_Blank.java | 68 ++ .../nyartoolkit/core/raster/NyARRaster_RGB.java | 70 ++ .../detector/NyARSingleDetectMarker.java | 175 +++++ src/jp/nyatla/nyartoolkit/sample/RawFileTest.java | 95 +++ src/jp/nyatla/util/BytePointer.java | 56 ++ src/jp/nyatla/util/DoublePointer.java | 106 +++ src/jp/nyatla/util/DoubleValue.java | 14 + src/jp/nyatla/util/IntPointer.java | 64 ++ src/jp/nyatla/util/IntValue.java | 17 + src/jp/nyatla/util/ShortPointer.java | 60 ++ src/jp/nyatla/util/StringPointer.java | 25 + src/jp/nyatla/util/StringValue.java | 25 + 56 files changed, 7231 insertions(+) create mode 100644 Data/320x240ABGR.raw create mode 100644 Data/camera_para.dat create mode 100644 Data/patt.hiro create mode 100644 Data/pattHiro.pdf create mode 100644 LICENCE.txt create mode 100644 bin/readme.txt create mode 100644 readme.ja.txt create mode 100644 sample/jmf/.classpath create mode 100644 sample/jmf/.project create mode 100644 sample/jmf/JmfCaptureTest.java create mode 100644 sample/jmf/NyarToolkitLinkTest.java create mode 100644 sample/jmf/jp/nyatla/nyartoolkit/jmf/JmfCameraCapture.java create mode 100644 sample/jmf/jp/nyatla/nyartoolkit/jmf/JmfCaptureListener.java create mode 100644 sample/jmf/jp/nyatla/nyartoolkit/jmf/MonitorCDS.java create mode 100644 sample/jmf/jp/nyatla/nyartoolkit/jmf/MonitorStream.java create mode 100644 sample/jogl/.classpath create mode 100644 sample/jogl/.project create mode 100644 sample/jogl/JavaSimpleLite.java create mode 100644 sample/jogl/jp/nyatla/nyartoolkit/gutil/GLNyARParam.java create mode 100644 sample/jogl/jp/nyatla/nyartoolkit/gutil/GLNyARRaster_RGB.java create mode 100644 sample/jogl/jp/nyatla/nyartoolkit/gutil/GLNyARSingleDetectMarker.java create mode 100644 src/.classpath create mode 100644 src/.project create mode 100644 src/jp/nyatla/nyartoolkit/NyARException.java create mode 100644 src/jp/nyatla/nyartoolkit/base/Config.java create mode 100644 src/jp/nyatla/nyartoolkit/base/Param.java create mode 100644 src/jp/nyatla/nyartoolkit/core/NyARCode.java create mode 100644 src/jp/nyatla/nyartoolkit/core/NyARColorPatt.java create mode 100644 src/jp/nyatla/nyartoolkit/core/NyARDetectMarker.java create mode 100644 src/jp/nyatla/nyartoolkit/core/NyARDetectSquare.java create mode 100644 src/jp/nyatla/nyartoolkit/core/NyARLabeling.java create mode 100644 src/jp/nyatla/nyartoolkit/core/NyARMarker.java create mode 100644 src/jp/nyatla/nyartoolkit/core/NyARMat.java create mode 100644 src/jp/nyatla/nyartoolkit/core/NyARParam.java create mode 100644 src/jp/nyatla/nyartoolkit/core/NyARSquare.java create mode 100644 src/jp/nyatla/nyartoolkit/core/NyARTransMat.java create mode 100644 src/jp/nyatla/nyartoolkit/core/NyARVec.java create mode 100644 src/jp/nyatla/nyartoolkit/core/NyARVersion.java create mode 100644 src/jp/nyatla/nyartoolkit/core/match/NyARMatchPatt.java create mode 100644 src/jp/nyatla/nyartoolkit/core/match/NyARMatchPatt_BlackWhite.java create mode 100644 src/jp/nyatla/nyartoolkit/core/match/NyARMatchPatt_Color_WITHOUT_PCA.java create mode 100644 src/jp/nyatla/nyartoolkit/core/match/NyARMatchPatt_Color_WITH_PCA.java create mode 100644 src/jp/nyatla/nyartoolkit/core/raster/NyARRaster.java create mode 100644 src/jp/nyatla/nyartoolkit/core/raster/NyARRaster_BGRA.java create mode 100644 src/jp/nyatla/nyartoolkit/core/raster/NyARRaster_Blank.java create mode 100644 src/jp/nyatla/nyartoolkit/core/raster/NyARRaster_RGB.java create mode 100644 src/jp/nyatla/nyartoolkit/detector/NyARSingleDetectMarker.java create mode 100644 src/jp/nyatla/nyartoolkit/sample/RawFileTest.java create mode 100644 src/jp/nyatla/util/BytePointer.java create mode 100644 src/jp/nyatla/util/DoublePointer.java create mode 100644 src/jp/nyatla/util/DoubleValue.java create mode 100644 src/jp/nyatla/util/IntPointer.java create mode 100644 src/jp/nyatla/util/IntValue.java create mode 100644 src/jp/nyatla/util/ShortPointer.java create mode 100644 src/jp/nyatla/util/StringPointer.java create mode 100644 src/jp/nyatla/util/StringValue.java diff --git a/Data/320x240ABGR.raw b/Data/320x240ABGR.raw new file mode 100644 index 0000000000000000000000000000000000000000..b5be0700c628060a781c4046002f708fb61f8a0d GIT binary patch literal 307200 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�VNuwA-0+o{as)KdJoxGaZe$ySk4%?r6NN|K8Gn yZ&&8{?2dhY`8S^P!=L9*^c%#F#mHx1K9BG0Gd=I8dUnn8o*8f6{W!iu?)!gTn>QE$ literal 0 HcmV?d00001 diff --git a/Data/camera_para.dat b/Data/camera_para.dat new file mode 100644 index 0000000000000000000000000000000000000000..eb671b1e7956a6f103e36138958f92b4194e8f75 GIT binary patch literal 136 zcmZQzU}|7sV0_@v`h3ou(o60PP~cE}0>(t49ojY=c;w$V%^}YKMI4=D{{dCRq4)(@ Vz@eZ5LR%Swf&B*)2fac?9sulL6vqGn literal 0 HcmV?d00001 diff --git a/Data/patt.hiro b/Data/patt.hiro new file mode 100644 index 0000000..7f78e3c --- /dev/null +++ b/Data/patt.hiro @@ -0,0 +1,196 @@ + 234 235 240 233 240 234 240 235 240 237 240 238 240 240 240 232 + 229 240 240 240 240 240 240 240 240 240 240 240 240 240 240 228 + 227 240 240 240 240 240 240 240 240 240 240 240 240 240 240 239 + 240 240 240 240 240 240 240 240 240 240 240 240 240 240 240 240 + 236 240 240 240 240 240 240 240 240 240 240 240 240 240 240 240 + 234 240 240 240 240 240 240 240 240 240 240 240 240 240 240 240 + 236 240 240 240 240 240 240 240 240 240 240 240 240 240 240 240 + 231 240 240 240 240 240 240 240 240 240 240 240 240 240 240 240 + 229 240 240 240 240 240 240 240 240 240 240 240 240 240 240 240 + 225 149 240 240 186 216 225 174 240 240 240 237 238 240 240 240 + 150 107 238 231 75 208 115 147 238 228 223 226 237 180 226 240 + 150 62 181 213 62 187 113 169 197 72 29 237 120 50 53 207 + 149 63 47 78 53 184 113 101 142 5 150 150 45 217 186 83 + 121 84 220 222 58 180 121 92 128 109 237 124 155 232 161 64 + 149 71 240 240 76 210 98 109 122 108 240 129 51 119 161 155 + 149 186 240 240 98 219 135 152 207 191 236 227 152 77 175 209 + 235 235 240 233 240 234 240 235 240 236 240 238 240 240 240 240 + 229 240 240 240 240 240 240 240 240 240 240 240 240 240 240 240 + 227 240 240 240 240 240 240 240 240 240 240 240 240 240 240 240 + 240 240 240 240 240 240 240 240 240 240 240 240 240 240 240 240 + 236 240 240 240 240 240 240 240 240 240 240 240 240 240 240 240 + 234 240 240 240 240 240 240 240 240 240 240 240 240 240 240 240 + 236 240 240 240 240 240 240 240 240 240 240 240 240 240 240 240 + 232 240 240 240 240 240 240 240 240 240 240 240 240 240 240 240 + 229 240 240 240 240 240 240 240 240 240 240 240 240 240 240 240 + 225 156 240 240 186 216 225 186 240 240 240 240 240 240 240 240 + 150 117 240 231 72 206 115 162 240 232 223 237 240 180 226 240 + 150 74 187 213 51 184 103 168 197 78 29 237 120 50 53 216 + 144 77 51 74 61 184 106 101 142 5 150 152 52 217 186 85 + 117 89 219 219 65 184 121 92 128 100 236 125 156 240 170 73 + 148 71 240 240 76 210 109 109 121 99 240 137 51 120 166 164 + 140 186 240 240 98 220 150 156 207 192 236 230 152 77 176 212 + 234 235 240 233 240 234 240 235 240 236 240 238 240 240 240 233 + 229 240 240 240 240 240 240 240 240 240 240 240 240 240 240 239 + 227 240 240 240 240 240 240 240 240 240 240 240 240 240 240 240 + 240 240 240 240 240 240 240 240 240 240 240 240 240 240 240 240 + 234 240 240 240 240 240 240 240 240 240 240 240 240 240 240 240 + 232 240 240 240 240 240 240 240 240 240 240 240 240 240 240 240 + 235 240 240 240 240 240 240 240 240 240 240 240 240 240 240 240 + 232 240 240 240 240 240 240 240 240 240 240 240 240 240 240 240 + 228 240 240 240 240 240 240 240 240 240 240 240 240 240 240 240 + 225 156 240 240 182 212 225 180 240 240 240 240 240 240 240 240 + 150 116 238 228 66 205 115 151 238 236 225 240 240 180 226 240 + 156 84 186 211 47 184 109 170 200 92 30 240 120 50 53 216 + 147 83 51 73 50 184 106 110 148 17 151 150 45 217 186 85 + 127 98 219 219 58 179 109 101 128 107 237 125 155 240 163 72 + 155 86 240 240 76 201 85 108 121 95 232 137 51 118 153 155 + 149 189 240 240 98 220 141 154 206 178 235 230 152 77 175 209 + + 232 228 239 240 240 240 240 240 240 240 240 207 83 64 155 209 + 240 240 240 240 240 240 240 240 240 240 226 53 186 161 161 175 + 240 240 240 240 240 240 240 240 240 240 180 50 217 232 119 77 + 240 240 240 240 240 240 240 240 240 238 237 120 45 155 51 152 + 238 240 240 240 240 240 240 240 240 237 226 237 150 124 129 227 + 240 240 240 240 240 240 240 240 240 240 223 29 150 237 240 236 + 237 240 240 240 240 240 240 240 240 240 228 72 5 109 108 191 + 240 240 240 240 240 240 240 240 240 240 238 197 142 128 122 207 + 235 240 240 240 240 240 240 240 240 174 147 169 101 92 109 152 + 240 240 240 240 240 240 240 240 240 225 115 113 113 121 98 135 + 234 240 240 240 240 240 240 240 240 216 208 187 184 180 210 219 + 240 240 240 240 240 240 240 240 240 186 75 62 53 58 76 98 + 233 240 240 240 240 240 240 240 240 240 231 213 78 222 240 240 + 240 240 240 240 240 240 240 240 240 240 238 181 47 220 240 240 + 235 240 240 240 240 240 240 240 240 149 107 62 63 84 71 186 + 234 229 227 240 236 234 236 231 229 225 150 150 149 121 149 149 + 240 240 240 240 240 240 240 240 240 240 240 216 85 73 164 212 + 240 240 240 240 240 240 240 240 240 240 226 53 186 170 166 176 + 240 240 240 240 240 240 240 240 240 240 180 50 217 240 120 77 + 240 240 240 240 240 240 240 240 240 240 240 120 52 156 51 152 + 238 240 240 240 240 240 240 240 240 240 237 237 152 125 137 230 + 240 240 240 240 240 240 240 240 240 240 223 29 150 236 240 236 + 236 240 240 240 240 240 240 240 240 240 232 78 5 100 99 192 + 240 240 240 240 240 240 240 240 240 240 240 197 142 128 121 207 + 235 240 240 240 240 240 240 240 240 186 162 168 101 92 109 156 + 240 240 240 240 240 240 240 240 240 225 115 103 106 121 109 150 + 234 240 240 240 240 240 240 240 240 216 206 184 184 184 210 220 + 240 240 240 240 240 240 240 240 240 186 72 51 61 65 76 98 + 233 240 240 240 240 240 240 240 240 240 231 213 74 219 240 240 + 240 240 240 240 240 240 240 240 240 240 240 187 51 219 240 240 + 235 240 240 240 240 240 240 240 240 156 117 74 77 89 71 186 + 235 229 227 240 236 234 236 232 229 225 150 150 144 117 148 140 + 233 239 240 240 240 240 240 240 240 240 240 216 85 72 155 209 + 240 240 240 240 240 240 240 240 240 240 226 53 186 163 153 175 + 240 240 240 240 240 240 240 240 240 240 180 50 217 240 118 77 + 240 240 240 240 240 240 240 240 240 240 240 120 45 155 51 152 + 238 240 240 240 240 240 240 240 240 240 240 240 150 125 137 230 + 240 240 240 240 240 240 240 240 240 240 225 30 151 237 232 235 + 236 240 240 240 240 240 240 240 240 240 236 92 17 107 95 178 + 240 240 240 240 240 240 240 240 240 240 238 200 148 128 121 206 + 235 240 240 240 240 240 240 240 240 180 151 170 110 101 108 154 + 240 240 240 240 240 240 240 240 240 225 115 109 106 109 85 141 + 234 240 240 240 240 240 240 240 240 212 205 184 184 179 201 220 + 240 240 240 240 240 240 240 240 240 182 66 47 50 58 76 98 + 233 240 240 240 240 240 240 240 240 240 228 211 73 219 240 240 + 240 240 240 240 240 240 240 240 240 240 238 186 51 219 240 240 + 235 240 240 240 240 240 240 240 240 156 116 84 83 98 86 189 + 234 229 227 240 234 232 235 232 228 225 150 156 147 127 155 149 + + 209 175 77 152 227 236 191 207 152 135 219 98 240 240 186 149 + 155 161 119 51 129 240 108 122 109 98 210 76 240 240 71 149 + 64 161 232 155 124 237 109 128 92 121 180 58 222 220 84 121 + 83 186 217 45 150 150 5 142 101 113 184 53 78 47 63 149 + 207 53 50 120 237 29 72 197 169 113 187 62 213 181 62 150 + 240 226 180 237 226 223 228 238 147 115 208 75 231 238 107 150 + 240 240 240 238 237 240 240 240 174 225 216 186 240 240 149 225 + 240 240 240 240 240 240 240 240 240 240 240 240 240 240 240 229 + 240 240 240 240 240 240 240 240 240 240 240 240 240 240 240 231 + 240 240 240 240 240 240 240 240 240 240 240 240 240 240 240 236 + 240 240 240 240 240 240 240 240 240 240 240 240 240 240 240 234 + 240 240 240 240 240 240 240 240 240 240 240 240 240 240 240 236 + 240 240 240 240 240 240 240 240 240 240 240 240 240 240 240 240 + 239 240 240 240 240 240 240 240 240 240 240 240 240 240 240 227 + 228 240 240 240 240 240 240 240 240 240 240 240 240 240 240 229 + 232 240 240 240 238 240 237 240 235 240 234 240 233 240 235 234 + 212 176 77 152 230 236 192 207 156 150 220 98 240 240 186 140 + 164 166 120 51 137 240 99 121 109 109 210 76 240 240 71 148 + 73 170 240 156 125 236 100 128 92 121 184 65 219 219 89 117 + 85 186 217 52 152 150 5 142 101 106 184 61 74 51 77 144 + 216 53 50 120 237 29 78 197 168 103 184 51 213 187 74 150 + 240 226 180 240 237 223 232 240 162 115 206 72 231 240 117 150 + 240 240 240 240 240 240 240 240 186 225 216 186 240 240 156 225 + 240 240 240 240 240 240 240 240 240 240 240 240 240 240 240 229 + 240 240 240 240 240 240 240 240 240 240 240 240 240 240 240 232 + 240 240 240 240 240 240 240 240 240 240 240 240 240 240 240 236 + 240 240 240 240 240 240 240 240 240 240 240 240 240 240 240 234 + 240 240 240 240 240 240 240 240 240 240 240 240 240 240 240 236 + 240 240 240 240 240 240 240 240 240 240 240 240 240 240 240 240 + 240 240 240 240 240 240 240 240 240 240 240 240 240 240 240 227 + 240 240 240 240 240 240 240 240 240 240 240 240 240 240 240 229 + 240 240 240 240 238 240 236 240 235 240 234 240 233 240 235 235 + 209 175 77 152 230 235 178 206 154 141 220 98 240 240 189 149 + 155 153 118 51 137 232 95 121 108 85 201 76 240 240 86 155 + 72 163 240 155 125 237 107 128 101 109 179 58 219 219 98 127 + 85 186 217 45 150 151 17 148 110 106 184 50 73 51 83 147 + 216 53 50 120 240 30 92 200 170 109 184 47 211 186 84 156 + 240 226 180 240 240 225 236 238 151 115 205 66 228 238 116 150 + 240 240 240 240 240 240 240 240 180 225 212 182 240 240 156 225 + 240 240 240 240 240 240 240 240 240 240 240 240 240 240 240 228 + 240 240 240 240 240 240 240 240 240 240 240 240 240 240 240 232 + 240 240 240 240 240 240 240 240 240 240 240 240 240 240 240 235 + 240 240 240 240 240 240 240 240 240 240 240 240 240 240 240 232 + 240 240 240 240 240 240 240 240 240 240 240 240 240 240 240 234 + 240 240 240 240 240 240 240 240 240 240 240 240 240 240 240 240 + 240 240 240 240 240 240 240 240 240 240 240 240 240 240 240 227 + 239 240 240 240 240 240 240 240 240 240 240 240 240 240 240 229 + 233 240 240 240 238 240 236 240 235 240 234 240 233 240 235 234 + + 149 149 121 149 150 150 225 229 231 236 234 236 240 227 229 234 + 186 71 84 63 62 107 149 240 240 240 240 240 240 240 240 235 + 240 240 220 47 181 238 240 240 240 240 240 240 240 240 240 240 + 240 240 222 78 213 231 240 240 240 240 240 240 240 240 240 233 + 98 76 58 53 62 75 186 240 240 240 240 240 240 240 240 240 + 219 210 180 184 187 208 216 240 240 240 240 240 240 240 240 234 + 135 98 121 113 113 115 225 240 240 240 240 240 240 240 240 240 + 152 109 92 101 169 147 174 240 240 240 240 240 240 240 240 235 + 207 122 128 142 197 238 240 240 240 240 240 240 240 240 240 240 + 191 108 109 5 72 228 240 240 240 240 240 240 240 240 240 237 + 236 240 237 150 29 223 240 240 240 240 240 240 240 240 240 240 + 227 129 124 150 237 226 237 240 240 240 240 240 240 240 240 238 + 152 51 155 45 120 237 238 240 240 240 240 240 240 240 240 240 + 77 119 232 217 50 180 240 240 240 240 240 240 240 240 240 240 + 175 161 161 186 53 226 240 240 240 240 240 240 240 240 240 240 + 209 155 64 83 207 240 240 240 240 240 240 240 240 239 228 232 + 140 148 117 144 150 150 225 229 232 236 234 236 240 227 229 235 + 186 71 89 77 74 117 156 240 240 240 240 240 240 240 240 235 + 240 240 219 51 187 240 240 240 240 240 240 240 240 240 240 240 + 240 240 219 74 213 231 240 240 240 240 240 240 240 240 240 233 + 98 76 65 61 51 72 186 240 240 240 240 240 240 240 240 240 + 220 210 184 184 184 206 216 240 240 240 240 240 240 240 240 234 + 150 109 121 106 103 115 225 240 240 240 240 240 240 240 240 240 + 156 109 92 101 168 162 186 240 240 240 240 240 240 240 240 235 + 207 121 128 142 197 240 240 240 240 240 240 240 240 240 240 240 + 192 99 100 5 78 232 240 240 240 240 240 240 240 240 240 236 + 236 240 236 150 29 223 240 240 240 240 240 240 240 240 240 240 + 230 137 125 152 237 237 240 240 240 240 240 240 240 240 240 238 + 152 51 156 52 120 240 240 240 240 240 240 240 240 240 240 240 + 77 120 240 217 50 180 240 240 240 240 240 240 240 240 240 240 + 176 166 170 186 53 226 240 240 240 240 240 240 240 240 240 240 + 212 164 73 85 216 240 240 240 240 240 240 240 240 240 240 240 + 149 155 127 147 156 150 225 228 232 235 232 234 240 227 229 234 + 189 86 98 83 84 116 156 240 240 240 240 240 240 240 240 235 + 240 240 219 51 186 238 240 240 240 240 240 240 240 240 240 240 + 240 240 219 73 211 228 240 240 240 240 240 240 240 240 240 233 + 98 76 58 50 47 66 182 240 240 240 240 240 240 240 240 240 + 220 201 179 184 184 205 212 240 240 240 240 240 240 240 240 234 + 141 85 109 106 109 115 225 240 240 240 240 240 240 240 240 240 + 154 108 101 110 170 151 180 240 240 240 240 240 240 240 240 235 + 206 121 128 148 200 238 240 240 240 240 240 240 240 240 240 240 + 178 95 107 17 92 236 240 240 240 240 240 240 240 240 240 236 + 235 232 237 151 30 225 240 240 240 240 240 240 240 240 240 240 + 230 137 125 150 240 240 240 240 240 240 240 240 240 240 240 238 + 152 51 155 45 120 240 240 240 240 240 240 240 240 240 240 240 + 77 118 240 217 50 180 240 240 240 240 240 240 240 240 240 240 + 175 153 163 186 53 226 240 240 240 240 240 240 240 240 240 240 + 209 155 72 85 216 240 240 240 240 240 240 240 240 240 239 233 + diff --git a/Data/pattHiro.pdf b/Data/pattHiro.pdf new file mode 100644 index 0000000000000000000000000000000000000000..dcaf4f54852c3627bb131ce6458868240057e494 GIT binary patch literal 1572 zcmah}e@qis99PH^5zZfMOr+pmocu}#ch_=luOM4TsUR&hZ5bkU^V+_`Nx8$`V?YT^ z@ppn|LD6M1EDl^mMmDBo%osIJw``c9TecZuWK0}mD$Z>Qf)4kt1FZ7LUhXdMzVG|} ze82DW{k#`%F&Ii;iG@l)0nxdM$jBf_6BJ5O8AxNb1Zm{G2>JkN^l}Iq zpi6W^A~TbakPmVd#KN3CUVEQlrz8Gpe7Nncji%cd_MN!%VPvFY-AeUy_N@cvVldUZ zfB4roqmLI~i+#C&^Wm=#+^efK)vn0Ao_KUz`>_2ItC=)6kA~JyHIJ6~s~_e5cCRRM zQXe+;eEUG}@V<{FsjW|k{1dHXOSBEggubMd6M1jv3`OQ2s(k(|lUd8un=UNtwLLqJ}Tr(8(+V3Cj5o6-Np3<Ikn}rTeNiuC5pjo=Bs;1Tm=M42I%3cyeJv#)CV_LLBUlE)D%JLg;FmBX$(Qy zDn(?h5?qKE1wdNGD#0C;SuSQq_5Wp_Ef|ODfrs--xQGJF%q+(a$myg(F!UT43s;Kc zESwJt2rz*ylU68+RX!J#fJ~W{AhSdP;dKefhcCd%G$C@plV!8zOC zEW8^8Fezr}=DAnJ8c;&vC8bh>U+spV0yT30vpB?g#ZtLOj67Z9!gXqIIuRz*>)Qc0;W_qglsDi^*blJqX0=;RPE@Dk#^UTjjOQcilULq##XMXbgK zGw0$3BuZt#p<>kdLou5$Mj(lZA+k(*YpxAsTWm&}lr_?rl;T z?-MN?LaDG)N)Fm;jSqSV3a|tXUvnMa{vZDJCcu`8Xc47Py6NydUCiV9kmM$PL7b$rU^zkb5J~4WJ}L zljdZdDy^Gip$Af0ih~}dhH^5rhjzMXH=FtY9s!+5i1VQUb+j^#CgS6>3yj3SErJp^ literal 0 HcmV?d00001 diff --git a/LICENCE.txt b/LICENCE.txt new file mode 100644 index 0000000..a3330c1 --- /dev/null +++ b/LICENCE.txt @@ -0,0 +1,35 @@ +ARToolkit Java class library NyARToolkit. +Copyright (C)2008 R.Iizuka + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation; either version 2 +of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program; if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + + +Java版 ARToolkit クラスライブラリ NyARToolkit +Copyright (C)2008 R.Iizuka + +このプログラムはフリーソフトウェアです。あなたはこれを、フリーソフトウェ +ア財団によって発行された GNU 一般公衆利用許諾契約書(バージョン2か、希 +望によってはそれ以降のバージョンのうちどれか)の定める条件の下で再頒布 +または改変することができます。 + +このプログラムは有用であることを願って頒布されますが、*全くの無保証* +です。商業可能性の保証や特定の目的への適合性は、言外に示されたものも含 +め全く存在しません。詳しくはGNU 一般公衆利用許諾契約書をご覧ください。 + +あなたはこのプログラムと共に、GNU 一般公衆利用許諾契約書の複製物を一部 +受け取ったはずです。もし受け取っていなければ、フリーソフトウェア財団ま +で請求してください(宛先は the Free Software Foundation, Inc., 59 +Temple Place, Suite 330, Boston, MA 02111-1307 USA)。 \ No newline at end of file diff --git a/bin/readme.txt b/bin/readme.txt new file mode 100644 index 0000000..089f20d --- /dev/null +++ b/bin/readme.txt @@ -0,0 +1,3 @@ +準備中です。ゴメンネ + +Not ready. \ No newline at end of file diff --git a/readme.ja.txt b/readme.ja.txt new file mode 100644 index 0000000..7ef27f4 --- /dev/null +++ b/readme.ja.txt @@ -0,0 +1,117 @@ +ARToolkit Java class library NyARToolkit. +Copyright (C)2008 R.Iizuka + +version Alpha 0.5.20080329.0 + +http://nyatla.jp/ +airmail(at)ebony.plala.or.jp +-------------------------------------------------- + + + + +・NyARToolkit + +NyARToolkitは、nativeなコードを一切使用しない、Pure Javaのみで +構成されたARToolkitクラスライブラリです。 + +ARToolkit 2.72.0をベースに構築されています。 + +J2SEでのみ動作を確認しました。 +J2MEやMIDP2.0にはそのうち対応します。 + + + + +・基本構成 + ++---------------------------- +| Application | ++-------+-------+-----------+ +|NyARJMF| NyJogl| | ++-------+-------+ | +| JMF | JOGL |NyARToolkit| ++-------+-------+ | +|Camera | 3D | | +----------------------------+ + +映像キャプチャにはJMFを使用し、3D描画にはJoglを使用しています。 +NyARJMFとNyJoglは、これらのエクステンションをApplicationやNyARToolKit +から使いやすくするためのラッパーです。 + +これらとNyARToolkitは完全に分離していますので、入力・出力ともに容易に +差し替えが出来ると思います。 + + + + +・サンプルなど + +動作させる前に、JMFとJOGLをインストールしてください。 +動作確認したバージョンと入手先はこちらです。 + +JMF JavaTM Media Framework 2.1.1e +http://java.sun.com/products/java-media/jmf/index.jsp + +jogl-1.1.1-pre-20080328-windows-i586.zip +https://jogl.dev.java.net/ + + +サンプルは以下のディレクトリにあります。 + +./src +NyARToolkitのEclipseプロジェクトがあります。 +jp.nyatla.nyartoolkit.sampleパッケージに、Rawイメージから +変換行列を求めるサンプルがあります。 + +./sample +NyARToolkitのアプリケーションサンプルEclipseプロジェクトがあります。 +NyARJMFにはビデオキャプチャの試験プログラムと、マーカー検出プログラムがあります。 +NyARJOGLにはARToolkitのsimpleLite相当のサンプルがあります。 + +NyARJMFのプロジェクトはNyARToolKitに依存し、NyARJOGLのプロジェクトはNyARToolKit +とNyARJMFに依存しています。 +zipを展開すると多分参照関係が壊れてますので、再設定してください。 + + + + +・NyARToolkitとオリジナルの差分 + +オリジナルと演算結果に互換性がありますが、関数構成を再設計した +ため、関数名や関数コールの手順の互換性がほとんどありません。 + +クラスは関数機能毎にまとめた作りになっていますので、オリジナルの +コード読んだことがあれば、なんとなく判ると思います。 + + + + +・足りない機能等 + +マーカーのセーブ機能と、複数マーカーの認識機能が未実装です。 + +今後実装していきます。 + + + + +・ライセンス +GPLです。詳しくはLICENCE.txtをみてください。 + + + + +・お願い + +NyARToolkitを使って面白いものが出来たら、是非教えてください。 + +それと強制では有りませんが、NyARToolkitを使った感想などを +送ってくれると、今後の励みになります。 + + + + +ではでは、楽しく遊んでくださいネ。 + +2008.03.29 R.Iizuka@nyatla.jp diff --git a/sample/jmf/.classpath b/sample/jmf/.classpath new file mode 100644 index 0000000..d77ac37 --- /dev/null +++ b/sample/jmf/.classpath @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/sample/jmf/.project b/sample/jmf/.project new file mode 100644 index 0000000..371348c --- /dev/null +++ b/sample/jmf/.project @@ -0,0 +1,17 @@ + + + NyARJMF + + + + + + org.eclipse.jdt.core.javabuilder + + + + + + org.eclipse.jdt.core.javanature + + diff --git a/sample/jmf/JmfCaptureTest.java b/sample/jmf/JmfCaptureTest.java new file mode 100644 index 0000000..8f3074f --- /dev/null +++ b/sample/jmf/JmfCaptureTest.java @@ -0,0 +1,56 @@ +/** + * VFMキャプチャテストプログラム + * (c)2008 R.iizuka + * airmail@ebony.plala.or.jp + * http://nyatla.jp/ + */ +import javax.media.*; + +import javax.media.util.BufferToImage; +import javax.media.format.*; + +import jp.nyatla.nyartoolkit.NyARException; +import jp.nyatla.nyartoolkit.jmf.*; +import java.awt.*; + + + +public class JmfCaptureTest extends Frame implements JmfCaptureListener{ + public JmfCaptureTest() throws NyARException + { + setTitle("JmfCaptureTest"); + setBounds(0,0,320+64,240+64); + capture=new JmfCameraCapture(320,240,30f,JmfCameraCapture.PIXCEL_FORMAT_RGB); + capture.setCaptureListener(this); + } + + + + private JmfCameraCapture capture; + public void onUpdateBuffer(Buffer i_buffer) + { + BufferToImage b2i=new BufferToImage((VideoFormat)i_buffer.getFormat()); + Image img=b2i.createImage(i_buffer); + Graphics g = getGraphics(); + g.drawImage(img, 32, 32,this); + } + private void startCapture() + { + try{ + capture.start(); + }catch(Exception e){ + e.printStackTrace(); + } + } + public static void main(String[] args) { + try{ + JmfCaptureTest mainwin = new JmfCaptureTest(); + mainwin.setVisible(true); + mainwin.startCapture(); + }catch(Exception e){ + e.printStackTrace(); + } + + } + +} diff --git a/sample/jmf/NyarToolkitLinkTest.java b/sample/jmf/NyarToolkitLinkTest.java new file mode 100644 index 0000000..ed667c5 --- /dev/null +++ b/sample/jmf/NyarToolkitLinkTest.java @@ -0,0 +1,108 @@ +/** + * VFM+ARToolkitテストプログラム + * カメラから取り込んだデータからマーカーを検出して、一致度と変換行列を表示します。 + * (c)2008 R.iizuka + * airmail(at)ebony.plala.or.jp + * http://nyatla.jp/ + */ +import javax.media.*; + +import javax.media.util.BufferToImage; +import javax.media.format.*; + +import jp.nyatla.nyartoolkit.NyARException; +import jp.nyatla.nyartoolkit.jmf.*; + +import java.awt.*; + +import jp.nyatla.nyartoolkit.core.*; +import jp.nyatla.nyartoolkit.detector.*; +import jp.nyatla.nyartoolkit.core.raster.*; + + + + + +public class NyarToolkitLinkTest extends Frame implements JmfCaptureListener +{ + private final String CARCODE_FILE ="../../Data/patt.hiro"; + private final String PARAM_FILE ="../../Data/camera_para.dat"; + private JmfCameraCapture capture; + NyARSingleDetectMarker nya; + + public NyarToolkitLinkTest() throws NyARException,NyARException + { + setTitle("JmfCaptureTest"); + setBounds(0,0,320+64,240+64); + //キャプチャの準備 + capture=new JmfCameraCapture(320,240,30f,JmfCameraCapture.PIXCEL_FORMAT_RGB); + capture.setCaptureListener(this); + + //NyARToolkitの準備 + NyARParam ar_param=new NyARParam(); + NyARCode ar_code =new NyARCode(16,16); + ar_param.loadFromARFile(PARAM_FILE); + ar_param.changeSize(320,240); + nya=new NyARSingleDetectMarker(ar_param,ar_code,80.0); + ar_code.LoadFromARFile(CARCODE_FILE); + } + + + + public void onUpdateBuffer(Buffer i_buffer) + { + try{ + //キャプチャしたイメージを加工 + BufferToImage b2i=new BufferToImage((VideoFormat)i_buffer.getFormat()); + Image img=b2i.createImage(i_buffer); + Graphics g = getGraphics(); + NyARRaster_RGB ra=NyARRaster_RGB.wrap((byte[])i_buffer.getData(), 320, 240); + //i_buffer. + boolean is_marker_exist=nya.detectMarkerLite(ra,100); + + double[][] atm=null; + if(is_marker_exist){ + //変換行列を取得 + atm=nya.getTransmationMatrix().getArray(); + } + //情報を画面に書く + g.drawImage(img, 32, 32,this); + if(is_marker_exist){ + g.drawString("マーカー検出:"+nya.getConfidence(),32,50); + for(int i=0;i<3;i++){ + for(int i2=0;i2<4;i2++){ + g.drawString("["+i+"]["+i2+"]"+atm[i][i2],32,50+(1+i2*3+i)*16); + } + + } + }else{ + g.drawString("マーカー未検出:",32,100); + } + }catch(Exception e){ + e.printStackTrace(); + } + + + + + } + private void startCapture() + { + try{ + capture.start(); + }catch(Exception e){ + e.printStackTrace(); + } + } + public static void main(String[] args) { + try{ + NyarToolkitLinkTest mainwin = new NyarToolkitLinkTest(); + mainwin.setVisible(true); + mainwin.startCapture(); + }catch(Exception e){ + e.printStackTrace(); + } + + } + +} diff --git a/sample/jmf/jp/nyatla/nyartoolkit/jmf/JmfCameraCapture.java b/sample/jmf/jp/nyatla/nyartoolkit/jmf/JmfCameraCapture.java new file mode 100644 index 0000000..e76f5af --- /dev/null +++ b/sample/jmf/jp/nyatla/nyartoolkit/jmf/JmfCameraCapture.java @@ -0,0 +1,175 @@ +/** + * JMFお手軽キャプチャクラス + * (c)2008 R.Iizuka + * airmail@ebony.plala.or.jp + * http://nyatla.jp/ + */ +package jp.nyatla.nyartoolkit.jmf; + + + +import javax.media.*; +import javax.media.protocol.*; +import javax.media.control.*; +import javax.media.format.*; +import java.awt.*; +import java.util.*; +import javax.media.protocol.DataSource; + +import jp.nyatla.nyartoolkit.NyARException; + + + + + +public class JmfCameraCapture +{ + private JmfCaptureListener capture_listener; + private DataSource jmf_data_source; + private MonitorStream jmf_monitor_stream; + private Processor jmf_processor; + private VideoFormat jmf_video_format; + + private Buffer read_buf=new Buffer(); + public static final String PIXCEL_FORMAT_RGB="RGB"; + public JmfCameraCapture(int i_width,int i_height,float i_rate,String i_pixcel_format) + { + String encoding = i_pixcel_format;//comboEncoding.getSelectedItem(); + Dimension size = new Dimension(i_width,i_height); + jmf_video_format = new VideoFormat(encoding, size, Format.NOT_SPECIFIED,null,i_rate); + } + public javax.media.Buffer readBuffer() throws NyARException + { + if(jmf_monitor_stream==null){ + throw new NyARException(); + } + try{ + jmf_monitor_stream.read(read_buf); + }catch(Exception e){ + throw new NyARException(e); + } + return read_buf; + } + public void setCaptureListener(JmfCaptureListener i_listener) throws NyARException + { + if(jmf_processor!=null){ + throw new NyARException(); + } + capture_listener=i_listener; + + } + public void start() throws NyARException + { + + DataSource ds=getCaptureDS(jmf_video_format); + VideoFormat[] formats=new VideoFormat[]{new VideoFormat(null)}; + ProcessorModel pm = new ProcessorModel(ds,formats,null);//, formats, ftd); + Processor processor; + try { + processor = Manager.createRealizedProcessor(pm); + } catch (Exception e){ + // Make sure the capture devices are released + ds.disconnect(); + throw new NyARException(e); + } + // Get the monitor control: + // Since there are more than one MonitorControl objects + // exported by the DataSource, we get the specific one + // that is also the MonitorStream object. + jmf_monitor_stream=(MonitorStream)ds.getControl("jmfsample.MonitorStream"); + jmf_monitor_stream.setCaptureListener(capture_listener); + jmf_data_source=ds; + jmf_processor=processor; + jmf_processor.start(); + } + public void stop() + { + jmf_processor.stop(); + jmf_processor.close(); + jmf_processor = null; + + } + protected void finalize() + { + if(jmf_processor!=null){ + jmf_processor.stop(); + jmf_processor.close(); + jmf_processor = null; + } + } + private static DataSource getCaptureDS(VideoFormat vf) { + DataSource dsVideo = null; + DataSource ds = null; + + // Create a capture DataSource for the video + // If there is no video capture device, then exit with null + if (vf != null) { + dsVideo = createDataSource(vf); + if (dsVideo == null) + return null; + } + + + // Create the monitoring datasource wrapper + if (dsVideo != null) { + dsVideo = new MonitorCDS(dsVideo); + return dsVideo; + } + + // Merge the data sources, if both audio and video are available + try { + ds = Manager.createMergingDataSource(new DataSource[]{dsVideo}); + } catch (IncompatibleSourceException ise){ + return null; + } + + return ds; + } + + private static DataSource createDataSource(Format format) { + DataSource ds; + Vector devices; + CaptureDeviceInfo cdi; + MediaLocator ml; + + // Find devices for format + devices = CaptureDeviceManager.getDeviceList(format); + if (devices.size() < 1) { + System.err.println("! No Devices for " + format); + return null; + } + // Pick the first device + cdi = (CaptureDeviceInfo) devices.elementAt(0); + + ml = cdi.getLocator(); + + try { + ds = Manager.createDataSource(ml); + ds.connect(); + if (ds instanceof CaptureDevice) + { + setCaptureFormat((CaptureDevice) ds, format); + } + } catch (Exception e) { + System.err.println(e); + return null; + } + return ds; + } + + private static void setCaptureFormat(CaptureDevice cdev, Format format) { + FormatControl [] fcs = cdev.getFormatControls(); + if (fcs.length < 1){ + return; + } + FormatControl fc = fcs[0]; + Format [] formats = fc.getSupportedFormats(); + for (int i = 0; i < formats.length; i++) { + if (formats[i].matches(format)){ + format = formats[i].intersects(format); + fc.setFormat(format); + break; + } + } + } +} \ No newline at end of file diff --git a/sample/jmf/jp/nyatla/nyartoolkit/jmf/JmfCaptureListener.java b/sample/jmf/jp/nyatla/nyartoolkit/jmf/JmfCaptureListener.java new file mode 100644 index 0000000..1998f6b --- /dev/null +++ b/sample/jmf/jp/nyatla/nyartoolkit/jmf/JmfCaptureListener.java @@ -0,0 +1,14 @@ +/** + * JMFお手軽キャプチャ用リスナ + * (c)2008 R.Iizuka + * airmail@ebony.plala.or.jp + * http://nyatla.jp/ + */ +package jp.nyatla.nyartoolkit.jmf; + +import javax.media.Buffer; + +public interface JmfCaptureListener{ + public void onUpdateBuffer(Buffer i_buffer); + +} \ No newline at end of file diff --git a/sample/jmf/jp/nyatla/nyartoolkit/jmf/MonitorCDS.java b/sample/jmf/jp/nyatla/nyartoolkit/jmf/MonitorCDS.java new file mode 100644 index 0000000..9e86106 --- /dev/null +++ b/sample/jmf/jp/nyatla/nyartoolkit/jmf/MonitorCDS.java @@ -0,0 +1,136 @@ +/* + * Copyright (c) 1996-2001 Sun Microsystems, Inc. All Rights Reserved. + * + * Sun grants you ("Licensee") a non-exclusive, royalty free, license to use, + * modify and redistribute this software in source and binary code form, + * provided that i) this copyright notice and license appear on all copies of + * the software; and ii) Licensee does not utilize the software in a manner + * which is disparaging to Sun. + * + * This software is provided "AS IS," without a warranty of any kind. ALL + * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY + * IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR + * NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN AND ITS LICENSORS SHALL NOT BE + * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING + * OR DISTRIBUTING THE SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR ITS + * LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, + * INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER + * CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF + * OR INABILITY TO USE SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + * + * This software is not designed or intended for use in on-line control of + * aircraft, air traffic, aircraft navigation or aircraft communications; or in + * the design, construction, operation or maintenance of any nuclear + * facility. Licensee represents and warrants that it will not use or + * redistribute the Software for such purposes. + */ +package jp.nyatla.nyartoolkit.jmf; + + +import javax.media.*; +import javax.media.protocol.*; +import javax.media.control.*; + +import java.io.IOException; + + + +public class MonitorCDS extends PushBufferDataSource{ + + private PushBufferDataSource delegate = null; + private PushBufferStream [] delStreams = null; + private MonitorStream monitorStream = null; + private PushBufferStream [] monitorStreams = null; + boolean delStarted = false; // variable used by MonitorStream also + private Control [] controls; + + public MonitorCDS(DataSource ds) + { + // Get the stream from the actual datasource + // and create a MonitorStream from it + // Export the MonitorControl interface of the MonitorStream + if (ds instanceof PushBufferDataSource) + { + delegate = (PushBufferDataSource) ds; + delStreams = delegate.getStreams(); + monitorStream = new MonitorStream(delStreams[0], this); + monitorStreams = new PushBufferStream[] {monitorStream}; + } + } + + public Object [] getControls() + { + return controls; + } + + public Object getControl(String value) { + if (value.equals("jmfsample.MonitorStream") || value.equals("javax.media.control.MonitorControl")) + return monitorStream; + else + return null; + } + + public javax.media.CaptureDeviceInfo getCaptureDeviceInfo() + { + return ((CaptureDevice)delegate).getCaptureDeviceInfo(); + } + + public FormatControl[] getFormatControls() + { + return ((CaptureDevice)delegate).getFormatControls(); + } + + public String getContentType() + { + return delegate.getContentType(); + } + + public void connect() throws IOException + { + if (delegate == null) + throw new IOException("Incompatible DataSource"); + // Delegate is already connected + } + + public void disconnect() + { + monitorStream.setEnabled(false); + delegate.disconnect(); + } + + public synchronized void start() throws IOException + { + startDelegate(); + delStarted = true; + } + + public synchronized void stop() throws IOException + { + if (!monitorStream.isEnabled()) { + stopDelegate(); + } + delStarted = false; + } + + public Time getDuration() + { + return delegate.getDuration(); + } + + public PushBufferStream [] getStreams() + { + return monitorStreams; + } + + void startDelegate() throws IOException + { + delegate.start(); + } + + void stopDelegate() throws IOException + { + delegate.stop(); + } + +} diff --git a/sample/jmf/jp/nyatla/nyartoolkit/jmf/MonitorStream.java b/sample/jmf/jp/nyatla/nyartoolkit/jmf/MonitorStream.java new file mode 100644 index 0000000..bfbd2b3 --- /dev/null +++ b/sample/jmf/jp/nyatla/nyartoolkit/jmf/MonitorStream.java @@ -0,0 +1,196 @@ +/* + * Copyright (c) 1996-2001 Sun Microsystems, Inc. All Rights Reserved. + * + * Sun grants you ("Licensee") a non-exclusive, royalty free, license to use, + * modify and redistribute this software in source and binary code form, + * provided that i) this copyright notice and license appear on all copies of + * the software; and ii) Licensee does not utilize the software in a manner + * which is disparaging to Sun. + * + * This software is provided "AS IS," without a warranty of any kind. ALL + * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY + * IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR + * NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN AND ITS LICENSORS SHALL NOT BE + * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING + * OR DISTRIBUTING THE SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR ITS + * LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, + * INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER + * CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF + * OR INABILITY TO USE SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGES. + * + * This software is not designed or intended for use in on-line control of + * aircraft, air traffic, aircraft navigation or aircraft communications; or in + * the design, construction, operation or maintenance of any nuclear + * facility. Licensee represents and warrants that it will not use or + * redistribute the Software for such purposes. + */ +package jp.nyatla.nyartoolkit.jmf; + + + +import javax.media.*; +import javax.media.protocol.*; + +import javax.media.util.BufferToImage; +import java.io.IOException; +import java.awt.*; + +public class MonitorStream implements PushBufferStream, BufferTransferHandler { + + JmfCaptureListener img_listener; + PushBufferStream actual = null; + boolean dataAvailable = false; + boolean terminate = false; + boolean enabled = false; + Object bufferLock = new Object(); + Buffer cbuffer = new Buffer(); + BufferTransferHandler transferHandler = null; + Component component = null; + MonitorCDS cds; + BufferToImage bti = null; + + MonitorStream(PushBufferStream actual, MonitorCDS cds) { + this.actual = actual; + actual.setTransferHandler(this); + this.cds = cds; + } + + public javax.media.Format getFormat() + { + return actual.getFormat(); + } + /** + * 非同期READ + */ + public void read(Buffer buffer) throws IOException + { + // Wait for data to be available + // Doesn't get used much because the transferData + // call is made when data IS available. And most + // Processors/Players read the data in the same + // thread that called transferData, although that's + // not a safe assumption to make + if (!dataAvailable) { + synchronized (bufferLock) { + while (!dataAvailable && !terminate) { + try { + bufferLock.wait(100); + } catch (InterruptedException ie) { + } + } + } + } + + if (dataAvailable) { + synchronized (bufferLock) { + // Copy the buffer attributes, but swap the data + // attributes so that no extra copy is made. + buffer.copy(cbuffer, true); + //dataAvailable = false; + } + } +// return; + } + public void setCaptureListener(JmfCaptureListener i_listener) + { + img_listener=i_listener; + } + + public void transferData(PushBufferStream pbs) + { + // Get the data from the original source stream + synchronized (bufferLock) { + try { + pbs.read(cbuffer); + } catch (IOException ioe) { + return; + } + dataAvailable = true; + bufferLock.notifyAll(); + } + if(img_listener!=null){ + img_listener.onUpdateBuffer(cbuffer); + } + +/* + // Display data if monitor is active + if (isEnabled()) { + if (bti == null) { + VideoFormat vf = (VideoFormat) cbuffer.getFormat(); + bti = new BufferToImage(vf); + } + if (bti != null && component != null) { + Image im = bti.createImage(cbuffer); + Graphics g = component.getGraphics(); + Dimension size = component.getSize(); + if (g != null) + g.drawImage(im, 0, 0, component); + } + } +*/ + // Maybe synchronize this with setTransferHandler() ? + if (transferHandler != null && cds.delStarted) + transferHandler.transferData(this); + } + + public void setTransferHandler(BufferTransferHandler transferHandler) { + this.transferHandler = transferHandler; + } + + public boolean setEnabled(boolean value) { + enabled = value; + if (value == false) { + if (!cds.delStarted) { + try { + cds.stopDelegate(); + } catch (IOException ioe) { + } + } + } else { + // Start the capture datasource if the monitor is enabled + try { + cds.startDelegate(); + }catch (IOException ioe) { + } + } + return enabled; + } + + public boolean isEnabled() + { + return enabled; + } + + + + public float setPreviewFrameRate(float rate) + { + System.err.println("TODO"); + return rate; + } + + public ContentDescriptor getContentDescriptor() + { + return actual.getContentDescriptor(); + } + + public long getContentLength() + { + return actual.getContentLength(); + } + + public boolean endOfStream() { + return actual.endOfStream(); + } + + public Object [] getControls() { + return new Object[0]; + } + + public Object getControl(String str) { + return null; + } + + +} diff --git a/sample/jogl/.classpath b/sample/jogl/.classpath new file mode 100644 index 0000000..1082ce7 --- /dev/null +++ b/sample/jogl/.classpath @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/sample/jogl/.project b/sample/jogl/.project new file mode 100644 index 0000000..c9e086a --- /dev/null +++ b/sample/jogl/.project @@ -0,0 +1,17 @@ + + + NyARJOGL + + + + + + org.eclipse.jdt.core.javabuilder + + + + + + org.eclipse.jdt.core.javanature + + diff --git a/sample/jogl/JavaSimpleLite.java b/sample/jogl/JavaSimpleLite.java new file mode 100644 index 0000000..5b54af6 --- /dev/null +++ b/sample/jogl/JavaSimpleLite.java @@ -0,0 +1,224 @@ +/** + * simpleLiteと同じようなテストプログラム + * マーカーの一致度の最低値をチェックするところを抜いたので、同じマーカーを大量に + * 検出すると面白いことになります。 + * (c)2008 R.iizuka + * airmail(at)ebony.plala.or.jp + * http://nyatla.jp/ + */ +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; +import java.awt.*; + +import javax.media.Buffer; + +import javax.media.opengl.GL; +import javax.media.opengl.GLAutoDrawable; +import javax.media.opengl.GLEventListener; +import javax.media.opengl.GLCanvas; + +import com.sun.opengl.util.Animator; + +import jp.nyatla.nyartoolkit.core.NyARCode; + +import jp.nyatla.nyartoolkit.jmf.JmfCameraCapture; +import jp.nyatla.nyartoolkit.jmf.JmfCaptureListener; +import jp.nyatla.nyartoolkit.gutil.*; + + +public class JavaSimpleLite implements GLEventListener,JmfCaptureListener +{ + private final String CARCODE_FILE ="../../Data/patt.hiro"; + private final String PARAM_FILE ="../../Data/camera_para.dat"; + + + private Animator animator; + private GLNyARRaster_RGB cap_image; + + private JmfCameraCapture capture; + private GL gl; + //NyARToolkit関係 + private GLNyARSingleDetectMarker nya; + private GLNyARParam ar_param; + /** + * 立方体を書く + * + */ + void drawCube() + { + // Colour cube data. + int polyList = 0; + float fSize = 0.5f; + int f, i; + float[][] cube_vertices=new float[][]{ + {1.0f, 1.0f, 1.0f}, {1.0f, -1.0f, 1.0f}, {-1.0f, -1.0f, 1.0f}, {-1.0f, 1.0f, 1.0f}, + {1.0f, 1.0f, -1.0f}, {1.0f, -1.0f, -1.0f}, {-1.0f, -1.0f, -1.0f}, {-1.0f, 1.0f, -1.0f} + }; + float[][] cube_vertex_colors=new float[][]{ + {1.0f, 1.0f, 1.0f}, {1.0f, 1.0f, 0.0f}, {0.0f, 1.0f, 0.0f}, {0.0f, 1.0f, 1.0f}, + {1.0f, 0.0f, 1.0f}, {1.0f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.0f}, {0.0f, 0.0f, 1.0f} + }; + int cube_num_faces = 6; + short[][] cube_faces =new short[][]{ + {3, 2, 1, 0}, {2, 3, 7, 6}, {0, 1, 5, 4}, {3, 0, 4, 7}, {1, 2, 6, 5}, {4, 5, 6, 7} + }; + + if (polyList==0) { + polyList = gl.glGenLists (1); + gl.glNewList(polyList, GL.GL_COMPILE); + gl.glBegin(GL.GL_QUADS); + for (f = 0; f < cube_num_faces; f++) + for (i = 0; i < 4; i++) { + gl.glColor3f (cube_vertex_colors[cube_faces[f][i]][0], cube_vertex_colors[cube_faces[f][i]][1], cube_vertex_colors[cube_faces[f][i]][2]); + gl.glVertex3f(cube_vertices[cube_faces[f][i]][0] * fSize, cube_vertices[cube_faces[f][i]][1] * fSize, cube_vertices[cube_faces[f][i]][2] * fSize); + } + gl.glEnd(); + gl.glColor3f(0.0f, 0.0f, 0.0f); + for (f = 0; f < cube_num_faces; f++) { + gl.glBegin (GL.GL_LINE_LOOP); + for (i = 0; i < 4; i++) + gl.glVertex3f(cube_vertices[cube_faces[f][i]][0] * fSize, cube_vertices[cube_faces[f][i]][1] * fSize, cube_vertices[cube_faces[f][i]][2] * fSize); + gl.glEnd (); + } + gl.glEndList (); + } + + gl.glPushMatrix(); // Save world coordinate system. + gl.glTranslatef(0.0f, 0.0f, 0.5f); // Place base of cube on marker surface. + gl.glRotatef(0.0f, 0.0f, 0.0f, 1.0f); // Rotate about z axis. + gl.glDisable(GL.GL_LIGHTING); // Just use colours. + gl.glCallList(polyList); // Draw the cube. + gl.glPopMatrix(); // Restore world coordinate system. + + } + + + + public JavaSimpleLite() + { + Frame frame = new Frame("Java simpleLite with NyARToolkit"); + + + // 3Dを描画するコンポーネント + GLCanvas canvas = new GLCanvas(); + frame.add(canvas); + canvas.addGLEventListener(this); + frame.addWindowListener(new WindowAdapter() { + public void windowClosing(WindowEvent e) { + System.exit(0); + } + }); + + frame.setVisible(true); + Insets ins=frame.getInsets(); + frame.setSize(320+ins.left+ins.right,240+ins.top+ins.bottom); + canvas.setBounds(ins.left,ins.top,320,240); + } + + public void init(GLAutoDrawable drawable) { + gl = drawable.getGL(); + gl.glClearColor(0.0f, 0.0f, 0.0f, 0.0f); + //NyARToolkitの準備 + try{ + //キャプチャの準備 + capture=new JmfCameraCapture(320,240,30f,JmfCameraCapture.PIXCEL_FORMAT_RGB); + capture.setCaptureListener(this); + capture.start(); + //NyARToolkitの準備 + ar_param=new GLNyARParam(); + NyARCode ar_code =new NyARCode(16,16); + ar_param.loadFromARFile(PARAM_FILE); + ar_param.changeSize(320,240); + nya=new GLNyARSingleDetectMarker(ar_param,ar_code,80.0); + ar_code.LoadFromARFile(CARCODE_FILE); + //GL対応のRGBラスタオブジェクト + cap_image=new GLNyARRaster_RGB(gl,ar_param,320,240); + + }catch(Exception e){ + e.printStackTrace(); + } + animator = new Animator(drawable); + + animator.start(); + + } + + public void reshape(GLAutoDrawable drawable, + int x, int y, + int width, int height) + { + float ratio = (float)height / (float)width; + gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT); + gl.glViewport(0, 0, width, height); + + //視体積の設定 + gl.glMatrixMode(GL.GL_PROJECTION); + gl.glLoadIdentity(); + gl.glFrustum(-1.0f, 1.0f, -ratio, ratio, + 5.0f, 40.0f); + //見る位置 + gl.glMatrixMode(GL.GL_MODELVIEW); + gl.glLoadIdentity(); + gl.glTranslatef(0.0f, 0.0f, -10.0f); + } + + public void display(GLAutoDrawable drawable) + { + + try{ + gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT); // Clear the buffers for new frame. + + + //キャプチャしたイメージを加工 + Buffer b=capture.readBuffer(); + //BufferToImage b2i=new BufferToImage((VideoFormat)b.getFormat()); + if(b.getData()==null){ + return; + }else{ + //画像準備OK + } + //画像チェックしてマーカー探して、背景を書く + boolean is_marker_exist; + synchronized(cap_image){ + is_marker_exist=nya.detectMarkerLite(cap_image,100); + //背景を書く + cap_image.glDispImage(1.0); + } + //あったら立方体を書く + if(is_marker_exist){ + // Projection transformation. + gl.glMatrixMode(GL.GL_PROJECTION); + gl.glLoadMatrixd(ar_param.getCameraFrustumRH(),0); + gl.glMatrixMode(GL.GL_MODELVIEW); + // Viewing transformation. + gl.glLoadIdentity(); + gl.glLoadMatrixd(nya.getCameraViewRH(),0); + + + // All other lighting and geometry goes here. + drawCube(); + } + }catch(Exception e){ + e.printStackTrace(); + } + } + public void onUpdateBuffer(Buffer i_buffer) + { + try{ + synchronized(cap_image){ + cap_image.setRawData((byte[])i_buffer.getData(), true); + } + }catch(Exception e){ + e.printStackTrace(); + } + } + + public void displayChanged(GLAutoDrawable drawable, + boolean modeChanged, + boolean deviceChanged) {} + + public static void main(String[] args) { + new JavaSimpleLite(); + } +} + diff --git a/sample/jogl/jp/nyatla/nyartoolkit/gutil/GLNyARParam.java b/sample/jogl/jp/nyatla/nyartoolkit/gutil/GLNyARParam.java new file mode 100644 index 0000000..5b6f51c --- /dev/null +++ b/sample/jogl/jp/nyatla/nyartoolkit/gutil/GLNyARParam.java @@ -0,0 +1,99 @@ +/** + * NyARParamにOpenGL向け関数を追加したもの + * (c)2008 R.iizuka + * airmail(at)ebony.plala.or.jp + * http://nyatla.jp/ + */ +package jp.nyatla.nyartoolkit.gutil; + +import jp.nyatla.nyartoolkit.core.*; +public class GLNyARParam extends NyARParam +{ + private double view_distance_min=0.1;//#define VIEW_DISTANCE_MIN 0.1 // Objects closer to the camera than this will not be displayed. + private double view_distance_max=100.0;//#define VIEW_DISTANCE_MAX 100.0 // Objects further away from the camera than this will not be displayed. + private double[] m_projection=null; + public void setViewDistanceMin(double i_new_value) + { + m_projection=null;//キャッシュ済変数初期化 + view_distance_min=i_new_value; + } + public void setViewDistanceMax(double i_new_value) + { + m_projection=null;//キャッシュ済変数初期化 + view_distance_max=i_new_value; + } + /** + * void arglCameraFrustumRH(const ARParam *cparam, const double focalmin, const double focalmax, GLdouble m_projection[16]) + * 関数の置き換え + * @param focalmin + * @param focalmax + * @return + */ + public double[] getCameraFrustumRH() + { + //既に値がキャッシュされていたらそれを使う + if(m_projection!=null){ + return m_projection; + } + //無ければ計算 + m_projection=new double[16]; + NyARMat trans_mat=new NyARMat(3,4); + NyARMat icpara_mat=new NyARMat(3,4); + double[][] p=new double[3][3], q=new double[4][4]; + int width, height; + int i, j; + + width = xsize; + height = ysize; + + decompMat(icpara_mat,trans_mat); + + double[][] icpara=icpara_mat.getArray(); + double[][] trans=trans_mat.getArray(); + for (i = 0; i < 4; i++) { + icpara[1][i] = (height - 1)*(icpara[2][i]) - icpara[1][i]; + } + + for(i = 0; i < 3; i++) { + for(j = 0; j < 3; j++) { + p[i][j] = icpara[i][j] / icpara[2][2]; + } + } + q[0][0] = (2.0 * p[0][0] / (width - 1)); + q[0][1] = (2.0 * p[0][1] / (width - 1)); + q[0][2] = -((2.0 * p[0][2] / (width - 1)) - 1.0); + q[0][3] = 0.0; + + q[1][0] = 0.0; + q[1][1] = -(2.0 * p[1][1] / (height - 1)); + q[1][2] = -((2.0 * p[1][2] / (height - 1)) - 1.0); + q[1][3] = 0.0; + + q[2][0] = 0.0; + q[2][1] = 0.0; + q[2][2] = (view_distance_max + view_distance_min)/(view_distance_min - view_distance_max); + q[2][3] = 2.0 * view_distance_max * view_distance_min / (view_distance_min - view_distance_max); + + q[3][0] = 0.0; + q[3][1] = 0.0; + q[3][2] = -1.0; + q[3][3] = 0.0; + + for (i = 0; i < 4; i++) { // Row. + // First 3 columns of the current row. + for (j = 0; j < 3; j++) { // Column. + m_projection[i + j*4] = + q[i][0] * trans[0][j] + + q[i][1] * trans[1][j] + + q[i][2] * trans[2][j]; + } + // Fourth column of the current row. + m_projection[i + 3*4]= + q[i][0] * trans[0][3] + + q[i][1] * trans[1][3] + + q[i][2] * trans[2][3] + + q[i][3]; + } + return m_projection; + } +} diff --git a/sample/jogl/jp/nyatla/nyartoolkit/gutil/GLNyARRaster_RGB.java b/sample/jogl/jp/nyatla/nyartoolkit/gutil/GLNyARRaster_RGB.java new file mode 100644 index 0000000..b956408 --- /dev/null +++ b/sample/jogl/jp/nyatla/nyartoolkit/gutil/GLNyARRaster_RGB.java @@ -0,0 +1,118 @@ +/** + * NyARRaster_RGBにOpenGL向け関数を追加したもの + * (c)2008 R.iizuka + * airmail(at)ebony.plala.or.jp + * http://nyatla.jp/ + */ +package jp.nyatla.nyartoolkit.gutil; + +import java.nio.ByteBuffer; +import java.nio.IntBuffer; + +import javax.media.opengl.GL; +import javax.media.opengl.glu.GLU; + +import jp.nyatla.nyartoolkit.core.NyARParam; +import jp.nyatla.nyartoolkit.core.raster.NyARRaster_RGB; + +public class GLNyARRaster_RGB extends NyARRaster_RGB +{ + private NyARParam cparam; + private GL ref_gl; + private GLU glu; + public GLNyARRaster_RGB(GL i_ref_gl,NyARParam i_cparam,int i_width,int i_height) + { + width=i_width; + height=i_height; + cparam=i_cparam; + ref_gl=i_ref_gl; + glu=new GLU(); + this.ref_buf=new byte[i_width*i_height*3]; + } + public void setRawData(byte[] i_buf,boolean i_is_reverse) + { + if(i_is_reverse){ + int length=width*3; + int src_idx=0; + int dest_idx=(height-1)*length; + for(int i=0;i + + + + + diff --git a/src/.project b/src/.project new file mode 100644 index 0000000..84e4f3f --- /dev/null +++ b/src/.project @@ -0,0 +1,17 @@ + + + NyARToolKit + + + + + + org.eclipse.jdt.core.javabuilder + + + + + + org.eclipse.jdt.core.javanature + + diff --git a/src/jp/nyatla/nyartoolkit/NyARException.java b/src/jp/nyatla/nyartoolkit/NyARException.java new file mode 100644 index 0000000..134ebbd --- /dev/null +++ b/src/jp/nyatla/nyartoolkit/NyARException.java @@ -0,0 +1,49 @@ +/* + * PROJECT: NyARToolkit + * -------------------------------------------------------------------------------- + * This work is based on the original ARToolKit developed by + * Hirokazu Kato + * Mark Billinghurst + * HITLab, University of Washington, Seattle + * http://www.hitl.washington.edu/artoolkit/ + * + * The NyARToolkit is Java version ARToolkit class library. + * Copyright (C)2008 R.Iizuka + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this framework; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * For further information please contact. + * http://nyatla.jp/nyatoolkit/ + * + * + */ +package jp.nyatla.nyartoolkit; + +public class NyARException extends Exception { + private static final long serialVersionUID = 1L; + public NyARException(){ + super(); + } + public NyARException(Exception e){ + super(e); + } + public NyARException(String m){ + super(m); + } + public static void trap(String m) throws NyARException + { + throw new NyARException("トラップ:"+m); + } +} diff --git a/src/jp/nyatla/nyartoolkit/base/Config.java b/src/jp/nyatla/nyartoolkit/base/Config.java new file mode 100644 index 0000000..add7b1b --- /dev/null +++ b/src/jp/nyatla/nyartoolkit/base/Config.java @@ -0,0 +1,75 @@ +package jp.nyatla.nyartoolkit.base; + +public class Config { + //Version系情報 +// public static final int AR_HEADER_VERSION_MAJOR=2; //#define AR_HEADER_VERSION_MAJOR 2 +// public static final int AR_HEADER_VERSION_MINOR=72;//#define AR_HEADER_VERSION_MINOR 72 +// +// public static final int AR_HEADER_VERSION_TINY=0;//#define AR_HEADER_VERSION_TINY 0 +// +// public static final int AR_HEADER_VERSION_BUILD=0;//#define AR_HEADER_VERSION_BUILD 0 +// +// public static final String AR_HEADER_VERSION_STRING="2.72.0";//#define AR_HEADER_VERSION_STRING "2.72.0" +// +// public static final boolean AR_HAVE_HEADER_VERSION_2=true;//#define AR_HAVE_HEADER_VERSION_2 +// public static final boolean AR_HAVE_HEADER_VERSION_2_72=true;//#define AR_HAVE_HEADER_VERSION_2_72 + + + /*ビデオ入力系?*/ + /*------------------------------------------------------------*/ + +// public static final int AR_DRAW_BY_GL_DRAW_PIXELS =0;//#define AR_DRAW_BY_GL_DRAW_PIXELS 0 +// public static final int AR_DRAW_BY_TEXTURE_MAPPING=1;//#define AR_DRAW_BY_TEXTURE_MAPPING 1 +// public static final int AR_DRAW_TEXTURE_FULL_IMAGE=0;//#define AR_DRAW_TEXTURE_FULL_IMAGE 0 +// public static final int AR_DRAW_TEXTURE_HALF_IMAGE=1;//#define AR_DRAW_TEXTURE_HALF_IMAGE 1 +// public static final int AR_IMAGE_PROC_IN_FULL=0;//#define AR_IMAGE_PROC_IN_FULL 0 +// public static final int AR_IMAGE_PROC_IN_HALF=1;//#define AR_IMAGE_PROC_IN_HALF 1 +// public static final int AR_FITTING_TO_IDEAL=0;//#define AR_FITTING_TO_IDEAL 0 +// public static final int AR_FITTING_TO_INPUT=1;//#define AR_FITTING_TO_INPUT 1 + +// public static final int AR_TEMPLATE_MATCHING_COLOR=0;//#define AR_TEMPLATE_MATCHING_COLOR 0 +// public static final int AR_TEMPLATE_MATCHING_BW=1;//#define AR_TEMPLATE_MATCHING_BW 1 +// public static final int AR_MATCHING_WITHOUT_PCA=0;//#define AR_MATCHING_WITHOUT_PCA 0 +// public static final int AR_MATCHING_WITH_PCA=1;//#define AR_MATCHING_WITH_PCA 1 + +// public static final int DEFAULT_TEMPLATE_MATCHING_MODE=AR_TEMPLATE_MATCHING_BW;//#define DEFAULT_TEMPLATE_MATCHING_MODE AR_TEMPLATE_MATCHING_COLOR +// public static final int DEFAULT_MATCHING_PCA_MODE=AR_MATCHING_WITH_PCA;//#define DEFAULT_MATCHING_PCA_MODE AR_MATCHING_WITHOUT_PCA + + + //#ifdef _WIN32 +// public static final int DEFAULT_IMAGE_PROC_MODE =AR_IMAGE_PROC_IN_FULL;//# define DEFAULT_IMAGE_PROC_MODE AR_IMAGE_PROC_IN_FULL +// public static final int DEFAULT_FITTING_MODE =AR_FITTING_TO_INPUT;//# define DEFAULT_FITTING_MODE AR_FITTING_TO_INPUT +// public static final int DEFAULT_DRAW_MODE =AR_DRAW_BY_TEXTURE_MAPPING;//# define DEFAULT_DRAW_MODE AR_DRAW_BY_TEXTURE_MAPPING +// public static final int DEFAULT_DRAW_TEXTURE_IMAGE =AR_DRAW_TEXTURE_FULL_IMAGE;//# define DEFAULT_DRAW_TEXTURE_IMAGE AR_DRAW_TEXTURE_FULL_IMAGE + //#endif + +// public static final int AR_PIX_SIZE_DEFAULT= +// (AR_DEFAULT_PIXEL_FORMAT == AR_PIXEL_FORMAT_ABGR) || (AR_DEFAULT_PIXEL_FORMAT == AR_PIXEL_FORMAT_BGRA) || (AR_DEFAULT_PIXEL_FORMAT == AR_PIXEL_FORMAT_RGBA) || (AR_DEFAULT_PIXEL_FORMAT == AR_PIXEL_FORMAT_ARGB)?4: +// ((AR_DEFAULT_PIXEL_FORMAT == AR_PIXEL_FORMAT_BGR) || (AR_DEFAULT_PIXEL_FORMAT == AR_PIXEL_FORMAT_RGB)?3: +// ((AR_DEFAULT_PIXEL_FORMAT == AR_PIXEL_FORMAT_2vuy) || (AR_DEFAULT_PIXEL_FORMAT == AR_PIXEL_FORMAT_yuvs)?2: +// ((AR_DEFAULT_PIXEL_FORMAT == AR_PIXEL_FORMAT_MONO)?1:-1))); +// public static final int AR_GET_TRANS_MAT_MAX_LOOP_COUNT=5;//#define AR_GET_TRANS_MAT_MAX_LOOP_COUNT 5 +// public static final double AR_GET_TRANS_MAT_MAX_FIT_ERROR=1.0;//#define AR_GET_TRANS_MAT_MAX_FIT_ERROR 1.0 +// public static final double AR_GET_TRANS_CONT_MAT_MAX_FIT_ERROR=1.0;//#define AR_GET_TRANS_CONT_MAT_MAX_FIT_ERROR 1.0 + +// public static final int AR_AREA_MAX=100000;//#define AR_AREA_MAX 100000 +// public static final int AR_AREA_MIN=70;//#define AR_AREA_MIN 70 + + +// public static final int AR_SQUARE_MAX=30;//#define AR_SQUARE_MAX 30 +// public static final int AR_CHAIN_MAX=10000;//#define AR_CHAIN_MAX 10000 +// public static final int AR_PATT_NUM_MAX=50;//#define AR_PATT_NUM_MAX 50 +// public static final int AR_PATT_SIZE_X=16;//#define AR_PATT_SIZE_X 16 +// public static final int AR_PATT_SIZE_Y=16;//#define AR_PATT_SIZE_Y 16 +// public static final int AR_PATT_SAMPLE_NUM=64;//#define AR_PATT_SAMPLE_NUM 64 + +// public static final double AR_GL_CLIP_NEAR=50.0;//#define AR_GL_CLIP_NEAR 50.0 +// public static final double AR_GL_CLIP_FAR=5000.0;//#define AR_GL_CLIP_FAR 5000.0 + +// public static final int AR_HMD_XSIZE=640;//#define AR_HMD_XSIZE 640 +// public static final int AR_HMD_YSIZE=480;//#define AR_HMD_YSIZE 480 + +// public static final int AR_PARAM_NMIN=6;//#define AR_PARAM_NMIN 6 +// public static final int AR_PARAM_NMAX=1000;//#define AR_PARAM_NMAX 1000 +// public static final double AR_PARAM_C34=100.0;//#define AR_PARAM_C34 100.0 +} diff --git a/src/jp/nyatla/nyartoolkit/base/Param.java b/src/jp/nyatla/nyartoolkit/base/Param.java new file mode 100644 index 0000000..5079131 --- /dev/null +++ b/src/jp/nyatla/nyartoolkit/base/Param.java @@ -0,0 +1,366 @@ +package jp.nyatla.nyartoolkit.base; + +import jp.nyatla.nyartoolkit.NyARException; +import jp.nyatla.nyartoolkit.core.NyARMat; + + + + +public class Param { + private static final int AR_PARAM_NMIN=6;//#define AR_PARAM_NMIN 6 + private static final int AR_PARAM_NMAX=1000;//#define AR_PARAM_NMAX 1000 + private static final double AR_PARAM_C34=100.0;//#define AR_PARAM_C34 100.0 + + /* + typedef struct { + int xsize, ysize; + double matL[3][4]; + double matR[3][4]; + double matL2R[3][4]; + double dist_factorL[4]; + double dist_factorR[4]; + } ARSParam;*/ +/* static class ARSParam{ + int xsize, ysize; + Double2dArray matL=new Double2dArray(3,4); + Double2dArray matR=new Double2dArray(3,4); + Double2dArray matL2R=new Double2dArray(3,4); + double[] dist_factorL=new double[4]; + double[] dist_factorR=new double[4]; + }*/ + private static final int arParamGet_AR_PARAM_CDMIN = 12; + /*int arParamGet( double global[][3], double screen[][2], int data_num,double mat[3][4] );*/ + public static int arParamGet( double global[][], double[][] screen, int num,double[][] mat) throws NyARException + { + NyARMat mat_a, mat_at, mat_r;//ARMat *mat_a, *mat_at, *mat_r, mat_cpara; + NyARMat mat_wm1,mat_wm2;//ARMat *mat_wm1, *mat_wm2; + int i; + + if(num < AR_PARAM_NMIN){ + return( -1 ); + } + if(num > AR_PARAM_NMAX){ + return( -1 ); + } + NyARException.trap("未チェックのパス");{ + mat_a = new NyARMat(2*num,arParamGet_AR_PARAM_CDMIN-1 );//mat_a = arMatrixAlloc( 2*num, AR_PARAM_CDMIN-1 ); + mat_at =new NyARMat(arParamGet_AR_PARAM_CDMIN-1, 2*num );//mat_at = arMatrixAlloc( AR_PARAM_CDMIN-1, 2*num ); + mat_r = new NyARMat(2*num,1);//mat_r = arMatrixAlloc( 2*num, 1 ); + mat_wm1 =new NyARMat(arParamGet_AR_PARAM_CDMIN-1, arParamGet_AR_PARAM_CDMIN-1 );//mat_wm1 = arMatrixAlloc( AR_PARAM_CDMIN-1, AR_PARAM_CDMIN-1 ); + mat_wm2 =new NyARMat(arParamGet_AR_PARAM_CDMIN-1, 2*num );//mat_wm2 = arMatrixAlloc( AR_PARAM_CDMIN-1, 2*num ); + } +/* + mat_a = Matrix.arMatrixAlloc( 2*num, arParamGet_AR_PARAM_CDMIN-1 );//mat_a = arMatrixAlloc( 2*num, AR_PARAM_CDMIN-1 ); + if( mat_a == null){ + return -1; + } + mat_at =Matrix.arMatrixAlloc(arParamGet_AR_PARAM_CDMIN-1, 2*num );//mat_at = arMatrixAlloc( AR_PARAM_CDMIN-1, 2*num ); + if(mat_at == null){ + return -1; + } + mat_r = Matrix.arMatrixAlloc( 2*num, 1 );//mat_r = arMatrixAlloc( 2*num, 1 ); + if(mat_r ==null){ + return -1; + } + mat_wm1 = Matrix.arMatrixAlloc(arParamGet_AR_PARAM_CDMIN-1, arParamGet_AR_PARAM_CDMIN-1 );//mat_wm1 = arMatrixAlloc( AR_PARAM_CDMIN-1, AR_PARAM_CDMIN-1 ); + if( mat_wm1 == null) { + return -1; + } + mat_wm2 = Matrix.arMatrixAlloc(arParamGet_AR_PARAM_CDMIN-1, 2*num );//mat_wm2 = arMatrixAlloc( AR_PARAM_CDMIN-1, 2*num ); + if( mat_wm2 == null ) { + return -1; + } +*/ + /* Initializing array */ + mat_a.zeroClear();//Javaではゼロクリアされるので不要 +// pa1=mat_a.getPointer();//pa1 = mat_a->m; +// for(i = 0; i < 2 * num * (arParamGet_AR_PARAM_CDMIN-1); i++){ +// {//*pa1++ = 0.0; +// pa1.set(0.0); +// pa1.incPtr();} +// } + double[][] pa1 =mat_a.getArray(); + double[][] pa2 =mat_a.getArray(); + /* Calculate A,R matrix */ + double[][] pr=mat_r.getArray(); + int pr_ptr_col=0; + for(i = 0; i < num; i++) {//for(i = 0, pr = mat_r->m; i < num; i++) { + int pa1_ptr_row =2*i; //pa1 = &(mat_a->m[ (2*i) * (AR_PARAM_CDMIN-1) + int pa2_ptr_row =2*i+1;//pa2 = &(mat_a->m[ (2*i+1) * (AR_PARAM_CDMIN-1) + 4]); + + +// pa1=mat_a.getPointer((2*i)*(arParamGet_AR_PARAM_CDMIN-1)); ]); +// pa2=mat_a.getPointer((2*i+1)*(arParamGet_AR_PARAM_CDMIN-1) + 4); + //*pa1++ = global[i][0]; *pa1++ = global[i][1]; + pa1[pa1_ptr_row][ 0]=global[i][0]; + pa1[pa1_ptr_row][ 1]=global[i][1]; + //*pa1++ = global[i][2]; *pa1++ = 1.0; + pa1[pa1_ptr_row][ 2]=global[i][2]; + pa1[pa1_ptr_row][ 3]=1.0; + //*pa2++ = global[i][0]; *pa2++ = global[i][1]; + pa2[pa2_ptr_row][ 4]=global[i][0]; + pa2[pa2_ptr_row][ 5]=global[i][1]; + //*pa2++ = global[i][2]; *pa2++ = 1.0; + pa2[pa2_ptr_row][ 6]=global[i][2]; + pa2[pa2_ptr_row][ 7]=1.0; + //pa1 += 4; + //*pa1++ = -global[i][0] * screen[i][0]; + pa1[pa1_ptr_row][ 8]=-global[i][0] * screen[i][0]; + //*pa1++ = -global[i][1] * screen[i][0]; + pa1[pa1_ptr_row][ 9]=-global[i][1] * screen[i][0]; + //*pa1 = -global[i][2] * screen[i][0]; + pa1[pa1_ptr_row][10]=-global[i][2]* screen[i][0]; + //*pa2++ = -global[i][0] * screen[i][1]; + pa2[pa2_ptr_row][ 8]=-global[i][0] * screen[i][1]; + //*pa2++ = -global[i][1] * screen[i][1]; + pa2[pa2_ptr_row][ 9]=-global[i][1] * screen[i][1]; + //*pa2 = -global[i][2] * screen[i][1]; + pa2[pa2_ptr_row][10]=-global[i][2] * screen[i][1]; + //*pr++ = screen[i][0] * AR_PARAM_C34; + pr[0][pr_ptr_col]=screen[i][0] * AR_PARAM_C34;pr_ptr_col++; + //*pr++ = screen[i][1] * AR_PARAM_C34; + pr[0][pr_ptr_col]=screen[i][1] * AR_PARAM_C34;pr_ptr_col++; + } + + NyARException.trap("未チェックのパス"); + NyARMat.matrixTrans(mat_at, mat_a );//if( arMatrixTrans( mat_at, mat_a ) < 0 ){ + + NyARException.trap("未チェックのパス"); + NyARMat.matrixMul( mat_wm1, mat_at, mat_a );//if( arMatrixMul( mat_wm1, mat_at, mat_a ) < 0 ) { + NyARException.trap("未チェックのパス"); + NyARMat.matrixSelfInv(mat_wm1 );//if( arMatrixSelfInv( mat_wm1 ) < 0 ) { + + NyARException.trap("未チェックのパス"); + NyARMat.matrixMul( mat_wm2, mat_wm1, mat_at );//if( arMatrixMul( mat_wm2, mat_wm1, mat_at ) < 0 ) { + + //mat_cpara.row = AR_PARAM_CDMIN-1;//mat_cpara.row = AR_PARAM_CDMIN-1; + //mat_cpara.clm = 1; + //mat_cpara.m = &(mat[0][0]); + /*1次元行列から3x4行列に転写。高負荷なところじゃないから地道に転写でOK*/ + NyARMat mat_cpara=new NyARMat(arParamGet_AR_PARAM_CDMIN-1,1); + double[][] mat_cpara_array=mat_cpara.getArray(); + NyARException.trap("未チェックのパス"); + NyARMat.matrixMul(mat_cpara, mat_wm2, mat_r );//if( arMatrixMul( &mat_cpara, mat_wm2, mat_r ) < 0 ) { + + for(int i2=0;i<3;i++){ + for(int i3=0;i3<4;i3++){ + mat[i2][i3]=mat_cpara_array[i2*4+i3][0]; + } + } + //ARMat.wrap(mat.array(),arParamGet_AR_PARAM_CDMIN-1,1);} + + mat[2][3]=AR_PARAM_C34;//mat[2][3] = AR_PARAM_C34; + + return 0; + } + +/* //int arsParamChangeSize( ARSParam *source, int xsize, int ysize, ARSParam *newparam ); + public static int arsParamChangeSize( ARSParam source, int xsize, int ysize, ARSParam newparam) + { + double scale; + + newparam.xsize=xsize;//newparam->xsize = xsize; + newparam.ysize=ysize;//newparam->ysize = ysize; + + scale=(double)xsize/ (double)(source.xsize);//scale = (double)xsize / (double)(source->xsize); + for(int i = 0; i < 4; i++ ){ + newparam.matL.set(0,i,source.matL.get(0,i)*scale);//newparam->matL[0][i] = source->matL[0][i] * scale; + newparam.matL.set(1,i,source.matL.get(1,i)*scale);//newparam->matL[1][i] = source->matL[1][i] * scale; + newparam.matL.set(2,i,source.matL.get(2,i));//newparam->matL[2][i] = source->matL[2][i]; + } + for(int i = 0; i < 4; i++ ) { + newparam.matR.set(0,i,source.matR.get(0,i)*scale);//newparam->matR[0][i] = source->matR[0][i] * scale; + newparam.matR.set(1,i,source.matR.get(1,i)*scale);//newparam->matR[1][i] = source->matR[1][i] * scale; + newparam.matR.set(2,i,source.matR.get(2,i));//newparam->matR[2][i] = source->matR[2][i]; + } + for(int i = 0; i < 4; i++ ) { + newparam.matL2R.set(0,i,source.matL2R.get(0,i));//newparam->matL2R[0][i] = source->matL2R[0][i]; + newparam.matL2R.set(1,i,source.matL2R.get(1,i));//newparam->matL2R[1][i] = source->matL2R[1][i]; + newparam.matL2R.set(2,i,source.matL2R.get(2,i));//newparam->matL2R[2][i] = source->matL2R[2][i]; + } + + newparam.dist_factorL[0] = source.dist_factorL[0] * scale;//newparam->dist_factorL[0] = source->dist_factorL[0] * scale; + newparam.dist_factorL[1] = source.dist_factorL[1] * scale;//newparam->dist_factorL[1] = source->dist_factorL[1] * scale; + newparam.dist_factorL[2] = source.dist_factorL[2] / (scale*scale);//newparam->dist_factorL[2] = source->dist_factorL[2] / (scale*scale); + newparam.dist_factorL[3] = source.dist_factorL[3];//newparam->dist_factorL[3] = source->dist_factorL[3]; + + newparam.dist_factorR[0] = source.dist_factorR[0] * scale;//newparam->dist_factorR[0] = source->dist_factorR[0] * scale; + newparam.dist_factorR[1] = source.dist_factorR[1] * scale;//newparam->dist_factorR[1] = source->dist_factorR[1] * scale; + newparam.dist_factorR[2] = source.dist_factorR[2] / (scale*scale);//newparam->dist_factorR[2] = source->dist_factorR[2] / (scale*scale); + newparam.dist_factorR[3] = source.dist_factorR[3];//newparam->dist_factorR[3] = source->dist_factorR[3]; + + return 0; + }*/ +/* //int arsParamSave( char *filename, ARSParam *sparam ); + public static int arsParamSave(String filename, ARSParam sparam) throws Exception + { + //int xsize, ysize; + //Double2dArray matL=new Double2dArray(3,4); + //Double2dArray matR=new Double2dArray(3,4); + //Double2dArray matL2R=new Double2dArray(3,4); + //double dist_factorL[]=new double[4]; + //double dist_factorR[]=new double[4]; + + byte[] buf=new byte[(4+4+(3*4*8)*3+(4*8)*2)]; + + //バッファをラップ + ByteBuffer bb = ByteBuffer.wrap(buf); + bb.order(ByteOrder.BIG_ENDIAN); + + //書き込み + bb.putInt(sparam.xsize); + bb.putInt(sparam.ysize); + for(int i=0;i<3;i++){ + for(int i2=0;i2<4;i2++){ + bb.putDouble(sparam.matL.get(i, i2)); + } + } + for(int i=0;i<3;i++){ + for(int i2=0;i2<4;i2++){ + bb.putDouble(sparam.matR.get(i, i2)); + } + } + for(int i=0;i<3;i++){ + for(int i2=0;i2<4;i2++){ + bb.putDouble(sparam.matL2R.get(i, i2)); + } + } + for(int i=0;i<4;i++){ + bb.putDouble(sparam.dist_factorL[i]); + } + for(int i=0;i<4;i++){ + bb.putDouble(sparam.dist_factorR[i]); + } + //ファイルに保存 + FileOutputStream fs=new FileOutputStream(filename); + fs.write(buf); + fs.close(); + + return 0; + }*/ +/* //int arsParamLoad( char *filename, ARSParam *sparam ); + public static int arsParamLoad(String filename, ARSParam sparam ) throws Exception + { + //ファイルを読んどく + FileInputStream fs=new FileInputStream(filename); + File f=new File(filename); + int file_size=(int)f.length(); + byte[] buf=new byte[file_size]; + fs.read(buf); + fs.close(); + + //バッファを加工 + ByteBuffer bb = ByteBuffer.wrap(buf); + bb.order(ByteOrder.BIG_ENDIAN); + + //固定回数パースして配列に格納 + sparam.xsize=bb.getInt(); + sparam.ysize=bb.getInt(); + for(int i=0;i<3;i++){ + for(int i2=0;i2<4;i2++){ + sparam.matL.set(i,i2,bb.getDouble()); + } + } + for(int i=0;i<3;i++){ + for(int i2=0;i2<4;i2++){ + sparam.matR.set(i,i2,bb.getDouble()); + } + } + for(int i=0;i<3;i++){ + for(int i2=0;i2<4;i2++){ + sparam.matL2R.set(i,i2,bb.getDouble()); + } + } + for(int i=0;i<3;i++){ + sparam.dist_factorL[i]=bb.getDouble(); + } + for(int i=0;i<3;i++){ + sparam.dist_factorR[i]=bb.getDouble(); + } + return 0; + }*/ +/* + // int arsParamGetMat( double matL[3][4], double matR[3][4],double cparaL[3][4], double cparaR[3][4], double matL2R[3][4] ); + public static int arsParamGetMat(double[][] matL, double[][] matR,double[][] cparaL, double[][] cparaR, double[][] matL2R) throws JartkException + { + ARMat t1,t2,t3;//ARMat *t1, *t2, *t3; + //double transL[3][4], transR[3][4]; + Double2dArray transL=new Double2dArray(3,4); + Double2dArray transR=new Double2dArray(3,4); + + arParamDecompMat( matL,cparaL,transL); + arParamDecompMat( matR,cparaR,transR); + JartkException.trap("未チェックパス");{ + t1=new ARMat(4,4);//t1 = arMatrixAlloc( 4, 4 ); + t2=new ARMat(4,4);//t2 = arMatrixAlloc( 4, 4 ); + } + double[][] t1_array=t1.getArray(); + double[][] t2_array=t2.getArray(); + for(int j = 0; j < 3; j++ ){ + for(int i = 0; i < 4; i++ ) { + JartkException.trap("未チェックパス");{ + t1_array[j][i]=transL.get(j,i);//t1->m[j*4+i] = transL[j][i]; + t2_array[j][i]=transL.get(j,i);//t2->m[j*4+i] = transR[j][i]; + } + } + } + JartkException.trap("未チェックパス");{ + t1_array[3][0]=t1_array[3][1]=t1_array[3][2]=0.0;//t1->m[12] = t1->m[13] = t1->m[14] = 0.0; + t1_array[3][3]=1.0;//t1->m[15] = 1.0; + t2_array[3][0]=t2_array[3][1]=t2_array[3][2]=0.0;//t2->m[12] = t2->m[13] = t2->m[14] = 0.0; + t2_array[3][3]=1.0;//t2->m[15] = 1.0; + } + JartkException.trap("未チェックのパス"); + ARMat.matrixSelfInv(t1);//if( arMatrixSelfInv(t1) != 0 ) { + + JartkException.trap("未チェックのパス"); + t3 =ARMat.matrixAllocMul(t2, t1);//t3 = arMatrixAllocMul(t2, t1); + double[][] t3_array=t3.getArray(); + if(t3==null){ + return -1; + } + + for(int j = 0; j < 3; j++ ) { + for(int i = 0; i < 4; i++ ) { + JartkException.trap("未チェックパス"); + matL2R[j][i]=t3_array[j][i];//matL2R[j][i] = t3->m[j*4+i]; + } + } + return 0; + } +*/ //int arsParamDisp( ARSParam *sparam ) +/* public static int arsParamDisp( ARSParam sparam) + { + System.out.println("--------------------------------------");//printf("--------------------------------------\n"); + System.out.println("SIZE = "+sparam.xsize+", "+sparam.ysize);// printf("SIZE = %d, %d\n", sparam->xsize, sparam->ysize); + System.out.println("-- Left --");//printf("-- Left --\n"); + System.out.println("Distotion factor = "+sparam.dist_factorL[0]+" "+sparam.dist_factorL[1]+" "+sparam.dist_factorL[2]+" "+sparam.dist_factorL[3]);//printf("Distotion factor = %f %f %f %f\n", sparam->dist_factorL[0],sparam->dist_factorL[1], sparam->dist_factorL[2], sparam->dist_factorL[3] ); + for(int j = 0; j < 3; j++ ) { + for(int i = 0; i < 4; i++ ){ + System.out.print(sparam.matL.get(j,i)+" ");//printf("%7.5f ", sparam->matL[j][i]); + } + System.out.println();//printf("\n"); + } + + System.out.println("-- Right --");//printf("-- Right --\n"); + System.out.println("Distotion factor = "+sparam.dist_factorR[0]+" "+sparam.dist_factorR[1]+" "+sparam.dist_factorR[2]+" "+sparam.dist_factorR[3]);//printf("Distotion factor = %f %f %f %f\n", sparam->dist_factorR[0],sparam->dist_factorR[1], sparam->dist_factorR[2], sparam->dist_factorR[3]); + for(int j = 0; j < 3; j++ ){ + for(int i = 0; i < 4; i++ ){ + System.out.println(sparam.matR.get(j,i)+" ");//printf("%7.5f ", sparam->matR[j][i]); + } + System.out.println();//printf("\n"); + } + + System.out.println("-- Left => Right --");//printf("-- Left => Right --\n"); + for(int j = 0; j < 3; j++ ) { + for(int i = 0; i < 4; i++ ){ + //printf("%7.5f ", sparam->matL2R[j][i]); + } + System.out.println();//printf("\n"); + } + + System.out.println("--------------------------------------");//printf("--------------------------------------\n"); + + return 0; + }*/ +} diff --git a/src/jp/nyatla/nyartoolkit/core/NyARCode.java b/src/jp/nyatla/nyartoolkit/core/NyARCode.java new file mode 100644 index 0000000..acc2596 --- /dev/null +++ b/src/jp/nyatla/nyartoolkit/core/NyARCode.java @@ -0,0 +1,165 @@ +/* + * PROJECT: NyARToolkit + * -------------------------------------------------------------------------------- + * This work is based on the original ARToolKit developed by + * Hirokazu Kato + * Mark Billinghurst + * HITLab, University of Washington, Seattle + * http://www.hitl.washington.edu/artoolkit/ + * + * The NyARToolkit is Java version ARToolkit class library. + * Copyright (C)2008 R.Iizuka + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this framework; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * For further information please contact. + * http://nyatla.jp/nyatoolkit/ + * + * + */ + +package jp.nyatla.nyartoolkit.core; + +import java.io.FileInputStream; +import java.io.InputStreamReader; +import java.io.StreamTokenizer; + +import jp.nyatla.nyartoolkit.NyARException; + + + + + +/** + * ARToolKitのマーカーコードを1個保持します。 + * + */ +public class NyARCode{ + private int[][][][] pat;//static int pat[AR_PATT_NUM_MAX][4][AR_PATT_SIZE_Y*AR_PATT_SIZE_X*3]; + private double[] patpow=new double[4];//static double patpow[AR_PATT_NUM_MAX][4]; + private short[][][] patBW;//static int patBW[AR_PATT_NUM_MAX][4][AR_PATT_SIZE_Y*AR_PATT_SIZE_X*3]; + private double[] patpowBW=new double[4];//static double patpowBW[AR_PATT_NUM_MAX][4]; + private int width,height; + public int[][][][] getPat() + { + return pat; + } + public double [] getPatPow() + { + return patpow; + } + public short[][][] getPatBW() + { + return patBW; + } + public double[] getPatPowBW() + { + return patpowBW; + } + public int getWidth() + { + return width; + } + public int getHeight() + { + return height; + } + + public NyARCode(int i_width,int i_height) + { + width=i_width; + height=i_height; + pat=new int[4][height][width][3];//static int pat[AR_PATT_NUM_MAX][4][AR_PATT_SIZE_Y*AR_PATT_SIZE_X*3]; + patBW=new short[4][height][width];//static int patBW[AR_PATT_NUM_MAX][4][AR_PATT_SIZE_Y*AR_PATT_SIZE_X*3]; + } + + /** + * int arLoadPatt( const char *filename ); + * ARToolKitのパターンファイルをロードする。 + * @param filename + * @return + * @throws Exception + */ + public void LoadFromARFile(String filename) throws NyARException + { + try{ + StreamTokenizer st=new StreamTokenizer(new InputStreamReader(new FileInputStream(filename))); + //パターンデータはGBRAで並んでる。 + for(int h=0; h<4; h++ ) { + int l = 0; + for(int i3 = 0; i3 < 3; i3++ ) { + for(int i2 = 0; i2 < height; i2++ ) { + for(int i1 = 0; i1 < width; i1++ ){ + //数値のみ読み出す + switch(st.nextToken()){//if( fscanf(fp, "%d", &j) != 1 ) { + case StreamTokenizer.TT_NUMBER: + break; + default: + throw new NyARException(); + } + short j=(short)(255-st.nval);//j = 255-j; + //標準ファイルのパターンはBGRでならんでるからRGBに並べなおす + switch(i3){ + case 0:pat[h][i2][i1][2] = j;break;//pat[patno][h][(i2*Config.AR_PATT_SIZE_X+i1)*3+2] = j;break; + case 1:pat[h][i2][i1][1] = j;break;//pat[patno][h][(i2*Config.AR_PATT_SIZE_X+i1)*3+1] = j;break; + case 2:pat[h][i2][i1][0] = j;break;//pat[patno][h][(i2*Config.AR_PATT_SIZE_X+i1)*3+0] = j;break; + } + //pat[patno][h][(i2*Config.AR_PATT_SIZE_X+i1)*3+i3] = j; + if( i3 == 0 ){ + patBW[h][i2][i1] = j;//patBW[patno][h][i2*Config.AR_PATT_SIZE_X+i1] = j; + }else{ + patBW[h][i2][i1] += j;//patBW[patno][h][i2*Config.AR_PATT_SIZE_X+i1] += j; + } + if( i3 == 2 ){ + patBW[h][i2][i1] /= 3;//patBW[patno][h][i2*Config.AR_PATT_SIZE_X+i1] /= 3; + } + l += j; + } + } + } + + l /= (height*width*3); + + int m = 0; + for(int i = 0; i < height; i++ ) {//for( i = 0; i < AR_PATT_SIZE_Y*AR_PATT_SIZE_X*3; i++ ) { + for(int i2 = 0; i2 < width; i2++ ) { + for(int i3 = 0; i3 < 3; i3++ ) { + pat[h][i][i2][i3] -= l; + m += (pat[h][i][i2][i3]*pat[h][i][i2][i3]); + } + } + } + patpow[h] = Math.sqrt((double)m); + if( patpow[h] == 0.0 ){ + patpow[h] = 0.0000001; + } + + m = 0; + for(int i = 0; i < height; i++ ) { + for(int i2 = 0; i2 < width; i2++ ) { + patBW[h][i][i2] -= l; + m += (patBW[h][i][i2]*patBW[h][i][i2]); + } + } + patpowBW[h] = Math.sqrt((double)m); + if(patpowBW[h] == 0.0 ){ + patpowBW[h] = 0.0000001; + } + } + }catch(Exception e){ + throw new NyARException(e); + } + } +} diff --git a/src/jp/nyatla/nyartoolkit/core/NyARColorPatt.java b/src/jp/nyatla/nyartoolkit/core/NyARColorPatt.java new file mode 100644 index 0000000..bfb749d --- /dev/null +++ b/src/jp/nyatla/nyartoolkit/core/NyARColorPatt.java @@ -0,0 +1,227 @@ +/* + * PROJECT: NyARToolkit + * -------------------------------------------------------------------------------- + * This work is based on the original ARToolKit developed by + * Hirokazu Kato + * Mark Billinghurst + * HITLab, University of Washington, Seattle + * http://www.hitl.washington.edu/artoolkit/ + * + * The NyARToolkit is Java version ARToolkit class library. + * Copyright (C)2008 R.Iizuka + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this framework; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * For further information please contact. + * http://nyatla.jp/nyatoolkit/ + * + * + */ +package jp.nyatla.nyartoolkit.core; + +import jp.nyatla.nyartoolkit.NyARException; +import jp.nyatla.nyartoolkit.core.raster.*; + +/** + * 24ビットカラーのマーカーを保持するために使うクラスです。 + * このクラスは、ARToolkitのパターンと、ラスタから取得したパターンを保持します。 + * + */ +public class NyARColorPatt +{ + public static final int AR_PATT_SAMPLE_NUM=64;//#define AR_PATT_SAMPLE_NUM 64 + private short extpat[][][]; + private int width; + private int height; + public NyARColorPatt(int i_width,int i_height) + { + width=i_width; + height=i_height; + extpat=new short[i_height][i_width][3]; + } + public short[][][] getPatArray() + { + return extpat; + } + public int getWidth() + { + return width; + } + public int getHeight() + { + return height; + } + private static void get_cpara( double world[][], double vertex[][],double para[][] ) throws NyARException + { + NyARMat a = new NyARMat( 8, 8 ); + double[][] a_array=a.getArray(); + NyARMat b = new NyARMat( 8, 1 ); + double[][] b_array=b.getArray(); + NyARMat c = new NyARMat( 8, 1 ); + double[][] c_array=c.getArray(); + + for(int i = 0; i < 4; i++ ) { + a_array[i*2][0]=world[i][0];//a->m[i*16+0] = world[i][0]; + a_array[i*2][1]=world[i][1];//a->m[i*16+1] = world[i][1]; + a_array[i*2][2]=1.0;//a->m[i*16+2] = 1.0; + a_array[i*2][3]=0.0;//a->m[i*16+3] = 0.0; + a_array[i*2][4]=0.0;//a->m[i*16+4] = 0.0; + a_array[i*2][5]=0.0;//a->m[i*16+5] = 0.0; + a_array[i*2][6]=-world[i][0] * vertex[i][0];//a->m[i*16+6] = -world[i][0] * vertex[i][0]; + a_array[i*2][7]=-world[i][1] * vertex[i][0];//a->m[i*16+7] = -world[i][1] * vertex[i][0]; + a_array[i*2+1][0]=0.0;//a->m[i*16+8] = 0.0; + a_array[i*2+1][1]=0.0;//a->m[i*16+9] = 0.0; + a_array[i*2+1][2]=0.0;//a->m[i*16+10] = 0.0; + a_array[i*2+1][3]=world[i][0];//a->m[i*16+11] = world[i][0]; + a_array[i*2+1][4]=world[i][1];//a->m[i*16+12] = world[i][1]; + a_array[i*2+1][5]=1.0;//a->m[i*16+13] = 1.0; + a_array[i*2+1][6]=-world[i][0] * vertex[i][1];//a->m[i*16+14] = -world[i][0] * vertex[i][1]; + a_array[i*2+1][7]=-world[i][1] * vertex[i][1];//a->m[i*16+15] = -world[i][1] * vertex[i][1]; + b_array[i*2+0][0]=vertex[i][0];//b->m[i*2+0] = vertex[i][0]; + b_array[i*2+1][0]=vertex[i][1];//b->m[i*2+1] = vertex[i][1]; + } +// JartkException.trap("未チェックのパス"); + NyARMat.matrixSelfInv(a); + +// JartkException.trap("未チェックのパス"); + NyARMat.matrixMul( c, a, b ); + for(int i = 0; i < 2; i++ ) { + para[i][0] = c_array[i*3+0][0];//para[i][0] = c->m[i*3+0]; + para[i][1] = c_array[i*3+1][0];//para[i][1] = c->m[i*3+1]; + para[i][2] = c_array[i*3+2][0];//para[i][2] = c->m[i*3+2]; + } + para[2][0] = c_array[2*3+0][0];//para[2][0] = c->m[2*3+0]; + para[2][1] = c_array[2*3+1][0];//para[2][1] = c->m[2*3+1]; + para[2][2] = 1.0;//para[2][2] = 1.0; + } + /** + * imageから、i_markerの位置にあるパターンを切り出して、保持します。 + * @param image + * @param i_marker + * @throws Exception + */ + public void pickFromRaster(NyARRaster image, NyARMarker i_marker) throws NyARException + { + int[] x_coord=i_marker.x_coord; + int[] y_coord=i_marker.y_coord; + int[] vertex=i_marker.vertex; + int[][][] ext_pat2=new int[height][width][3];//ARUint32 ext_pat2[AR_PATT_SIZE_Y][AR_PATT_SIZE_X][3]; + double[][] world=new double[4][2];//double world[4][2]; + double[][] local=new double[4][2];//double local[4][2]; + double[][] para=new double[3][3]; //double para[3][3]; + double d, xw, yw; + int xc, yc; + int xdiv, ydiv; + int xdiv2, ydiv2; + int lx1, lx2, ly1, ly2; + int img_x,img_y; + + img_x=image.getWidth(); + img_y=image.getHeight(); + + double xdiv2_reciprocal; // [tp] + double ydiv2_reciprocal; // [tp] + int ext_pat2_x_index; + int ext_pat2_y_index; + + world[0][0] = 100.0; + world[0][1] = 100.0; + world[1][0] = 100.0 + 10.0; + world[1][1] = 100.0; + world[2][0] = 100.0 + 10.0; + world[2][1] = 100.0 + 10.0; + world[3][0] = 100.0; + world[3][1] = 100.0 + 10.0; + for(int i = 0; i < 4; i++ ) { + local[i][0] = x_coord[vertex[i]]; + local[i][1] = y_coord[vertex[i]]; + } + get_cpara( world, local, para ); + + lx1 = (int)((local[0][0] - local[1][0])*(local[0][0] - local[1][0])+ (local[0][1] - local[1][1])*(local[0][1] - local[1][1])); + lx2 = (int)((local[2][0] - local[3][0])*(local[2][0] - local[3][0])+ (local[2][1] - local[3][1])*(local[2][1] - local[3][1])); + ly1 = (int)((local[1][0] - local[2][0])*(local[1][0] - local[2][0])+ (local[1][1] - local[2][1])*(local[1][1] - local[2][1])); + ly2 = (int)((local[3][0] - local[0][0])*(local[3][0] - local[0][0])+ (local[3][1] - local[0][1])*(local[3][1] - local[0][1])); + if( lx2 > lx1 ){ + lx1 = lx2; + } + if( ly2 > ly1 ){ + ly1 = ly2; + } + xdiv2 =width; + ydiv2 =height; + + while( xdiv2*xdiv2 < lx1/4 ){ + xdiv2*=2; + } + while( ydiv2*ydiv2 < ly1/4 ){ + ydiv2*=2; + } + + if( xdiv2 > AR_PATT_SAMPLE_NUM) + { + xdiv2 =AR_PATT_SAMPLE_NUM; + } + if( ydiv2 >AR_PATT_SAMPLE_NUM) + { + ydiv2 = AR_PATT_SAMPLE_NUM; + } + + xdiv = xdiv2/width;//xdiv = xdiv2/Config.AR_PATT_SIZE_X; + ydiv = ydiv2/height;//ydiv = ydiv2/Config.AR_PATT_SIZE_Y; +/* +printf("%3d(%f), %3d(%f)\n", xdiv2, sqrt(lx1), ydiv2, sqrt(ly1)); +*/ + + xdiv2_reciprocal = 1.0 / xdiv2; + ydiv2_reciprocal = 1.0 / ydiv2; + int[] rgb_tmp=new int[3]; + // arGetCode_put_zero(ext_pat2);//put_zero( (ARUint8 *)ext_pat2, AR_PATT_SIZE_Y*AR_PATT_SIZE_X*3*sizeof(ARUint32) ); + for(int j = 0; j < ydiv2; j++ ) { + yw = 102.5 + 5.0 * (j+0.5) * ydiv2_reciprocal; + for(int i = 0; i < xdiv2; i++ ) { + xw = 102.5 + 5.0 * (i+0.5) * xdiv2_reciprocal; + d = para[2][0]*xw + para[2][1]*yw + para[2][2]; + if( d == 0 ){ + throw new NyARException(); + } + xc = (int)((para[0][0]*xw + para[0][1]*yw + para[0][2])/d); + yc = (int)((para[1][0]*xw + para[1][1]*yw + para[1][2])/d); + + + if( xc >= 0 && xc < img_x && yc >= 0 && yc < img_y ) { + ext_pat2_y_index = j/ydiv; + ext_pat2_x_index = i/xdiv; +// image_index = (yc*arUtil_c.arImXsize+xc)*Config.AR_PIX_SIZE_DEFAULT; + image.pickRgbArray(xc, yc, rgb_tmp); + + ext_pat2[ext_pat2_y_index][ext_pat2_x_index][0] += rgb_tmp[0];//R + ext_pat2[ext_pat2_y_index][ext_pat2_x_index][1] += rgb_tmp[1];//G + ext_pat2[ext_pat2_y_index][ext_pat2_x_index][2] += rgb_tmp[2];//B +// System.out.println(xc+":"+yc+":"+rgb_tmp[0]+":"+rgb_tmp[1]+":"+rgb_tmp[2]); + } + } + } +// short[][][] ext_pat=new short[Config.AR_PATT_SIZE_Y][Config.AR_PATT_SIZE_X][3];//ARUint32 ext_pat2[AR_PATT_SIZE_Y][AR_PATT_SIZE_X][3]; + + for(int j = 0; j < height; j++ ) { + for(int i = 0; i < width; i++ ) { // PRL 2006-06-08. + extpat[j][i][0]=(short)(ext_pat2[j][i][0] / (xdiv*ydiv));//ext_pat[j][i][0] = (byte)(ext_pat2[j][i][0] / (xdiv*ydiv)); + extpat[j][i][1]=(short)(ext_pat2[j][i][1] / (xdiv*ydiv));//ext_pat[j][i][1] = (byte)(ext_pat2[j][i][1] / (xdiv*ydiv)); + extpat[j][i][2]=(short)(ext_pat2[j][i][2] / (xdiv*ydiv));//ext_pat[j][i][2] = (byte)(ext_pat2[j][i][2] / (xdiv*ydiv)); + } + } + } +} \ No newline at end of file diff --git a/src/jp/nyatla/nyartoolkit/core/NyARDetectMarker.java b/src/jp/nyatla/nyartoolkit/core/NyARDetectMarker.java new file mode 100644 index 0000000..2a73545 --- /dev/null +++ b/src/jp/nyatla/nyartoolkit/core/NyARDetectMarker.java @@ -0,0 +1,403 @@ +/* + * PROJECT: NyARToolkit + * -------------------------------------------------------------------------------- + * This work is based on the original ARToolKit developed by + * Hirokazu Kato + * Mark Billinghurst + * HITLab, University of Washington, Seattle + * http://www.hitl.washington.edu/artoolkit/ + * + * The NyARToolkit is Java version ARToolkit class library. + * Copyright (C)2008 R.Iizuka + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this framework; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * For further information please contact. + * http://nyatla.jp/nyatoolkit/ + * + * + */ +package jp.nyatla.nyartoolkit.core; + + + + +import jp.nyatla.nyartoolkit.NyARException; +import jp.nyatla.util.IntValue; +/** + * イメージからマーカー情報を検出するクラス。 + * このクラスは、arDetectMarker2.cとの置き換えになります。 + * ラベリング済みのラスタデータからマーカー位置を検出して、結果を保持します。 + * + */ +public class NyARDetectMarker { + private static final int AR_AREA_MAX=100000;//#define AR_AREA_MAX 100000 + private static final int AR_AREA_MIN=70;//#define AR_AREA_MIN 70 + + private int area_max=AR_AREA_MAX; + private int area_min=AR_AREA_MIN; + private NyARMarker[] marker_info2_array; + private int marker_num; + private int width,height; + /** + * 最大i_squre_max個のマーカーを検出するクラスを作成する。 + * @param i_width + * @param i_height + * @param i_squre_max + */ + public NyARDetectMarker(int i_width,int i_height,int i_squre_max) + { + width =i_width; + height=i_height; + marker_info2_array=new NyARMarker[i_squre_max]; + } + public int getMarkerNum() + { + return marker_num; + } + public NyARMarker[] getMarkerArray() throws NyARException + { + return marker_info2_array; + } + /** + * static int get_vertex( int x_coord[], int y_coord[], int st, int ed,double thresh, int vertex[], int *vnum) + * 関数の代替関数 + * @param x_coord + * @param y_coord + * @param st + * @param ed + * @param thresh + * @param vertex + * @param vnum + * @return + */ + private static boolean get_vertex( int[] x_coord, int[] y_coord, int st, int ed,double thresh, int vertex[],IntValue vnum) + { + double d, dmax; + double a, b, c; + int i, v1=0; + + a = y_coord[ed] - y_coord[st]; + b = x_coord[st] - x_coord[ed]; + c = x_coord[ed]*y_coord[st] - y_coord[ed]*x_coord[st]; + dmax = 0; + for(i=st+1;i dmax ) { + dmax = d*d; + v1 = i; + } + } + if( dmax/(a*a+b*b) > thresh ) { + if(!get_vertex(x_coord, y_coord, st, v1, thresh, vertex, vnum)){ + return false; + } + if( vnum.get() > 5 ){ + return false; + } + vertex[vnum.get()] = v1;//vertex[(*vnum)] = v1; + vnum.inc();//(*vnum)++; + + if(!get_vertex(x_coord, y_coord, v1, ed, thresh, vertex, vnum)){ + return false; + } + } + return true; + } + /** + * static int arDetectMarker2_check_square( int area, ARMarkerInfo2 *marker_info2, double factor ) + * 関数の代替関数 + * @param area + * @param i_marker_info2 + * @param factor + * @return + */ + private static boolean check_square( int area, NyARMarker i_marker_info2, double factor ) + { + int sx, sy; + int dmax, d, v1; + int[] vertex=new int[10];//int vertex[10] + int[] wv1=new int[10],wv2=new int[10];//int wv1[10],wv2[10]; + int v2;// int wvnum1,wvnum2,v2; + double thresh; + int i; + IntValue wvnum1=new IntValue(),wvnum2=new IntValue(); + + + dmax = 0; + v1 = 0; + sx = i_marker_info2.x_coord[0];//sx = marker_info2->x_coord[0]; + sy = i_marker_info2.y_coord[0];//sy = marker_info2->y_coord[0]; + for(i=1;icoord_num-1;i++) { + d = (i_marker_info2.x_coord[i]-sx)*(i_marker_info2.x_coord[i]-sx)+ (i_marker_info2.y_coord[i]-sy)*(i_marker_info2.y_coord[i]-sy); + if( d > dmax ) { + dmax = d; + v1 = i; + } + } + + thresh = (area/0.75) * 0.01 * factor; +// vnum = 1; + vertex[0] = 0; + wvnum1.set(0);// wvnum1 = 0; + wvnum2.set(0);// wvnum2 = 0; + + if(!get_vertex(i_marker_info2.x_coord, i_marker_info2.y_coord, 0, v1,thresh, wv1, wvnum1)){ //if( get_vertex(marker_info2->x_coord, marker_info2->y_coord, 0, v1,thresh, wv1, &wvnum1) < 0 ) { + return false; + } + if(!get_vertex(i_marker_info2.x_coord, i_marker_info2.y_coord,v1, i_marker_info2.coord_num-1, thresh, wv2, wvnum2)) {//if(get_vertex(marker_info2->x_coord, marker_info2->y_coord,v1, marker_info2->coord_num-1, thresh, wv2, &wvnum2) < 0 ) { + return false; + } + + if( wvnum1.get() == 1 && wvnum2.get() == 1 ) {//if( wvnum1 == 1 && wvnum2 == 1 ) { + vertex[1] = wv1[0]; + vertex[2] = v1; + vertex[3] = wv2[0]; + }else if( wvnum1.get() > 1 && wvnum2.get() == 0 ) {//}else if( wvnum1 > 1 && wvnum2 == 0 ) { + v2 = v1 / 2; + wvnum1.set(0);wvnum2.set(0);//wvnum1 = wvnum2 = 0; + if(!get_vertex(i_marker_info2.x_coord, i_marker_info2.y_coord,0, v2, thresh, wv1, wvnum1)) { + return false; + } + if(!get_vertex(i_marker_info2.x_coord, i_marker_info2.y_coord,v2, v1, thresh, wv2, wvnum2)) { + return false; + } + if( wvnum1.get() == 1 && wvnum2.get() == 1 ) { + vertex[1] = wv1[0]; + vertex[2] = wv2[0]; + vertex[3] = v1; + }else{ + return false; + } + }else if( wvnum1.get() == 0 && wvnum2.get() > 1 ) { + v2 = (v1 + i_marker_info2.coord_num-1) / 2; + + wvnum1.set(0);wvnum2.set(0);//wvnum1 = wvnum2 = 0; + if(!get_vertex(i_marker_info2.x_coord, i_marker_info2.y_coord,v1, v2, thresh, wv1,wvnum1)) { + return false; + } + if(!get_vertex(i_marker_info2.x_coord, i_marker_info2.y_coord,v2, i_marker_info2.coord_num-1, thresh, wv2, wvnum2)) { + return false; + } + if( wvnum1.get() == 1 && wvnum2.get() == 1 ) { + vertex[1] = v1; + vertex[2] = wv1[0]; + vertex[3] = wv2[0]; + } + else { + return false; + } + } + else { + return false; + } + + i_marker_info2.vertex[0] = vertex[0]; + i_marker_info2.vertex[1] = vertex[1]; + i_marker_info2.vertex[2] = vertex[2]; + i_marker_info2.vertex[3] = vertex[3]; + i_marker_info2.vertex[4] = i_marker_info2.coord_num-1; + + return true; + } + /** + * int arGetContour( ARInt16 *limage, int *label_ref,int label, int clip[4], ARMarkerInfo2 *marker_info2 ) + * 関数の代替品 + * @param limage + * @param label_ref + * @param label + * @param clip + * @return + * 検出したマーカーからマーカーオブジェクトを生成して返す。 + * @throws NyARException + */ + private NyARMarker arGetContour(short[][] limage, int[] label_ref,int label, int[] clip) throws NyARException + { + final int[] xdir={0, 1, 1, 1, 0,-1,-1,-1}; //static int xdir[8] = { 0, 1, 1, 1, 0,-1,-1,-1}; + final int[] ydir={-1,-1, 0, 1, 1, 1, 0,-1};//static int ydir[8] = {-1,-1, 0, 1, 1, 1, 0,-1}; + //ShortPointer p1;//ARInt16 *p1; + int sx=0, sy=0, dir; + int dmax, d, v1=0; + int i, j; + +// JartkException.trap("未チェックのパス"); + j = clip[2]; + //p1=ShortPointer.wrap(limage,j*xsize+clip.get());//p1 = &(limage[j*xsize+clip[0]]); + for( i = clip[0]; i <= clip[1]; i++){//for( i = clip[0]; i <= clip[1]; i++, p1++ ) { + if(limage[j][i] > 0 && label_ref[(limage[j][i])-1] == label ) {//if( *p1 > 0 && label_ref[(*p1)-1] == label ) { + sx = i; sy = j; + break; + } + } + if(i> clip[1]){//if( i > clip[1] ) { + System.out.println("??? 1");//printf(); + throw new NyARException();//return(-1); + } + + NyARMarker marker_info2=new NyARMarker(); + + marker_info2.coord_num=1;//marker_info2->coord_num = 1; + marker_info2.x_coord[0]=sx;//marker_info2->x_coord[0] = sx; + marker_info2.y_coord[0]=sy;//marker_info2->y_coord[0] = sy; + dir = 5; + + for(;;){ + int r=marker_info2.y_coord[marker_info2.coord_num-1]; + int c=marker_info2.x_coord[marker_info2.coord_num-1]; + //p1 = &(limage[marker_info2->y_coord[marker_info2->coord_num-1] * xsize+ marker_info2->x_coord[marker_info2->coord_num-1]]); + dir = (dir+5)%8; + for(i=0;i<8;i++) { + if(limage[r+ydir[dir]][c+xdir[dir]]>0){//if( p1[ydir[dir]*xsize+xdir[dir]] > 0 ){ + break; + } + dir = (dir+1)%8; + } + if( i == 8 ){ + System.out.println("??? 2");//printf("??? 2\n"); + throw new NyARException();//return(-1); + } + marker_info2.x_coord[marker_info2.coord_num]= marker_info2.x_coord[marker_info2.coord_num-1] + xdir[dir];//marker_info2->x_coord[marker_info2->coord_num]= marker_info2->x_coord[marker_info2->coord_num-1] + xdir[dir]; + marker_info2.y_coord[marker_info2.coord_num]= marker_info2.y_coord[marker_info2.coord_num-1] + ydir[dir];//marker_info2->y_coord[marker_info2->coord_num]= marker_info2->y_coord[marker_info2->coord_num-1] + ydir[dir]; + if( marker_info2.x_coord[marker_info2.coord_num] == sx && marker_info2.y_coord[marker_info2.coord_num] == sy ){ + break; + } + marker_info2.coord_num++; + if( marker_info2.coord_num == marker_info2.x_coord.length-1){//if( marker_info2.coord_num == Config.AR_CHAIN_MAX-1 ){ + System.out.println("??? 3");//printf("??? 3\n"); + throw new NyARException();//return(-1); + } + } + + dmax = 0; + for(i=1;icoord_num;i++) { + d = (marker_info2.x_coord[i]-sx)*(marker_info2.x_coord[i]-sx)+ (marker_info2.y_coord[i]-sy)*(marker_info2.y_coord[i]-sy);// d = (marker_info2->x_coord[i]-sx)*(marker_info2->x_coord[i]-sx)+ (marker_info2->y_coord[i]-sy)*(marker_info2->y_coord[i]-sy); + if( d > dmax ) { + dmax = d; + v1 = i; + } + } + + int[] wx=new int[v1];//new int[Config.AR_CHAIN_MAX]; + int[] wy=new int[v1]; //new int[Config.AR_CHAIN_MAX]; + for(i=0;ix_coord[i]; + wy[i] = marker_info2.y_coord[i];//wy[i] = marker_info2->y_coord[i]; + } + for(i=v1;icoord_num;i++) { + marker_info2.x_coord[i-v1] = marker_info2.x_coord[i];//marker_info2->x_coord[i-v1] = marker_info2->x_coord[i]; + marker_info2.y_coord[i-v1] = marker_info2.y_coord[i];//marker_info2->y_coord[i-v1] = marker_info2->y_coord[i]; + } + for(i=0;ix_coord[i-v1+marker_info2->coord_num] = wx[i]; + marker_info2.y_coord[i-v1+marker_info2.coord_num] = wy[i];//marker_info2->y_coord[i-v1+marker_info2->coord_num] = wy[i]; + } + marker_info2.x_coord[marker_info2.coord_num] = marker_info2.x_coord[0];//marker_info2->x_coord[marker_info2->coord_num] = marker_info2->x_coord[0]; + marker_info2.y_coord[marker_info2.coord_num] = marker_info2.y_coord[0];//marker_info2->y_coord[marker_info2->coord_num] = marker_info2->y_coord[0]; + marker_info2.coord_num++;//marker_info2->coord_num++; + + return marker_info2; + } + /** + * ARMarkerInfo2 *arDetectMarker2( ARInt16 *limage, int label_num, int *label_ref,int *warea, double *wpos, int *wclip,int area_max, int area_min, double factor, int *marker_num ) + * 関数の代替品 + * ラベリング情報からマーカー一覧を作成して、保持する。 + * @param i_labeling + * ラベリング済みの情報を持つラベリングオブジェクト + * @param area_max + * 何かの閾値? + * @param area_min + * 何かの閾値? + * @param factor + * 何かの閾値? + * @return + * @throws NyARException + */ +// public void detectMarker(short[][] limage,int label_num,int[] label_ref,int[] warea,double[] wpos,int[] wclip,int area_max, int area_min, double factor) throws JartkException + public void detectMarker(NyARLabeling i_labeling,double factor) throws NyARException + { + int xsize, ysize; + int marker_num2; + int i, j; + double d; + int[] warea =i_labeling.getArea(); + int label_num =i_labeling.getLabelNum(); + int[][] wclip =i_labeling.getClip(); + double[] wpos =i_labeling.getPos(); + short[][] limage =i_labeling.getLabelImg(); + int[] label_ref =i_labeling.getLabelRef(); + + marker_num=0; + xsize =width; + ysize =height; + + marker_num2 = 0; + for(i=0; i area_max ){ + continue; + } + if( wclip[i][0] == 1 || wclip[i][1] == xsize-2 ){//if( wclip[i*4+0] == 1 || wclip[i*4+1] == xsize-2 ){ + continue; + } + if( wclip[i][2] == 1 || wclip[i][3] == ysize-2 ){//if( wclip[i*4+2] == 1 || wclip[i*4+3] == ysize-2 ){ + continue; + } + //ret = arGetContour( limage, label_ref, i+1,&(wclip[i*4]), &(marker_info2[marker_num2])); + marker_info2_array[marker_num2]=arGetContour( limage, label_ref, i+1,wclip[i]); + + boolean ret = check_square( warea[i], marker_info2_array[marker_num2], factor );//ret = check_square( warea[i], &(marker_info2[marker_num2]), factor ); + if(!ret){ + marker_info2_array[marker_num2]=null; + continue; + } + marker_info2_array[marker_num2].area = warea[i]; + marker_info2_array[marker_num2].pos[0] = wpos[i*2+0]; + marker_info2_array[marker_num2].pos[1] = wpos[i*2+1]; + marker_num2++; + //マーカーリストが上限に達した + if( marker_num2 == marker_info2_array.length){ + break; + } + } + for( i=0; i < marker_num2; i++ ) { + for( j=i+1; j < marker_num2; j++ ) { + d = (marker_info2_array[i].pos[0] - marker_info2_array[j].pos[0])* + (marker_info2_array[i].pos[0] - marker_info2_array[j].pos[0])+ + (marker_info2_array[i].pos[1] - marker_info2_array[j].pos[1])* + (marker_info2_array[i].pos[1] - marker_info2_array[j].pos[1]); + if(marker_info2_array[i].area >marker_info2_array[j].area ) { + if( d + * + */ +package jp.nyatla.nyartoolkit.core; + +import jp.nyatla.util.DoubleValue; +import jp.nyatla.nyartoolkit.NyARException; +import jp.nyatla.nyartoolkit.core.raster.*; + + +public class NyARDetectSquare{ + private NyARLabeling labeling; + private NyARDetectMarker detect; + + private NyARSquare[] marker_info; + private int number_of_square; + private NyARParam param; + + /** + * 最大i_sqaure_max個のマーカー情報を抽出できるインスタンスを作る。 + * @param i_sqaure_max + */ + public NyARDetectSquare(int i_sqaure_max,NyARParam i_param) + { + param=i_param; + marker_info=new NyARSquare[i_sqaure_max];//static ARMarkerInfo marker_infoL[AR_SQUARE_MAX]; + //解析オブジェクトを作る + int width=i_param.getX(); + int height=i_param.getY(); + + labeling=new NyARLabeling(width,height); + detect=new NyARDetectMarker(width,height,i_sqaure_max); + } + public NyARSquare[] getSquareArray() + { + return marker_info; + } + public int getSquareCount() + { + return number_of_square; + } + /** + * 矩形を検出する。 + * @param i_marker + * @param i_number_of_marker + * @throws NyARException + */ + public void detectSquare(NyARRaster i_image,int i_thresh) throws NyARException + { + number_of_square=0; + labeling.labeling(i_image, i_thresh); + if(labeling.getLabelNum()<1){ + return; + } + detect.detectMarker(labeling,1.0); + NyARMarker[] marker=detect.getMarkerArray(); + int number_of_marker=detect.getMarkerNum(); + + + + int j=0; + for (int i = 0; i =marker_info.length){ + break; + } + } + number_of_square=j; + } + /** + * arGetLine(int x_coord[], int y_coord[], int coord_num,int vertex[], double line[4][3], double v[4][2]) + * arGetLine2(int x_coord[], int y_coord[], int coord_num,int vertex[], double line[4][3], double v[4][2], double *dist_factor) + * の2関数の合成品です。 + * @param x_coord + * @param y_coord + * @param coord_num + * @param vertex + * @param line + * @param v + * @return + * @throws NyARException + */ + private boolean getLine(int x_coord[], int y_coord[], int coord_num,int vertex[], double line[][], double v[][]) throws NyARException + { + NyARMat input,evec; + NyARVec ev,mean; + double w1; + int st, ed, n; + int i, j; + DoubleValue dv1=new DoubleValue(); + DoubleValue dv2=new DoubleValue(); + + ev = new NyARVec(2); + mean = new NyARVec(2); + evec = new NyARMat(2,2); + double[] mean_array=mean.getArray(); + double[][] evec_array=evec.getArray(); + for( i = 0; i < 4; i++ ) { + w1 = (double)(vertex[i+1]-vertex[i]+1) * 0.05 + 0.5; + st = (int)(vertex[i] + w1); + ed = (int)(vertex[i+1] - w1); + n = ed - st + 1; + if(n<2){//nが2以下でmatrix.PCAを計算することはできないので、エラーにしておく。 + //System.err.println("NyARDetectSquare::getLine 稀に出るエラーです。このエラーが出ても例外が起こらなければ平気だと思いますが、出たらnyatlaまで連絡してください。"); + return false;//throw new NyARException(); + } + input = new NyARMat( n, 2 ); + double [][] in_array=input.getArray(); + for( j = 0; j < n; j++ ) { + param.observ2Ideal(x_coord[st+j], y_coord[st+j],dv1,dv2);//arParamObserv2Ideal( dist_factor, x_coord[st+j], y_coord[st+j],&(input->m[j*2+0]), &(input->m[j*2+1]) ); + in_array[j][0]=dv1.get(); + in_array[j][1]=dv2.get(); + } + NyARMat.matrixPCA(input, evec, ev, mean); + + line[i][0] = evec_array[0][1];//line[i][0] = evec->m[1]; + line[i][1] = -evec_array[0][0];//line[i][1] = -evec->m[0]; + line[i][2] = -(line[i][0]*mean_array[0] + line[i][1]*mean_array[1]);//line[i][2] = -(line[i][0]*mean->v[0] + line[i][1]*mean->v[1]); + } + + for( i = 0; i < 4; i++ ) { + w1 = line[(i+3)%4][0] * line[i][1] - line[i][0] * line[(i+3)%4][1]; + if( w1 == 0.0 ){ + return false; + } + v[i][0] = ( line[(i+3)%4][1] * line[i][2]- line[i][1] * line[(i+3)%4][2] ) / w1; + v[i][1] = ( line[i][0] * line[(i+3)%4][2]- line[(i+3)%4][0] * line[i][2] ) / w1; + } + return true; + } +} diff --git a/src/jp/nyatla/nyartoolkit/core/NyARLabeling.java b/src/jp/nyatla/nyartoolkit/core/NyARLabeling.java new file mode 100644 index 0000000..d649977 --- /dev/null +++ b/src/jp/nyatla/nyartoolkit/core/NyARLabeling.java @@ -0,0 +1,383 @@ +/* + * PROJECT: NyARToolkit + * -------------------------------------------------------------------------------- + * This work is based on the original ARToolKit developed by + * Hirokazu Kato + * Mark Billinghurst + * HITLab, University of Washington, Seattle + * http://www.hitl.washington.edu/artoolkit/ + * + * The NyARToolkit is Java version ARToolkit class library. + * Copyright (C)2008 R.Iizuka + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this framework; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * For further information please contact. + * http://nyatla.jp/nyatoolkit/ + * + * + */ +package jp.nyatla.nyartoolkit.core; + +import jp.nyatla.nyartoolkit.NyARException; +import jp.nyatla.nyartoolkit.core.raster.*; + + + + +/** + * ラベリングクラス。NyARRasterをラベリングして、結果値を保持します。 + * + */ +public class NyARLabeling{ + private final int WORK_SIZE=1024*32;//#define WORK_SIZE 1024*32 + private short[][] label_img;//static ARInt16 l_imageL[HARDCODED_BUFFER_WIDTH*HARDCODED_BUFFER_HEIGHT]; + private int[] work=new int[WORK_SIZE];//static int workL[WORK_SIZE]; + private int[] work2=new int[WORK_SIZE*7];//static int work2L[WORK_SIZE*7]; + private int[] area=new int[WORK_SIZE];//static int wareaL[WORK_SIZE]; + private int[][] clip=new int[WORK_SIZE][4];//static int wclipL[WORK_SIZE*4]; + private double[] pos=new double[WORK_SIZE*2];//static double wposL[WORK_SIZE*2]; + private int label_num; + // + private int width; + private int height; + /** + * @param i_width + * ラベリング画像の幅。解析するラスタの幅より大きいこと。 + * @param i_height + * ラベリング画像の高さ。解析するラスタの高さより大きいこと。 + */ + public NyARLabeling(int i_width,int i_height) + { + width =i_width; + height=i_height; + label_img=new short[height][width]; + label_num=0; + } + /** + * 検出したラベルの数を返す + * @return + */ + public int getLabelNum() + { + return label_num; + } + /** + * わからん + * @return + * @throws NyARException + */ + public int[] getLabelRef() throws NyARException + { + if(label_num<1){ + throw new NyARException(); + } + return work; + } + /** + * 検出したエリア配列?よくわからぬ + * @return + * @throws NyARException + */ + public int[] getArea() throws NyARException + { + if(label_num<1){ + throw new NyARException(); + } + return area; + } + /** + * 検出したクリップ配列?よくわからぬ + * @return + * @throws NyARException + */ + public int[][] getClip() throws NyARException + { + if(label_num<1){ + throw new NyARException(); + } + return clip; + } + /** + * 検出した位置配列?よくわからぬ + * @return + * @throws NyARException + */ + public double[] getPos() throws NyARException + { + if(label_num<1){ + throw new NyARException(); + } + return pos; + } + /** + * ラベリング済みイメージを返す + * @return + * @throws NyARException + */ + public short[][] getLabelImg() throws NyARException + { + if(label_num<1){ + throw new NyARException(); + } + return label_img; + } + /** + * 配列の先頭からsize個をゼロクリアする + * @param array + * @param size + */ + private void putZero(int[] array,int size) + { + for(int i=0;i0){//if( *pnt1 > 0 ) { + label_img[p2][i]=label_img[p1][i];//*pnt2 = *pnt1; + + + int p2_index=(label_img[p2][i]-1)*7; + work2[p2_index+0]++;//work2[((*pnt2)-1)*7+0] ++; + work2[p2_index+1]+=i;//work2[((*pnt2)-1)*7+1] += i; + work2[p2_index+2]+=j;//work2[((*pnt2)-1)*7+2] += j; + work2[p2_index+6]=j;//work2[((*pnt2)-1)*7+6] = j; + }else if(label_img[p1][i+1]> 0 ) {//}else if( *(pnt1+1) > 0 ) { + if(label_img[p1][i-1] > 0 ) {//if( *(pnt1-1) > 0 ) { + m = work[label_img[p1][i+1]-1];//m = work[*(pnt1+1)-1]; + n = work[label_img[p1][i-1]-1];//n = work[*(pnt1-1)-1]; + if( m > n ){ + //JartkException.trap("未チェックのパス"); + label_img[p2][i]=(short)n;//*pnt2 = n; + //wk=IntPointer.wrap(work, 0);//wk = &(work[0]); + for(int k = 0; k < wk_max; k++) { + //JartkException.trap("未チェックのパス"); + if(work[k] == m ){//if( *wk == m ) + //JartkException.trap("未チェックのパス"); + work[k]=n;//*wk = n; + } + } + }else if( m < n ) { + //JartkException.trap("未チェックのパス"); + label_img[p2][i]=(short)m;//*pnt2 = m; + //wk=IntPointer.wrap(work,0);//wk = &(work[0]); + for(int k = 0; k < wk_max; k++){ + //JartkException.trap("未チェックのパス"); + if(work[k]==n){//if( *wk == n ){ + //JartkException.trap("未チェックのパス"); + work[k]=m;//*wk = m; + } + } + }else{ + label_img[p2][i]=(short)m;//*pnt2 = m; + } + + int p2_index=(label_img[p2][i]-1)*7; + work2[p2_index+0] ++; + work2[p2_index+1] += i; + work2[p2_index+2] += j; + work2[p2_index+6] = j; + }else if( (label_img[p2][i-1]) > 0 ) {//}else if( *(pnt2-1) > 0 ) { + m = work[(label_img[p1][i+1])-1];//m = work[*(pnt1+1)-1]; + n = work[(label_img[p2][i-1])-1];//n = work[*(pnt2-1)-1]; + if( m > n ) { + + label_img[p2][i]=(short)n;//*pnt2 = n; + for(int k = 0; k < wk_max; k++) { + if(work[k]==m){//if( *wk == m ){ + work[k]=n;//*wk = n; + } + } + }else if( m < n ) { + label_img[p2][i]=(short)m;//*pnt2 = m; + for(int k = 0; k < wk_max; k++) { + if(work[k]==n){//if( *wk == n ){ + work[k]=m;//*wk = m; + } + } + }else{ + label_img[p2][i]=(short)m;//*pnt2 = m; + } + + + int p2_index=((label_img[p2][i])-1)*7; + work2[p2_index+0] ++;//work2[((*pnt2)-1)*7+0] ++; + work2[p2_index+1] += i;//work2[((*pnt2)-1)*7+1] += i; + work2[p2_index+2] += j;//work2[((*pnt2)-1)*7+2] += j; + }else{ + + label_img[p2][i]=label_img[p1][i+1];//*pnt2 = *(pnt1+1); + + int p2_index=((label_img[p2][i])-1)*7; + work2[p2_index+0] ++;//work2[((*pnt2)-1)*7+0] ++; + work2[p2_index+1] += i;//work2[((*pnt2)-1)*7+1] += i; + work2[p2_index+2] += j;//work2[((*pnt2)-1)*7+2] += j; + if( work2[p2_index+3] > i ){//if( work2[((*pnt2)-1)*7+3] > i ){ + work2[p2_index+3] = i;// work2[((*pnt2)-1)*7+3] = i; + } + work2[p2_index+6] = j;//work2[((*pnt2)-1)*7+6] = j; + } + }else if( (label_img[p1][i-1]) > 0 ) {//}else if( *(pnt1-1) > 0 ) { + label_img[p2][i]=label_img[p1][i-1];//*pnt2 = *(pnt1-1); + + int p2_index=((label_img[p2][i])-1)*7; + work2[p2_index+0] ++;//work2[((*pnt2)-1)*7+0] ++; + work2[p2_index+1] += i;//work2[((*pnt2)-1)*7+1] += i; + work2[p2_index+2] += j;//work2[((*pnt2)-1)*7+2] += j; + if( work2[p2_index+4] < i ){//if( work2[((*pnt2)-1)*7+4] < i ){ + work2[p2_index+4] = i;// work2[((*pnt2)-1)*7+4] = i; + } + work2[p2_index+6] = j;//work2[((*pnt2)-1)*7+6] = j; + }else if(label_img[p2][i-1] > 0) {//}else if( *(pnt2-1) > 0) { + label_img[p2][i]=label_img[p2][i-1];//*pnt2 = *(pnt2-1); + + int p2_index=((label_img[p2][i])-1)*7; + work2[p2_index+0] ++;//work2[((*pnt2)-1)*7+0] ++; + work2[p2_index+1] += i;//work2[((*pnt2)-1)*7+1] += i; + work2[p2_index+2] += j;//work2[((*pnt2)-1)*7+2] += j; + if( work2[p2_index+4] < i ){//if( work2[((*pnt2)-1)*7+4] < i ){ + work2[p2_index+4] = i;// work2[((*pnt2)-1)*7+4] = i; + } + }else{ + wk_max++; + if( wk_max > WORK_SIZE ) { + throw new NyARException();//return (0); + } + work[wk_max-1] = wk_max;label_img[p2][i]=(short)wk_max;//work[wk_max-1] = *pnt2 = wk_max; + work2[(wk_max-1)*7+0] = 1; + work2[(wk_max-1)*7+1] = i; + work2[(wk_max-1)*7+2] = j; + work2[(wk_max-1)*7+3] = i; + work2[(wk_max-1)*7+4] = i; + work2[(wk_max-1)*7+5] = j; + work2[(wk_max-1)*7+6] = j; + } + }else { + label_img[p2][i]=0;//*pnt2 = 0; + } + } + } + int j = 1; + for(int i = 0; i < wk_max; i++){//for(int i = 1; i <= wk_max; i++, wk++) { + work[i]=(work[i]==i+1)? j++: work[work[i]-1];//*wk = (*wk==i)? j++: work[(*wk)-1]; + } + + int wlabel_num=j - 1;//*label_num = *wlabel_num = j - 1; + + if(wlabel_num==0){//if( *label_num == 0 ) { + //発見数0 + return; + } + + putZero(warea,wlabel_num);//put_zero( (ARUint8 *)warea, *label_num * sizeof(int) ); + putZero(wpos,wlabel_num*2);//put_zero( (ARUint8 *)wpos, *label_num * 2 * sizeof(double) ); + for(int i = 0; i < wlabel_num; i++) {//for(i = 0; i < *label_num; i++) { + wclip[i][0] = lxsize;//wclip[i*4+0] = lxsize; + wclip[i][1] = 0;//wclip[i*4+1] = 0; + wclip[i][2] = lysize;//wclip[i*4+2] = lysize; + wclip[i][3] = 0;//wclip[i*4+3] = 0; + } + for(int i = 0; i < wk_max; i++) { + j = work[i] - 1; + warea[j] += work2[i*7+0]; + wpos[j*2+0] += work2[i*7+1]; + wpos[j*2+1] += work2[i*7+2]; + if( wclip[j][0] > work2[i*7+3] ){ + wclip[j][0] = work2[i*7+3]; + } + if( wclip[j][1] < work2[i*7+4] ){ + wclip[j][1] = work2[i*7+4]; + } + if( wclip[j][2] > work2[i*7+5] ){ + wclip[j][2] = work2[i*7+5]; + } + if( wclip[j][3] < work2[i*7+6] ){ + wclip[j][3] = work2[i*7+6]; + } + } + + for(int i = 0; i < wlabel_num; i++ ) {//for(int i = 0; i < *label_num; i++ ) { + wpos[i*2+0] /= warea[i]; + wpos[i*2+1] /= warea[i]; + } + + label_num=wlabel_num; + return; + } +} + diff --git a/src/jp/nyatla/nyartoolkit/core/NyARMarker.java b/src/jp/nyatla/nyartoolkit/core/NyARMarker.java new file mode 100644 index 0000000..48fdaa6 --- /dev/null +++ b/src/jp/nyatla/nyartoolkit/core/NyARMarker.java @@ -0,0 +1,60 @@ +/* + * PROJECT: NyARToolkit + * -------------------------------------------------------------------------------- + * This work is based on the original ARToolKit developed by + * Hirokazu Kato + * Mark Billinghurst + * HITLab, University of Washington, Seattle + * http://www.hitl.washington.edu/artoolkit/ + * + * The NyARToolkit is Java version ARToolkit class library. + * Copyright (C)2008 R.Iizuka + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this framework; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * For further information please contact. + * http://nyatla.jp/nyatoolkit/ + * + * + */ +package jp.nyatla.nyartoolkit.core; + + + + + +/** + * typedef struct { + * int area; + * double pos[2]; + * int coord_num; + * int x_coord[AR_CHAIN_MAX]; + * int y_coord[AR_CHAIN_MAX]; + * int vertex[5]; + * } ARMarkerInfo2; + * + */ +class NyARMarker +{ + private static final int AR_CHAIN_MAX=10000; + int area; + double[] pos=new double[2]; + int coord_num; + int[] x_coord=new int[AR_CHAIN_MAX]; + int[] y_coord=new int[AR_CHAIN_MAX]; + int[] vertex=new int[5]; +} + + diff --git a/src/jp/nyatla/nyartoolkit/core/NyARMat.java b/src/jp/nyatla/nyartoolkit/core/NyARMat.java new file mode 100644 index 0000000..223637e --- /dev/null +++ b/src/jp/nyatla/nyartoolkit/core/NyARMat.java @@ -0,0 +1,861 @@ +/* + * PROJECT: NyARToolkit + * -------------------------------------------------------------------------------- + * This work is based on the original ARToolKit developed by + * Hirokazu Kato + * Mark Billinghurst + * HITLab, University of Washington, Seattle + * http://www.hitl.washington.edu/artoolkit/ + * + * The NyARToolkit is Java version ARToolkit class library. + * Copyright (C)2008 R.Iizuka + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this framework; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * For further information please contact. + * http://nyatla.jp/nyatoolkit/ + * + * + */ +package jp.nyatla.nyartoolkit.core; + +import jp.nyatla.nyartoolkit.NyARException; + + + +/** + * ARMat構造体に対応するクラス + * typedef struct { + * double *m; + * int row; + * int clm; + * }ARMat; + * + */ +public class NyARMat{ + private double[][] m; + private int clm,row; + public NyARMat(int i_row,int i_clm) + { + m=new double[i_row][i_clm]; + clm=i_clm; + row=i_row; + } + public int getClm() + { + return clm; + } + public int getRow() + { + return row; + } + /** + * 行列をゼロクリアする。 + */ + public void zeroClear() + { + for(int i=0;irow, m->clm); + for(int r = 0; r < row; r++){//for(int r = 0; r < m->row; r++) { + System.out.print(" |");//printf(" |"); + for(int c = 0; c < clm; c++) {//for(int c = 0; c < m->clm; c++) { + System.out.print(" "+m[r][c]);//printf(" %10g", ARELEM0(m, r, c)); + } + System.out.println(" |");//printf(" |\n"); + } + System.out.println(" ======================");//printf(" ======================\n"); + return 0; + } + private final static double PCA_EPS=1e-6; //#define EPS 1e-6 + private final static int PCA_MAX_ITER=100; //#define MAX_ITER 100 + private final static double PCA_VZERO=1e-16; //#define VZERO 1e-16 + /** + * static int EX( ARMat *input, ARVec *mean )の代替関数 + * @param input + * @param mean + * @return + * @throws NyARException + */ + private static void PCA_EX(NyARMat input, NyARVec mean) throws NyARException + { + double[] v; + + int row, clm; + + row = input.row; + clm = input.clm; + if(row <= 0 || clm <= 0){ + throw new NyARException(); + } + if( mean.getClm() != clm ){ + throw new NyARException(); + } + double[] mean_array=mean.getArray(); + for(int i = 0; i < clm; i++ ){ + mean_array[i]=0.0;//mean->v[i] = 0.0; + } + + v=mean.getArray(); + for(int i = 0; i < row; i++ ) { + for(int j = 0; j < clm; j++ ){ + //*(v++) += *(m++); + v[j]+=input.m[i][j]; + } + } + + for(int i = 0; i < clm; i++ ){ + mean_array[i]/=row;//mean->v[i] /= row; + } + } + /** + * static int CENTER( ARMat *inout, ARVec *mean )の代替関数 + * @param inout + * @param mean + * @return + */ + private static void PCA_CENTER(NyARMat inout, NyARVec mean) throws NyARException + { + double[] v; + int row, clm; + + row = inout.getRow(); + clm = inout.getClm(); + if(mean.getClm()!= clm){ + throw new NyARException(); + } + + v = mean.getArray(); + for(int i = 0; i < row; i++ ) { + for(int j = 0; j < clm; j++ ){ + //*(m++) -= *(v++); + inout.m[i][j]-=v[j]; + } + } + } + /** + * int x_by_xt( ARMat *input, ARMat *output )の代替関数 + * @param input + * @param output + * @throws NyARException + */ + private static void PCA_x_by_xt( NyARMat input, NyARMat output) throws NyARException + { + NyARException.trap("動作未チェック/配列化未チェック"); + int row, clm; +// double[][] out; + double[] in1,in2; + + NyARException.trap("未チェックのパス"); + row = input.row; + clm = input.clm; + NyARException.trap("未チェックのパス"); + if( output.row != row || output.clm != row ){ + throw new NyARException(); + } + +// out = output.getArray(); + for(int i = 0; i < row; i++ ) { + for(int j = 0; j < row; j++ ) { + if( j < i ) { + NyARException.trap("未チェックのパス");{ + output.m[i][j]=output.m[j][i];//*out = output->m[j*row+i]; + } + }else{ + in1=input.getRowArray(i);//in1 = &(input->m[clm*i]); + in2=input.getRowArray(j);//in2 = &(input->m[clm*j]); + output.m[i][j]=0;//*out = 0.0; + for(int k = 0; k < clm; k++ ){ + output.m[i][j]+=(in1[k]*in2[k]);//*out += *(in1++) * *(in2++); + } + } + // out.incPtr(); + } + } + } + /** + * static int xt_by_x( ARMat *input, ARMat *output )の代替関数 + * @param input + * @param output + * @throws NyARException + */ + private static void PCA_xt_by_x(NyARMat input, NyARMat output) throws NyARException + { + double[] in; + int row, clm; + + row = input.row; + clm = input.clm; + if(output.row!= clm || output.clm != clm ){ + throw new NyARException(); + } + + for(int i = 0; i < clm; i++ ) { + for(int j = 0; j < clm; j++ ) { + if( j < i ) { + output.m[i][j]=output.m[j][i];//*out = output->m[j*clm+i]; + }else{ + output.m[i][j]=0.0;//*out = 0.0; + for(int k = 0; k < row; k++ ){ + in=input.getRowArray(k); + output.m[i][j]+=(in[i]*in[j]);//*out += *in1 * *in2; + } + } + } + } + } + /** + * static int QRM( ARMat *a, ARVec *dv )の代替関数 + * @param a + * @param dv + * @throws NyARException + */ + private static void PCA_QRM(NyARMat a, NyARVec dv) throws NyARException + { + double w, t, s, x, y, c; + int dim, iter; + double[] dv_array=dv.getArray(); + + dim = a.row; + if( dim != a.clm || dim < 2 ){ + throw new NyARException(); + } + if( dv.getClm() != dim ){ + throw new NyARException(); + } + + NyARVec ev = new NyARVec( dim ); + double[] ev_array=ev.getArray(); + if( ev == null ){ + throw new NyARException(); + } + + NyARVec.vecTridiagonalize(a,dv,ev,1); + + ev_array[0]=0.0;//ev->v[0] = 0.0; + for(int h = dim-1; h > 0; h-- ) { + int j = h; + while(j>0 && Math.abs(ev_array[j]) > PCA_EPS*(Math.abs(dv_array[j-1])+Math.abs(dv_array[j]))){// while(j>0 && fabs(ev->v[j]) > EPS*(fabs(dv->v[j-1])+fabs(dv->v[j]))) j--; + j--; + } + if( j == h ){ + continue; + } + iter = 0; + do{ + iter++; + if( iter > PCA_MAX_ITER ){ + break; + } + w = (dv_array[h-1] - dv_array[h]) / 2;//w = (dv->v[h-1] - dv->v[h]) / 2;//ここ? + t = ev_array[h] * ev_array[h];//t = ev->v[h] * ev->v[h]; + s = Math.sqrt(w*w+t); + if( w < 0 ){ + s = -s; + } + x=dv_array[j] - dv_array[h] + t/(w+s);//x = dv->v[j] - dv->v[h] + t/(w+s); + y=ev_array[j+1];//y = ev->v[j+1]; + for(int k = j; k < h; k++ ){ + if( Math.abs(x) >= Math.abs(y)){ + if( Math.abs(x) > PCA_VZERO ) { + t = -y / x; + c = 1 / Math.sqrt(t*t+1); + s = t * c; + }else{ + c = 1.0; + s = 0.0; + } + }else{ + t = -x / y; + s = 1.0 / Math.sqrt(t*t+1); + c = t * s; + } + w = dv_array[k] - dv_array[k+1];//w = dv->v[k] - dv->v[k+1]; + t = (w * s + 2 * c * ev_array[k+1]) * s;//t = (w * s + 2 * c * ev->v[k+1]) * s; + dv_array[k]-=t;//dv->v[k] -= t; + dv_array[k+1]+=t;//dv->v[k+1] += t; + if( k > j){ + NyARException.trap("未チェックパス");{ + ev_array[k]=c * ev_array[k] - s * y;//ev->v[k] = c * ev->v[k] - s * y; + } + } + ev_array[k+1]+=s * (c * w - 2 * s * ev_array[k+1]);//ev->v[k+1] += s * (c * w - 2 * s * ev->v[k+1]); + + for(int i = 0; i < dim; i++ ){ + x = a.m[k][i];//x = a->m[k*dim+i]; + y = a.m[k+1][i];//y = a->m[(k+1)*dim+i]; + a.m[k][i]=c * x - s * y;//a->m[k*dim+i] = c * x - s * y; + a.m[k+1][i]=s * x + c * y;//a->m[(k+1)*dim+i] = s * x + c * y; + } + if( k < h-1 ) { + NyARException.trap("未チェックパス");{ + x = ev_array[k+1];//x = ev->v[k+1]; + y =-s*ev_array[k+2];//y = -s * ev->v[k+2]; + ev_array[k+2]*=c;//ev->v[k+2] *= c; + } + } + } + }while(Math.abs(ev_array[h]) > PCA_EPS*(Math.abs(dv_array[h-1])+Math.abs(dv_array[h]))); + } + double[] v1,v2; + for(int k = 0; k < dim-1; k++ ) { + int h = k; + t=dv_array[h];//t = dv->v[h]; + for(int i = k+1; i < dim; i++ ){ + if(dv_array[i] > t ){//if( dv->v[i] > t ) { + h = i; + t=dv_array[h];//t = dv->v[h]; + } + } + dv_array[h]=dv_array[k];//dv->v[h] = dv->v[k]; + dv_array[k]=t;//dv->v[k] = t; + v1=a.getRowArray(h);//v1 = &(a->m[h*dim]); + v2=a.getRowArray(k);//v2 = &(a->m[k*dim]); + for(int i = 0; i < dim; i++ ) { + w=v1[i];//w = *v1; + v1[i]=v2[i];//*v1 = *v2; + v2[i]=w;//*v2 = w; + } + } + } + /** + * static int EV_create( ARMat *input, ARMat *u, ARMat *output, ARVec *ev )の代替関数 + * @param input + * @param u + * @param output + * @param ev + * @throws NyARException + */ + private static void PCA_EV_create(NyARMat input, NyARMat u, NyARMat output, NyARVec ev) throws NyARException + { + NyARException.trap("未チェックのパス"); + int row, clm; + row = input.row;//row = input->row; + clm = input.clm;//clm = input->clm; + if( row <= 0 || clm <= 0 ){ + throw new NyARException(); + } + if( u.row != row || u.clm != row ){//if( u->row != row || u->clm != row ){ + throw new NyARException(); + } + if( output.row != row || output.clm != clm ){//if( output->row != row || output->clm != clm ){ + throw new NyARException(); + } + if( ev.getClm()!= row ){//if( ev->clm != row ){ + throw new NyARException(); + } + double[][] m,in; + double[] m1,ev_array; + double sum, work; + + m =output.getArray();//m = output->m; + in=input.getArray(); + int i; + ev_array=ev.getArray(); + for(i = 0; i < row; i++ ) { + NyARException.trap("未チェックのパス"); + if( ev_array[i]v[i] < VZERO ){ + break; + } + NyARException.trap("未チェックのパス"); + work = 1 / Math.sqrt(Math.abs(ev_array[i]));//work = 1 / sqrt(fabs(ev->v[i])); + for(int j = 0; j < clm; j++ ) { + sum = 0.0; + m1=u.getRowArray(i);//m1 = &(u->m[i*row]); + // m2=input.getPointer(j);//m2 = &(input->m[j]); + for(int k = 0; k < row; k++ ) { + sum+=m1[k]+in[k][j];//sum += *m1 * *m2; + // m1.incPtr(); //m1++; + // m2.addPtr(clm);//m2 += clm; + } + m1[j]=sum * work;//*(m++) = sum * work; + // {//*(m++) = sum * work; + // m.set(sum * work); + // m.incPtr();} + } + } + for( ; i < row; i++ ) { + NyARException.trap("未チェックのパス"); + ev_array[i]=0.0;//ev->v[i] = 0.0; + for(int j = 0; j < clm; j++ ){ + m[i][j]=0.0; + // m.set(0.0);//*(m++) = 0.0; + // m.incPtr(); + } + } + } + /*static int PCA( ARMat *input, ARMat *output, ARVec *ev )*/ + private static void PCA_PCA(NyARMat input, NyARMat output, NyARVec ev) throws NyARException + { + NyARMat u; + int row, clm, min; + double[] ev_array=ev.getArray(); + + row =input.row;//row = input->row; + clm =input.clm;//clm = input->clm; + min =(clm < row)? clm: row; + if( row < 2 || clm < 2 ){ + throw new NyARException(); + } + if( output.clm != input.clm){//if( output->clm != input->clm ){ + throw new NyARException(); + } + if( output.row!= min ){//if( output->row != min ){ + throw new NyARException(); + } + if( ev.getClm() != min ){//if( ev->clm != min ){ + throw new NyARException(); + } + u =new NyARMat( min, min ); + + if( row < clm ){ + NyARException.trap("未チェックのパス"); + PCA_x_by_xt( input, u );//if(x_by_xt( input, u ) < 0 ) { + }else{ + PCA_xt_by_x( input, u );//if(xt_by_x( input, u ) < 0 ) { + } + PCA_QRM( u, ev ); + + double[][] m1,m2; + if( row < clm ) { + NyARException.trap("未チェックのパス");{ + PCA_EV_create( input, u, output, ev ); + } + }else{ + m1=u.m;//m1 = u->m; + m2=output.m;//m2 = output->m; + int i; + for(i = 0; i < min; i++){ + if( ev_array[i] < PCA_VZERO){//if( ev->v[i] < VZERO ){ + break; + } + for(int j = 0; j < min; j++ ){ + m2[i][j]=m1[i][j];//*(m2++) = *(m1++); + } + } + for( ; i < min; i++){//for( ; i < min; i++){ + //コードを見た限りあってそうだからコメントアウト(2008/03/26)NyARException.trap("未チェックのパス"); + ev_array[i]=0.0;//ev->v[i] = 0.0; + for(int j = 0; j < min; j++ ){ + m2[i][j]=0.0;//*(m2++) = 0.0; + } + } + } + } + + /*int arMatrixPCA( ARMat *input, ARMat *evec, ARVec *ev, ARVec *mean );*/ + public static void matrixPCA(NyARMat input, NyARMat evec, NyARVec ev, NyARVec mean) throws NyARException + { + NyARMat work; + double srow, sum; + int row, clm; + int check; + + row=input.row;//row = input->row; + clm=input.clm;//clm = input->clm; + check = (row < clm)? row: clm; + if( row < 2 || clm < 2 ){ + throw new NyARException(); + } + if( evec.clm != input.clm || evec.row != check ){//if( evec->clm != input->clm || evec->row != check ){ + throw new NyARException(); + } + if( ev.getClm() != check ){//if( ev->clm != check ){ + throw new NyARException(); + } + if( mean.getClm() != input.clm){//if( mean->clm != input->clm ){ + throw new NyARException(); + } + + work =input.matrixAllocDup();//arMatrixAllocDup( input );work = arMatrixAllocDup( input ); + + srow = Math.sqrt((double)row); + PCA_EX( work, mean ); + + PCA_CENTER(work,mean); + + + for(int i=0; im[i] /= srow; + } + } + + PCA_PCA( work, evec, ev ); + + sum = 0.0; + double[] ev_array=ev.getArray(); + for(int i = 0; i < ev.getClm(); i++ ){//for(int i = 0; i < ev->clm; i++ ){ + sum+=ev_array[i];//sum += ev->v[i]; + } + for(int i = 0; i < ev.getClm(); i++ ){//for(int i = 0; i < ev->clm; i++ ){ + ev_array[i]/=sum;//ev->v[i] /= sum; + } + } + + /*int arMatrixPCA2( ARMat *input, ARMat *evec, ARVec *ev );*/ + public static void arMatrixPCA2( NyARMat input, NyARMat evec, NyARVec ev) throws NyARException + { + NyARException.trap("未チェックのパス"); + NyARMat work; + // double srow; // unreferenced + double sum; + int row, clm; + int check; + + row=input.row;//row = input->row; + clm=input.clm;//clm = input->clm; + check = (row < clm)? row: clm; + if( row < 2 || clm < 2 ){ + throw new NyARException(); + } + if( evec.getClm()!= input.clm|| evec.row!=check){//if( evec->clm != input->clm || evec->row != check ){ + throw new NyARException(); + } + if( ev.getClm() != check ){//if( ev->clm != check ){ + throw new NyARException(); + } + + NyARException.trap("未チェックのパス"); + work =input.matrixAllocDup(); + + NyARException.trap("未チェックパス"); + PCA_PCA( work, evec, ev );//rval = PCA( work, evec, ev ); + sum = 0.0; + double[] ev_array=ev.getArray(); + for(int i = 0; i < ev.getClm(); i++ ){//for( i = 0; i < ev->clm; i++ ){ + NyARException.trap("未チェックパス"); + sum+=ev_array[i];//sum += ev->v[i]; + } + for(int i = 0; i < ev.getClm(); i++ ){//for(int i = 0; i < ev->clm; i++ ){ + NyARException.trap("未チェックパス"); + ev_array[i]/=sum;//ev->v[i] /= sum; + } + return; + } + public static NyARMat matrixAllocMul(NyARMat a, NyARMat b) throws NyARException + { + NyARException.trap("未チェックのパス"); + NyARMat dest=new NyARMat(a.row, b.clm); + NyARException.trap("未チェックのパス"); + matrixMul(dest, a, b); + return dest; + } + /*static double mdet(double *ap, int dimen, int rowa)*/ + private static double Det_mdet(double[][] ap, int dimen, int rowa) throws NyARException + { + NyARException.trap("動作未チェック/配列化未チェック"); + double det = 1.0; + double work; + int is = 0; + int mmax; + + for(int k = 0; k < dimen - 1; k++) { + mmax = k; + for(int i = k + 1; i < dimen; i++){ +// if (Math.abs(arMatrixDet_MATRIX_get(ap, i, k, rowa)) > Math.abs(arMatrixDet_MATRIX_get(ap, mmax, k, rowa))){ + if (Math.abs(ap[i][k]) > Math.abs(ap[mmax][k])){ + mmax = i; + } + } + if(mmax != k) { + for (int j = k; j < dimen; j++) { + work = ap[k][j];//work = MATRIX(ap, k, j, rowa); + ap[k][j]=ap[mmax][j];//MATRIX(ap, k, j, rowa) = MATRIX(ap, mmax, j, rowa); + ap[mmax][j]=work;//MATRIX(ap, mmax, j, rowa) = work; + } + is++; + } + for(int i = k + 1; i < dimen; i++) { + work = ap[i][k]/ ap[k][k];//work = arMatrixDet_MATRIX_get(ap, i, k, rowa) / arMatrixDet_MATRIX_get(ap, k, k, rowa); + for (int j = k + 1; j < dimen; j++){ + //MATRIX(ap, i, j, rowa) -= work * MATRIX(ap, k, j, rowa); + ap[i][j]-=work * ap[k][j]; + } + } + } + for(int i = 0; i < dimen; i++){ + det=ap[i][i];//det *= MATRIX(ap, i, i, rowa); + } + for(int i = 0; i < is; i++){ + det *= -1.0; + } + return det; + } + /*double arMatrixDet(ARMat *m);*/ + public static double arMatrixDet(NyARMat m) throws NyARException + { + NyARException.trap("動作未チェック/配列化未チェック"); + if(m.row != m.clm){ + return 0.0; + } + return Det_mdet(m.getArray(), m.row, m.clm);//return mdet(m->m, m->row, m->row); + } +} \ No newline at end of file diff --git a/src/jp/nyatla/nyartoolkit/core/NyARParam.java b/src/jp/nyatla/nyartoolkit/core/NyARParam.java new file mode 100644 index 0000000..a91400a --- /dev/null +++ b/src/jp/nyatla/nyartoolkit/core/NyARParam.java @@ -0,0 +1,351 @@ +/* + * PROJECT: NyARToolkit + * -------------------------------------------------------------------------------- + * This work is based on the original ARToolKit developed by + * Hirokazu Kato + * Mark Billinghurst + * HITLab, University of Washington, Seattle + * http://www.hitl.washington.edu/artoolkit/ + * + * The NyARToolkit is Java version ARToolkit class library. + * Copyright (C)2008 R.Iizuka + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this framework; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * For further information please contact. + * http://nyatla.jp/nyatoolkit/ + * + * + */ +package jp.nyatla.nyartoolkit.core; + + +import java.io.*; +import java.nio.*; + +import jp.nyatla.nyartoolkit.NyARException; +import jp.nyatla.util.DoubleValue; + +/*typedef struct { + int xsize, ysize; + double mat[3][4]; + double dist_factor[4]; +} ARParam;*/ +public class NyARParam{ + private static final int SIZE_OF_PARAM_SET=4+4+(3*4*8)+(4*8); + private static final int PD_LOOP = 3; + protected int xsize, ysize; + private double[][] mat=new double[3][4];//Double2dArray mat=new Double2dArray(3,4); + private double[] dist_factor=new double[4]; + public int getX() + { + return xsize; + } + public int getY() + { + return ysize; + } + public double[] getDistFactor() + { + return dist_factor; + } + public double[][] getMat() + { + return mat; + } + /** + * ARToolKit標準ファイルから1個目の設定をロードする。 + * @param i_filename + * @throws NyARException + */ + public void loadFromARFile(String i_filename) throws NyARException + { + try{ + FileInputStream fs=new FileInputStream(i_filename); + NyARParam new_inst[]=arParamLoad(fs,1); + fs.close(); + xsize =new_inst[0].xsize; + ysize =new_inst[0].ysize; + mat =new_inst[0].mat; + dist_factor=new_inst[0].dist_factor; + }catch(Exception e){ + throw new NyARException(e); + } + } + /*static double dot( double a1, double a2, double a3,double b1, double b2, double b3 )*/ + private static double dot( double a1, double a2, double a3,double b1, double b2, double b3 ) + { + return( a1 * b1 + a2 * b2 + a3 * b3 ); + } + /* static double norm( double a, double b, double c )*/ + private static double norm( double a, double b, double c ) + { + return Math.sqrt( a*a + b*b + c*c ); + } + /** + * int arParamDecompMat( double source[3][4], double cpara[3][4], double trans[3][4] ); + * 関数の置き換え + * @param o_cpara + * 戻り引数。3x4のマトリクスを指定すること。 + * @param o_trans + * 戻り引数。3x4のマトリクスを指定すること。 + * @return + */ + public void decompMat(NyARMat o_cpara, NyARMat o_trans) + { + double[][] source=mat; + double[][] Cpara=new double[3][4];//double Cpara[3][4]; + double rem1, rem2, rem3; + + if(source[2][3]>= 0 ){//if( source[2][3] >= 0 ) { + for(int r = 0; r < 3; r++ ){ + for(int c = 0; c < 4; c++ ){ + Cpara[r][c]=source[r][c];//Cpara[r][c] = source[r][c]; + } + } + }else { + for(int r = 0; r < 3; r++ ){ + for(int c = 0; c < 4; c++ ){ + Cpara[r][c]=-source[r][c];//Cpara[r][c] = -(source[r][c]); + } + } + } + + double[][] cpara=o_cpara.getArray(); + double[][] trans=o_trans.getArray(); + for(int r = 0; r < 3; r++ ){ + for(int c = 0; c < 4; c++ ){ + cpara[r][c]=0.0;//cpara[r][c] = 0.0; + } + } + cpara[2][2]=norm(Cpara[2][0],Cpara[2][1], Cpara[2][2]);//cpara[2][2] = norm( Cpara[2][0], Cpara[2][1], Cpara[2][2] ); + trans[2][0]=Cpara[2][0] / cpara[2][2];//trans[2][0] = Cpara[2][0] / cpara[2][2]; + trans[2][1]=Cpara[2][1]/ cpara[2][2];//trans[2][1] = Cpara[2][1] / cpara[2][2]; + trans[2][2]=Cpara[2][2]/ cpara[2][2];//trans[2][2] = Cpara[2][2] / cpara[2][2]; + trans[2][3]=Cpara[2][3] / cpara[2][2];//trans[2][3] = Cpara[2][3] / cpara[2][2]; + + cpara[1][2]=dot(trans[2][0], trans[2][1], trans[2][2],Cpara[1][0], Cpara[1][1], Cpara[1][2]);//cpara[1][2] = dot( trans[2][0], trans[2][1], trans[2][2],Cpara[1][0], Cpara[1][1], Cpara[1][2] ); + rem1=Cpara[1][0]- cpara[1][2] * trans[2][0];//rem1 = Cpara[1][0] - cpara[1][2] * trans[2][0]; + rem2=Cpara[1][1] - cpara[1][2] * trans[2][1];//rem2 = Cpara[1][1] - cpara[1][2] * trans[2][1]; + rem3=Cpara[1][2] - cpara[1][2] * trans[2][2];//rem3 = Cpara[1][2] - cpara[1][2] * trans[2][2]; + cpara[1][1]=norm(rem1, rem2, rem3 );//cpara[1][1] = norm( rem1, rem2, rem3 ); + trans[1][0]= rem1/cpara[1][1];//trans[1][0] = rem1 / cpara[1][1]; + trans[1][1]=rem2/cpara[1][1];//trans[1][1] = rem2 / cpara[1][1]; + trans[1][2]=rem3 / cpara[1][1];//trans[1][2] = rem3 / cpara[1][1]; + + cpara[0][2]=dot(trans[2][0],trans[2][1], trans[2][2],Cpara[0][0], Cpara[0][1], Cpara[0][2] );//cpara[0][2] = dot( trans[2][0], trans[2][1], trans[2][2],Cpara[0][0], Cpara[0][1], Cpara[0][2] ); + cpara[0][1]=dot(trans[1][0],trans[1][1], trans[1][2],Cpara[0][0], Cpara[0][1], Cpara[0][2]);//cpara[0][1] = dot( trans[1][0], trans[1][1], trans[1][2],Cpara[0][0], Cpara[0][1], Cpara[0][2] ); + rem1=Cpara[0][0]- cpara[0][1]*trans[1][0] - cpara[0][2]*trans[2][0];//rem1 = Cpara[0][0] - cpara[0][1]*trans[1][0] - cpara[0][2]*trans[2][0]; + rem2 = Cpara[0][1] - cpara[0][1]*trans[1][1] - cpara[0][2]*trans[2][1];//rem2 = Cpara[0][1] - cpara[0][1]*trans[1][1] - cpara[0][2]*trans[2][1]; + rem3 = Cpara[0][2] - cpara[0][1]*trans[1][2] - cpara[0][2]*trans[2][2];//rem3 = Cpara[0][2] - cpara[0][1]*trans[1][2] - cpara[0][2]*trans[2][2]; + cpara[0][0]=norm(rem1, rem2, rem3);//cpara[0][0] = norm( rem1, rem2, rem3 ); + trans[0][0]=rem1 / cpara[0][0];//trans[0][0] = rem1 / cpara[0][0]; + trans[0][1]= rem2 / cpara[0][0];//trans[0][1] = rem2 / cpara[0][0]; + trans[0][2]= rem3 / cpara[0][0];//trans[0][2] = rem3 / cpara[0][0]; + + trans[1][3]=(Cpara[1][3] - cpara[1][2]*trans[2][3]) / cpara[1][1];//trans[1][3] = (Cpara[1][3] - cpara[1][2]*trans[2][3]) / cpara[1][1]; + trans[0][3]=(Cpara[0][3] - cpara[0][1]*trans[1][3]- cpara[0][2]*trans[2][3]) / cpara[0][0];//trans[0][3] = (Cpara[0][3] - cpara[0][1]*trans[1][3]- cpara[0][2]*trans[2][3]) / cpara[0][0]; + + for(int r = 0; r < 3; r++ ){ + for(int c = 0; c < 3; c++ ){ + cpara[r][c]/=cpara[2][2];//cpara[r][c] /= cpara[2][2]; + } + } + } + + + /*int arParamDisp( ARParam *param );*/ + public int paramDisp() + { + System.out.println("--------------------------------------");//printf("--------------------------------------\n"); + System.out.print("SIZE = "+xsize+", "+ysize);//printf("SIZE = %d, %d\n", param->xsize, param->ysize); + System.out.println("Distortion factor = "+dist_factor[0]+" "+dist_factor[1]+" "+dist_factor[2]+" "+dist_factor[3]);//printf("Distortion factor = %f %f %f %f\n", param->dist_factor[0],param->dist_factor[1], param->dist_factor[2], param->dist_factor[3] ); + for(int j = 0; j < 3; j++ ) {//for(j = 0; j < 3; j++ ) { + for(int i = 0; i < 4; i++ ){ + System.out.print(mat[j][i]+" ");//printf("%7.5f ", param->mat[j][i]); + } + System.out.println();// printf("\n"); + }//} + System.out.println("--------------------------------------");//printf("--------------------------------------\n"); + return 0; + } +// /*int arParamDecomp( ARParam *source, ARParam *icpara, double trans[3][4] );*/ +// private static int arParamDecomp( NyARParam source, NyARParam icpara, double[][] trans) +// { +// icpara.xsize = source.xsize;//icpara->xsize = source->xsize; +// icpara.ysize = source.ysize;//icpara->ysize = source->ysize; +// icpara.dist_factor[0] = source.dist_factor[0];//icpara->dist_factor[0] = source->dist_factor[0]; +// icpara.dist_factor[1] = source.dist_factor[1];// icpara->dist_factor[1] = source->dist_factor[1]; +// icpara.dist_factor[2] = source.dist_factor[2];//icpara->dist_factor[2] = source->dist_factor[2]; +// icpara.dist_factor[3] = source.dist_factor[3];//icpara->dist_factor[3] = source->dist_factor[3]; +// return arParamDecompMat(source.mat, icpara.mat, trans ); +// } + /** + * int arParamChangeSize( ARParam *source, int xsize, int ysize, ARParam *newparam ); + * 関数の代替関数 + * サイズプロパティをi_xsize,i_ysizeに変更します。 + * @param xsize + * @param ysize + * @param newparam + * @return + * + */ + public void changeSize(int i_xsize, int i_ysize) + { + double scale; + scale = (double)i_xsize / (double)(xsize);//scale = (double)xsize / (double)(source->xsize); + + for(int i = 0; i < 4; i++ ) { + mat[0][i]=mat[0][i]*scale;//newparam->mat[0][i] = source->mat[0][i] * scale; + mat[1][i]=mat[1][i]*scale;//newparam->mat[1][i] = source->mat[1][i] * scale; + mat[2][i]=mat[2][i];//newparam->mat[2][i] = source->mat[2][i]; + } + + dist_factor[0] = dist_factor[0] * scale;//newparam->dist_factor[0] = source->dist_factor[0] * scale; + dist_factor[1] = dist_factor[1] * scale;//newparam->dist_factor[1] = source->dist_factor[1] * scale; + dist_factor[2] = dist_factor[2] / (scale*scale);//newparam->dist_factor[2] = source->dist_factor[2] / (scale*scale); + dist_factor[3] = dist_factor[3];//newparam->dist_factor[3] = source->dist_factor[3]; + + xsize = i_xsize;//newparam->xsize = xsize; + ysize = i_ysize;//newparam->ysize = ysize; + } + /** + * int arParamIdeal2Observ( const double dist_factor[4], const double ix, const double iy,double *ox, double *oy ) + * 関数の代替関数 + * @param ix + * @param iy + * @param ox + * @param oy + */ + public void ideal2Observ(double ix,double iy,DoubleValue ox, DoubleValue oy) + { + double x, y, d; + + x = (ix - dist_factor[0]) * dist_factor[3]; + y = (iy - dist_factor[1]) * dist_factor[3]; + if( x == 0.0 && y == 0.0 ) { + ox.set(dist_factor[0]); + oy.set(dist_factor[1]); + }else{ + d = 1.0 - dist_factor[2]/100000000.0 * (x*x+y*y); + ox.set(x * d + dist_factor[0]); + oy.set(y * d + dist_factor[1]); + } + } + /*int arParamObserv2Ideal( const double dist_factor[4], const double ox, const double oy,double *ix, double *iy );*/ + public int observ2Ideal(double ox,double oy,DoubleValue ix,DoubleValue iy) + { + double z02, z0, p, q, z, px, py; + + px = ox - dist_factor[0]; + py = oy - dist_factor[1]; + p = dist_factor[2]/100000000.0; + z02 = px*px+ py*py; + q = z0 = Math.sqrt(px*px+ py*py); + + for(int i = 1; ; i++ ) { + if( z0 != 0.0 ) { + z = z0 - ((1.0 - p*z02)*z0 - q) / (1.0 - 3.0*p*z02); + px = px * z / z0; + py = py * z / z0; + }else { + px = 0.0; + py = 0.0; + break; + } + if( i == PD_LOOP ){ + break; + } + z02 = px*px+ py*py; + z0 = Math.sqrt(px*px+ py*py); + } + + ix.set(px / dist_factor[3] + dist_factor[0]); + iy.set(py / dist_factor[3] + dist_factor[1]); + return 0; + } + /** + * int arParamLoad( const char *filename, int num, ARParam *param, ...); + * i_streamの入力ストリームからi_num個の設定を読み込み、パラメタを配列にして返します。 + * @param filename + * @param num + * @param param + * @return + * 設定を格納した配列を返します。 + * @throws Exception + * i_num個の設定が読み出せない場合、JartkExceptionを発生します。 + */ + private static NyARParam[] arParamLoad(InputStream i_stream, int i_num) throws NyARException + { + try{ + int read_size=SIZE_OF_PARAM_SET*i_num; + byte[] buf=new byte[read_size]; + i_stream.read(buf); + //返却配列を確保 + NyARParam[] result=new NyARParam[i_num]; + + //バッファを加工 + ByteBuffer bb = ByteBuffer.wrap(buf); + bb.order(ByteOrder.BIG_ENDIAN); + + //固定回数パースして配列に格納 + for(int i=0;i + * + */ +package jp.nyatla.nyartoolkit.core; +/** + * ARMarkerInfoに相当するクラス。 + * スクエア情報を保持します。 + * + */ +public class NyARSquare{ + private NyARMarker marker; + public int area; + public double[] pos; + public double[][] line; //double[4][3] + public double[][] vertex;//double[4][2]; + public NyARSquare(NyARMarker i_marker,double[][] i_attached_line,double[][] i_attached_vertex) + { + //ARSquareは、ARMarkerを完全にラップするようにした。 + marker=i_marker; + area=i_marker.area; + pos =i_marker.pos; + line =i_attached_line; + vertex=i_attached_vertex; + } + public NyARMarker getMarker() + { + return marker; + } +} diff --git a/src/jp/nyatla/nyartoolkit/core/NyARTransMat.java b/src/jp/nyatla/nyartoolkit/core/NyARTransMat.java new file mode 100644 index 0000000..f42a0c4 --- /dev/null +++ b/src/jp/nyatla/nyartoolkit/core/NyARTransMat.java @@ -0,0 +1,817 @@ +/* + * PROJECT: NyARToolkit + * -------------------------------------------------------------------------------- + * This work is based on the original ARToolKit developed by + * Hirokazu Kato + * Mark Billinghurst + * HITLab, University of Washington, Seattle + * http://www.hitl.washington.edu/artoolkit/ + * + * The NyARToolkit is Java version ARToolkit class library. + * Copyright (C)2008 R.Iizuka + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this framework; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * For further information please contact. + * http://nyatla.jp/nyatoolkit/ + * + * + */ +package jp.nyatla.nyartoolkit.core; + + + +import jp.nyatla.nyartoolkit.NyARException; +import jp.nyatla.util.DoubleValue; + +public class NyARTransMat{ + private static final int AR_FITTING_TO_IDEAL=0;//#define AR_FITTING_TO_IDEAL 0 + private static final int AR_FITTING_TO_INPUT=1;//#define AR_FITTING_TO_INPUT 1 + private static final int arFittingMode =AR_FITTING_TO_INPUT; + + private static final int AR_GET_TRANS_MAT_MAX_LOOP_COUNT=5;//#define AR_GET_TRANS_MAT_MAX_LOOP_COUNT 5 + private static final double AR_GET_TRANS_MAT_MAX_FIT_ERROR=1.0;//#define AR_GET_TRANS_MAT_MAX_FIT_ERROR 1.0 + private final static int P_MAX=500;//#define P_MAX 500 + private double[][] pos3d=new double[P_MAX][3];//pos3d[P_MAX][3]; + private double[][] pos2d=new double[P_MAX][2];//pos2d[P_MAX][2]; + + private double[] center={0.0,0.0}; + private NyARParam param; + private double conv[][]; + private NyARMat result_mat=new NyARMat(3,4); + public NyARTransMat(NyARParam i_param) + { + conv=result_mat.getArray(); + param=i_param; + + } + public void setCenter(double i_x,double i_y) + { + center[0]=i_x; + center[1]=i_x; + } + public NyARMat getTransformationMatrix() + { + return result_mat; + } + /*double arGetTransMat( ARMarkerInfo *marker_info,double center[2], double width, double conv[3][4] )*/ + public double transMat( NyARSquare square,int i_direction, double width)throws NyARException + { + double[][] rot=new double[3][3]; + double[][] ppos2d=new double[4][2]; + double[][] ppos3d=new double[4][2]; + int dir; + double err=-1; + + + + + if( arGetInitRot( square,i_direction, rot ) < 0 ){ + return -1; + } + + dir = i_direction; + ppos2d[0][0] = square.vertex[(4-dir)%4][0]; + ppos2d[0][1] = square.vertex[(4-dir)%4][1]; + ppos2d[1][0] = square.vertex[(5-dir)%4][0]; + ppos2d[1][1] = square.vertex[(5-dir)%4][1]; + ppos2d[2][0] = square.vertex[(6-dir)%4][0]; + ppos2d[2][1] = square.vertex[(6-dir)%4][1]; + ppos2d[3][0] = square.vertex[(7-dir)%4][0]; + ppos2d[3][1] = square.vertex[(7-dir)%4][1]; + ppos3d[0][0] = center[0] - width/2.0; + ppos3d[0][1] = center[1] + width/2.0; + ppos3d[1][0] = center[0] + width/2.0; + ppos3d[1][1] = center[1] + width/2.0; + ppos3d[2][0] = center[0] + width/2.0; + ppos3d[2][1] = center[1] - width/2.0; + ppos3d[3][0] = center[0] - width/2.0; + ppos3d[3][1] = center[1] - width/2.0; + + + + + + for(int i=0;i pmax[0] ){ + pmax[0] = ppos3d[i][0]; + } + if( ppos3d[i][0] < pmin[0] ){ + pmin[0] = ppos3d[i][0]; + } + if( ppos3d[i][1] > pmax[1] ){ + pmax[1] = ppos3d[i][1]; + } + if( ppos3d[i][1] < pmin[1] ){ + pmin[1] = ppos3d[i][1]; + } + } + off[0] = -(pmax[0] + pmin[0]) / 2.0; + off[1] = -(pmax[1] + pmin[1]) / 2.0; + off[2] = -(pmax[2] + pmin[2]) / 2.0; + for(int i = 0; i < num; i++ ) { + pos3d[i][0] = ppos3d[i][0] + off[0]; + pos3d[i][1] = ppos3d[i][1] + off[1]; + + pos3d[i][2] = 0.0; + } + ret = arGetTransMatSub( rot, ppos2d, pos3d, num); + + conv[0][3] = conv[0][0]*off[0] + conv[0][1]*off[1] + conv[0][2]*off[2] + conv[0][3]; + conv[1][3] = conv[1][0]*off[0] + conv[1][1]*off[1] + conv[1][2]*off[2] + conv[1][3]; + conv[2][3] = conv[2][0]*off[0] + conv[2][1]*off[1] + conv[2][2]*off[2] + conv[2][3]; + + return ret; + } + /*static double arGetTransMatSub( double rot[3][3], double ppos2d[][2],double pos3d[][3], int num, double conv[3][4],double *dist_factor, double cpara[3][4] )*/ + private double arGetTransMatSub( double rot[][], double ppos2d[][],double pos3d[][], int num) throws NyARException + { + double cpara[][]=param.getMat(); + NyARMat mat_a,mat_b,mat_c,mat_d,mat_e,mat_f;//ARMat *mat_a, *mat_b, *mat_c, *mat_d, *mat_e, *mat_f; + double[] trans=new double[3];//double trans[3]; + double wx, wy, wz; + double ret; + int i, j; + mat_a = new NyARMat(num*2, 3 ); + mat_b = new NyARMat( 3, num*2 ); + mat_c = new NyARMat( num*2, 1 ); + mat_d = new NyARMat( 3, 3 ); + mat_e = new NyARMat( 3, 1 ); + mat_f = new NyARMat( 3, 1 ); + double[][] a_array=mat_a.getArray(); + double[][] b_array=mat_b.getArray(); + double[][] c_array=mat_c.getArray(); + double[][] f_array=mat_f.getArray(); + + if(arFittingMode == AR_FITTING_TO_INPUT ){ + DoubleValue a1=new DoubleValue(),a2=new DoubleValue(); + for( i = 0; i < num; i++ ) { + param.ideal2Observ(ppos2d[i][0], ppos2d[i][1],a1,a2);//arParamIdeal2Observ(dist_factor, ppos2d[i][0], ppos2d[i][1],&pos2d[i][0], &pos2d[i][1]); + pos2d[i][0]=a1.get(); + pos2d[i][1]=a2.get(); + } + }else{ + for( i = 0; i < num; i++ ) { + pos2d[i][0] = ppos2d[i][0]; + pos2d[i][1] = ppos2d[i][1]; + } + } + for( j = 0; j < num; j++ ) { + int x2=j*2; + wx = rot[0][0] * pos3d[j][0]+ rot[0][1] * pos3d[j][1]+ rot[0][2] * pos3d[j][2]; + wy = rot[1][0] * pos3d[j][0]+ rot[1][1] * pos3d[j][1]+ rot[1][2] * pos3d[j][2]; + wz = rot[2][0] * pos3d[j][0]+ rot[2][1] * pos3d[j][1]+ rot[2][2] * pos3d[j][2]; + a_array[x2 ][0]=b_array[0][x2]=cpara[0][0];//mat_a->m[j*6+0] = mat_b->m[num*0+j*2] = cpara[0][0]; + a_array[x2 ][1]=b_array[1][x2]=cpara[0][1];//mat_a->m[j*6+1] = mat_b->m[num*2+j*2] = cpara[0][1]; + a_array[x2 ][2]=b_array[2][x2]=cpara[0][2] - pos2d[j][0];//mat_a->m[j*6+2] = mat_b->m[num*4+j*2] = cpara[0][2] - pos2d[j][0]; + a_array[x2+1][0]=b_array[0][x2+1]=0.0;//mat_a->m[j*6+3] = mat_b->m[num*0+j*2+1] = 0.0; + a_array[x2+1][1]=b_array[1][x2+1]=cpara[1][1];//mat_a->m[j*6+4] = mat_b->m[num*2+j*2+1] = cpara[1][1]; + a_array[x2+1][2]=b_array[2][x2+1]=cpara[1][2] - pos2d[j][1];//mat_a->m[j*6+5] = mat_b->m[num*4+j*2+1] = cpara[1][2] - pos2d[j][1]; + c_array[x2][0] =wz * pos2d[j][0]- cpara[0][0]*wx - cpara[0][1]*wy - cpara[0][2]*wz;//mat_c->m[j*2+0] = wz * pos2d[j][0]- cpara[0][0]*wx - cpara[0][1]*wy - cpara[0][2]*wz; + c_array[x2+1][0]=wz * pos2d[j][1]- cpara[1][1]*wy - cpara[1][2]*wz;//mat_c->m[j*2+1] = wz * pos2d[j][1]- cpara[1][1]*wy - cpara[1][2]*wz; + } +// JartkException.trap("未チェックのパス");{ + NyARMat.matrixMul( mat_d, mat_b, mat_a ); + NyARMat.matrixMul( mat_e, mat_b, mat_c ); + NyARMat.matrixSelfInv(mat_d); + NyARMat.matrixMul( mat_f, mat_d, mat_e ); +// } + trans[0] = f_array[0][0];//trans[0] = mat_f->m[0]; + trans[1] = f_array[1][0]; + trans[2] = f_array[2][0];//trans[2] = mat_f->m[2]; + + ret = arModifyMatrix( rot, trans, pos3d, pos2d, num ); + for( j = 0; j < num; j++ ) { + int x2=j*2; + wx = rot[0][0] * pos3d[j][0]+ rot[0][1] * pos3d[j][1]+ rot[0][2] * pos3d[j][2]; + wy = rot[1][0] * pos3d[j][0]+ rot[1][1] * pos3d[j][1]+ rot[1][2] * pos3d[j][2]; + wz = rot[2][0] * pos3d[j][0]+ rot[2][1] * pos3d[j][1]+ rot[2][2] * pos3d[j][2]; + a_array[x2 ][0]=b_array[0][x2]=cpara[0][0];//mat_a->m[j*6+0] = mat_b->m[num*0+j*2] = cpara[0][0]; + a_array[x2 ][1]=b_array[1][x2]=cpara[0][1];//mat_a->m[j*6+1] = mat_b->m[num*2+j*2] = cpara[0][1]; + a_array[x2 ][2]=b_array[2][x2]=cpara[0][2] - pos2d[j][0];//mat_a->m[j*6+2] = mat_b->m[num*4+j*2] = cpara[0][2] - pos2d[j][0]; + a_array[x2+1][0]=b_array[0][x2+1]=0.0;//mat_a->m[j*6+3] = mat_b->m[num*0+j*2+1] = 0.0; + a_array[x2+1][1]=b_array[1][x2+1]=cpara[1][1];//mat_a->m[j*6+4] = mat_b->m[num*2+j*2+1] = cpara[1][1]; + a_array[x2+1][2]=b_array[2][x2+1]=cpara[1][2] - pos2d[j][1];//mat_a->m[j*6+5] = mat_b->m[num*4+j*2+1] = cpara[1][2] - pos2d[j][1]; + c_array[x2][0] =wz * pos2d[j][0]- cpara[0][0]*wx - cpara[0][1]*wy - cpara[0][2]*wz;//mat_c->m[j*2+0] = wz * pos2d[j][0]- cpara[0][0]*wx - cpara[0][1]*wy - cpara[0][2]*wz; + c_array[x2+1][0]=wz * pos2d[j][1]- cpara[1][1]*wy - cpara[1][2]*wz;//mat_c->m[j*2+1] = wz * pos2d[j][1]- cpara[1][1]*wy - cpara[1][2]*wz; + } +// JartkException.trap("未チェックのパス");{ + NyARMat.matrixMul( mat_d, mat_b, mat_a ); + NyARMat.matrixMul( mat_e, mat_b, mat_c ); + NyARMat.matrixSelfInv(mat_d); + NyARMat.matrixMul( mat_f, mat_d, mat_e ); +// } + trans[0] = f_array[0][0];//trans[0] = mat_f->m[0]; + trans[1] = f_array[1][0]; + trans[2] = f_array[2][0];//trans[2] = mat_f->m[2]; + + + ret = arModifyMatrix( rot, trans, pos3d, pos2d, num ); + for( j = 0; j < 3; j++ ) { + for( i = 0; i < 3; i++ ){ + conv[j][i] = rot[j][i]; + } + conv[j][3] = trans[j]; + } + return ret; + } + private double arModifyMatrix( double rot[][], double trans[],double vertex[][], double pos2d[][], int num ) + { + double factor; + DoubleValue a=new DoubleValue(),b=new DoubleValue(),c=new DoubleValue();//double a, b, c; + double a1, b1, c1; + double a2, b2, c2; + double ma = 0.0, mb = 0.0, mc = 0.0; + double[][] combo=new double[3][4]; + double hx, hy, h, x, y; + double err, minerr=0; + int t1, t2, t3; + int s1 = 0, s2 = 0, s3 = 0; + int i, j; + + arGetAngle( rot, a, b, c);//arGetAngle( rot, &a, &b, &c ); + a2 = a.get(); + b2 = b.get(); + c2 = c.get(); + factor = 10.0*Math.PI/180.0; + for( j = 0; j < 10; j++ ) { + minerr = 1000000000.0; + for(t1=-1;t1<=1;t1++) { + for(t2=-1;t2<=1;t2++) { + for(t3=-1;t3<=1;t3++) { + a1 = a2 + factor*t1; + b1 = b2 + factor*t2; + c1 = c2 + factor*t3; + arGetNewMatrix( a1, b1, c1, trans, null, combo ); + err = 0.0; + for( i = 0; i < num; i++ ) { + hx = combo[0][0] * vertex[i][0]+ combo[0][1] * vertex[i][1]+ combo[0][2] * vertex[i][2]+ combo[0][3]; + hy = combo[1][0] * vertex[i][0]+ combo[1][1] * vertex[i][1]+ combo[1][2] * vertex[i][2]+ combo[1][3]; + h = combo[2][0] * vertex[i][0]+ combo[2][1] * vertex[i][1]+ combo[2][2] * vertex[i][2]+ combo[2][3]; + x = hx / h; + y = hy / h; + err += (pos2d[i][0] - x) * (pos2d[i][0] - x)+ (pos2d[i][1] - y) * (pos2d[i][1] - y); + } + if( err < minerr ) { + minerr = err; + ma = a1; + mb = b1; + mc = c1; + s1 = t1; + s2 = t2; + s3 = t3; + } + } + } + } + if( s1 == 0 && s2 == 0 && s3 == 0 ){ + factor *= 0.5; + } + a2 = ma; + b2 = mb; + c2 = mc; + } + arGetRot( ma, mb, mc, rot ); + /* printf("factor = %10.5f\n", factor*180.0/MD_PI); */ + return minerr/num; + } + private int arGetNewMatrix( double a, double b, double c,double trans[], double trans2[][], double ret[][] ) + { + double cpara[][]=param.getMat(); + double[][] cpara2=new double[3][4]; + double[][] rot=new double[3][3]; + int i, j; + + arGetRot( a, b, c, rot ); + + if( trans2 != null ) { + for( j = 0; j < 3; j++ ) { + for( i = 0; i < 4; i++ ) { + cpara2[j][i] = cpara[j][0] * trans2[0][i]+ cpara[j][1] * trans2[1][i]+ cpara[j][2] * trans2[2][i]; + } + } + }else{ + for( j = 0; j < 3; j++ ) { + for( i = 0; i < 4; i++ ) { + cpara2[j][i] = cpara[j][i]; + } + } + } + for( j = 0; j < 3; j++ ) { + for( i = 0; i < 3; i++ ) { + ret[j][i] = cpara2[j][0] * rot[0][i]+ cpara2[j][1] * rot[1][i]+ cpara2[j][2] * rot[2][i]; + } + ret[j][3] = cpara2[j][0] * trans[0]+ cpara2[j][1] * trans[1]+ cpara2[j][2] * trans[2]+ cpara2[j][3]; + } + return(0); + } + private int arGetRot( double a, double b, double c, double rot[][] ) + { + double sina, sinb, sinc; + double cosa, cosb, cosc; + + sina = Math.sin(a); + cosa = Math.cos(a); + sinb = Math.sin(b); + cosb = Math.cos(b); + sinc = Math.sin(c); + cosc = Math.cos(c); + rot[0][0] = cosa*cosa*cosb*cosc+sina*sina*cosc+sina*cosa*cosb*sinc-sina*cosa*sinc; + rot[0][1] = -cosa*cosa*cosb*sinc-sina*sina*sinc+sina*cosa*cosb*cosc-sina*cosa*cosc; + rot[0][2] = cosa*sinb; + rot[1][0] = sina*cosa*cosb*cosc-sina*cosa*cosc+sina*sina*cosb*sinc+cosa*cosa*sinc; + rot[1][1] = -sina*cosa*cosb*sinc+sina*cosa*sinc+sina*sina*cosb*cosc+cosa*cosa*cosc; + rot[1][2] = sina*sinb; + rot[2][0] = -cosa*sinb*cosc-sina*sinb*sinc; + rot[2][1] = cosa*sinb*sinc-sina*sinb*cosc; + rot[2][2] = cosb; + + return 0; + } + /*int arGetAngle( double rot[3][3], double *wa, double *wb, double *wc )*/ + private int arGetAngle( double rot[][], DoubleValue wa, DoubleValue wb, DoubleValue wc ) + { + double a, b, c; + double sina, cosa, sinb, cosb, sinc, cosc; +// #if CHECK_CALC +// double w[3]; +// int i; +// for(i=0;i<3;i++) w[i] = rot[i][0]; +// for(i=0;i<3;i++) rot[i][0] = rot[i][1]; +// for(i=0;i<3;i++) rot[i][1] = rot[i][2]; +// for(i=0;i<3;i++) rot[i][2] = w[i]; +// #endif + if( rot[2][2] > 1.0 ) { + /* printf("cos(beta) = %f\n", rot[2][2]); */ + rot[2][2] = 1.0; + }else if( rot[2][2] < -1.0 ) { + /* printf("cos(beta) = %f\n", rot[2][2]); */ + rot[2][2] = -1.0; + } + cosb = rot[2][2]; + b = Math.acos( cosb ); + sinb = Math.sin( b ); + if( b >= 0.000001 || b <= -0.000001) { + cosa = rot[0][2] / sinb; + sina = rot[1][2] / sinb; + if( cosa > 1.0 ) { + /* printf("cos(alph) = %f\n", cosa); */ + cosa = 1.0; + sina = 0.0; + } + if( cosa < -1.0 ) { + /* printf("cos(alph) = %f\n", cosa); */ + cosa = -1.0; + sina = 0.0; + } + if( sina > 1.0 ) { + /* printf("sin(alph) = %f\n", sina); */ + sina = 1.0; + cosa = 0.0; + } + if( sina < -1.0 ) { + /* printf("sin(alph) = %f\n", sina); */ + sina = -1.0; + cosa = 0.0; + } + a = Math.acos( cosa ); + if( sina < 0 ){ + a = -a; + } + sinc = (rot[2][1]*rot[0][2]-rot[2][0]*rot[1][2])/ (rot[0][2]*rot[0][2]+rot[1][2]*rot[1][2]); + cosc = -(rot[0][2]*rot[2][0]+rot[1][2]*rot[2][1])/ (rot[0][2]*rot[0][2]+rot[1][2]*rot[1][2]); + if( cosc > 1.0 ) { + /* printf("cos(r) = %f\n", cosc); */ + cosc = 1.0; + sinc = 0.0; + } + if( cosc < -1.0 ) { + /* printf("cos(r) = %f\n", cosc); */ + cosc = -1.0; + sinc = 0.0; + } + if( sinc > 1.0 ) { + /* printf("sin(r) = %f\n", sinc); */ + sinc = 1.0; + cosc = 0.0; + } + if( sinc < -1.0 ) { + /* printf("sin(r) = %f\n", sinc); */ + sinc = -1.0; + cosc = 0.0; + } + c = Math.acos( cosc ); + if( sinc < 0 ){ + c = -c; + } + }else { + a = b = 0.0; + cosa = cosb = 1.0; + sina = sinb = 0.0; + cosc = rot[0][0]; + sinc = rot[1][0]; + if( cosc > 1.0 ) { + /* printf("cos(r) = %f\n", cosc); */ + cosc = 1.0; + sinc = 0.0; + } + if( cosc < -1.0 ) { + /* printf("cos(r) = %f\n", cosc); */ + cosc = -1.0; + sinc = 0.0; + } + if( sinc > 1.0 ) { + /* printf("sin(r) = %f\n", sinc); */ + sinc = 1.0; + cosc = 0.0; + } + if( sinc < -1.0 ) { + /* printf("sin(r) = %f\n", sinc); */ + sinc = -1.0; + cosc = 0.0; + } + c = Math.acos( cosc ); + if( sinc < 0 ) c = -c; + } + + wa.set(a);//*wa = a; + wb.set(b);//*wb = b; + wc.set(c);//*wc = c; + + return 0; + } + /*int arGetInitRot( ARMarkerInfo *marker_info, double cpara[3][4], double rot[3][3] )*/ + private int arGetInitRot( NyARSquare marker_info,int i_direction, double rot[][] ) throws NyARException + { + double cpara[][]=param.getMat(); + double[][] wdir=new double[3][3]; + double w, w1, w2, w3; + int dir; + int j; + + dir = i_direction; + + for( j = 0; j < 2; j++ ) { + w1 = marker_info.line[(4-dir+j)%4][0] * marker_info.line[(6-dir+j)%4][1]- marker_info.line[(6-dir+j)%4][0] * marker_info.line[(4-dir+j)%4][1]; + w2 = marker_info.line[(4-dir+j)%4][1] * marker_info.line[(6-dir+j)%4][2]- marker_info.line[(6-dir+j)%4][1] * marker_info.line[(4-dir+j)%4][2]; + w3 = marker_info.line[(4-dir+j)%4][2] * marker_info.line[(6-dir+j)%4][0]- marker_info.line[(6-dir+j)%4][2] * marker_info.line[(4-dir+j)%4][0]; + + wdir[j][0] = w1*(cpara[0][1]*cpara[1][2]-cpara[0][2]*cpara[1][1])+ w2*cpara[1][1]- w3*cpara[0][1]; + wdir[j][1] = -w1*cpara[0][0]*cpara[1][2]+ w3*cpara[0][0]; + wdir[j][2] = w1*cpara[0][0]*cpara[1][1]; + w = Math.sqrt( wdir[j][0]*wdir[j][0]+ wdir[j][1]*wdir[j][1]+ wdir[j][2]*wdir[j][2] ); + wdir[j][0] /= w; + wdir[j][1] /= w; + wdir[j][2] /= w; + } + + if( check_dir(wdir[0], marker_info.vertex[(4-dir)%4],marker_info.vertex[(5-dir)%4], cpara) < 0 ){ + return -1; + } + if( check_dir(wdir[1], marker_info.vertex[(7-dir)%4],marker_info.vertex[(4-dir)%4], cpara) < 0 ){ + return -1; + } + if( check_rotation(wdir) < 0 ){ + return -1; + } + + wdir[2][0] = wdir[0][1]*wdir[1][2] - wdir[0][2]*wdir[1][1]; + wdir[2][1] = wdir[0][2]*wdir[1][0] - wdir[0][0]*wdir[1][2]; + wdir[2][2] = wdir[0][0]*wdir[1][1] - wdir[0][1]*wdir[1][0]; + w = Math.sqrt( wdir[2][0]*wdir[2][0]+ wdir[2][1]*wdir[2][1]+ wdir[2][2]*wdir[2][2] ); + wdir[2][0] /= w; + wdir[2][1] /= w; + wdir[2][2] /= w; + /* + if( wdir[2][2] < 0 ) { + wdir[2][0] /= -w; + wdir[2][1] /= -w; + wdir[2][2] /= -w; + } + else { + wdir[2][0] /= w; + wdir[2][1] /= w; + wdir[2][2] /= w; + } + */ + + rot[0][0] = wdir[0][0]; + rot[1][0] = wdir[0][1]; + rot[2][0] = wdir[0][2]; + rot[0][1] = wdir[1][0]; + rot[1][1] = wdir[1][1]; + rot[2][1] = wdir[1][2]; + rot[0][2] = wdir[2][0]; + rot[1][2] = wdir[2][1]; + rot[2][2] = wdir[2][2]; + + return 0; + } + + /*static int check_dir( double dir[3], double st[2], double ed[2],double cpara[3][4] )*/ + private static int check_dir( double dir[], double st[], double ed[],double cpara[][]) throws NyARException + { + + double[][] world=new double[2][3]; + double[][] camera=new double[2][2]; + double[][] v=new double[2][2]; + double h; + int i, j; + // JartkException.trap("未チェックパス"); + NyARMat mat_a = new NyARMat( 3, 3 ); + double[][] a_array=mat_a.getArray(); + for(j=0;j<3;j++){ + for(i=0;i<3;i++){ + a_array[j][i]=cpara[j][i];//m[j*3+i] = cpara[j][i]; + } + } + // JartkException.trap("未チェックのパス"); + NyARMat.matrixSelfInv(mat_a); + world[0][0] = a_array[0][0]*st[0]*10.0+ a_array[0][1]*st[1]*10.0+ a_array[0][2]*10.0;//mat_a->m[0]*st[0]*10.0+ mat_a->m[1]*st[1]*10.0+ mat_a->m[2]*10.0; + world[0][1] = a_array[1][0]*st[0]*10.0+ a_array[1][1]*st[1]*10.0+ a_array[1][2]*10.0;//mat_a->m[3]*st[0]*10.0+ mat_a->m[4]*st[1]*10.0+ mat_a->m[5]*10.0; + world[0][2] = a_array[2][0]*st[0]*10.0+ a_array[2][1]*st[1]*10.0+ a_array[2][2]*10.0;//mat_a->m[6]*st[0]*10.0+ mat_a->m[7]*st[1]*10.0+ mat_a->m[8]*10.0; + world[1][0] = world[0][0] + dir[0]; + world[1][1] = world[0][1] + dir[1]; + world[1][2] = world[0][2] + dir[2]; + + for( i = 0; i < 2; i++ ) { + h = cpara[2][0] * world[i][0]+ cpara[2][1] * world[i][1]+ cpara[2][2] * world[i][2]; + if( h == 0.0 ){ + return -1; + } + camera[i][0] = (cpara[0][0] * world[i][0]+ cpara[0][1] * world[i][1]+ cpara[0][2] * world[i][2]) / h; + camera[i][1] = (cpara[1][0] * world[i][0]+ cpara[1][1] * world[i][1]+ cpara[1][2] * world[i][2]) / h; + } + + v[0][0] = ed[0] - st[0]; + v[0][1] = ed[1] - st[1]; + v[1][0] = camera[1][0] - camera[0][0]; + v[1][1] = camera[1][1] - camera[0][1]; + + if( v[0][0]*v[1][0] + v[0][1]*v[1][1] < 0 ) { + dir[0] = -dir[0]; + dir[1] = -dir[1]; + dir[2] = -dir[2]; + } + return 0; + } + + /*int check_rotation( double rot[2][3] )*/ + private static int check_rotation( double rot[][] ) + { + double[] v1=new double[3], v2=new double[3], v3=new double[3]; + double ca, cb, k1, k2, k3, k4; + double a, b, c, d; + double p1, q1, r1; + double p2, q2, r2; + double p3, q3, r3; + double p4, q4, r4; + double w; + double e1, e2, e3, e4; + int f; + + v1[0] = rot[0][0]; + v1[1] = rot[0][1]; + v1[2] = rot[0][2]; + v2[0] = rot[1][0]; + v2[1] = rot[1][1]; + v2[2] = rot[1][2]; + v3[0] = v1[1]*v2[2] - v1[2]*v2[1]; + v3[1] = v1[2]*v2[0] - v1[0]*v2[2]; + v3[2] = v1[0]*v2[1] - v1[1]*v2[0]; + w = Math.sqrt( v3[0]*v3[0]+v3[1]*v3[1]+v3[2]*v3[2] ); + if( w == 0.0 ) return -1; + v3[0] /= w; + v3[1] /= w; + v3[2] /= w; + + cb = v1[0]*v2[0] + v1[1]*v2[1] + v1[2]*v2[2]; + if( cb < 0 ) cb *= -1.0; + ca = (Math.sqrt(cb+1.0) + Math.sqrt(1.0-cb)) * 0.5; + + if( v3[1]*v1[0] - v1[1]*v3[0] != 0.0 ) { + f = 0; + } + else { + if( v3[2]*v1[0] - v1[2]*v3[0] != 0.0 ) { + w = v1[1]; v1[1] = v1[2]; v1[2] = w; + w = v3[1]; v3[1] = v3[2]; v3[2] = w; + f = 1; + } + else { + w = v1[0]; v1[0] = v1[2]; v1[2] = w; + w = v3[0]; v3[0] = v3[2]; v3[2] = w; + f = 2; + } + } + if( v3[1]*v1[0] - v1[1]*v3[0] == 0.0 ){ + return -1; + } + k1 = (v1[1]*v3[2] - v3[1]*v1[2]) / (v3[1]*v1[0] - v1[1]*v3[0]); + k2 = (v3[1] * ca) / (v3[1]*v1[0] - v1[1]*v3[0]); + k3 = (v1[0]*v3[2] - v3[0]*v1[2]) / (v3[0]*v1[1] - v1[0]*v3[1]); + k4 = (v3[0] * ca) / (v3[0]*v1[1] - v1[0]*v3[1]); + + a = k1*k1 + k3*k3 + 1; + b = k1*k2 + k3*k4; + c = k2*k2 + k4*k4 - 1; + + d = b*b - a*c; + if( d < 0 ){ + return -1; + } + r1 = (-b + Math.sqrt(d))/a; + p1 = k1*r1 + k2; + q1 = k3*r1 + k4; + r2 = (-b - Math.sqrt(d))/a; + p2 = k1*r2 + k2; + q2 = k3*r2 + k4; + if( f == 1 ) { + w = q1; q1 = r1; r1 = w; + w = q2; q2 = r2; r2 = w; + w = v1[1]; v1[1] = v1[2]; v1[2] = w; + w = v3[1]; v3[1] = v3[2]; v3[2] = w; + f = 0; + } + if( f == 2 ) { + w = p1; p1 = r1; r1 = w; + w = p2; p2 = r2; r2 = w; + w = v1[0]; v1[0] = v1[2]; v1[2] = w; + w = v3[0]; v3[0] = v3[2]; v3[2] = w; + f = 0; + } + + if( v3[1]*v2[0] - v2[1]*v3[0] != 0.0 ) { + f = 0; + } + else { + if( v3[2]*v2[0] - v2[2]*v3[0] != 0.0 ) { + w = v2[1]; v2[1] = v2[2]; v2[2] = w; + w = v3[1]; v3[1] = v3[2]; v3[2] = w; + f = 1; + } + else { + w = v2[0]; v2[0] = v2[2]; v2[2] = w; + w = v3[0]; v3[0] = v3[2]; v3[2] = w; + f = 2; + } + } + if( v3[1]*v2[0] - v2[1]*v3[0] == 0.0 ) return -1; + k1 = (v2[1]*v3[2] - v3[1]*v2[2]) / (v3[1]*v2[0] - v2[1]*v3[0]); + k2 = (v3[1] * ca) / (v3[1]*v2[0] - v2[1]*v3[0]); + k3 = (v2[0]*v3[2] - v3[0]*v2[2]) / (v3[0]*v2[1] - v2[0]*v3[1]); + k4 = (v3[0] * ca) / (v3[0]*v2[1] - v2[0]*v3[1]); + + a = k1*k1 + k3*k3 + 1; + b = k1*k2 + k3*k4; + c = k2*k2 + k4*k4 - 1; + + d = b*b - a*c; + if( d < 0 ){ + return -1; + } + r3 = (-b + Math.sqrt(d))/a; + p3 = k1*r3 + k2; + q3 = k3*r3 + k4; + r4 = (-b - Math.sqrt(d))/a; + p4 = k1*r4 + k2; + q4 = k3*r4 + k4; + if( f == 1 ) { + w = q3; q3 = r3; r3 = w; + w = q4; q4 = r4; r4 = w; + w = v2[1]; v2[1] = v2[2]; v2[2] = w; + w = v3[1]; v3[1] = v3[2]; v3[2] = w; + f = 0; + } + if( f == 2 ) { + w = p3; p3 = r3; r3 = w; + w = p4; p4 = r4; r4 = w; + w = v2[0]; v2[0] = v2[2]; v2[2] = w; + w = v3[0]; v3[0] = v3[2]; v3[2] = w; + f = 0; + } + + e1 = p1*p3+q1*q3+r1*r3; + if( e1 < 0 ){ + e1 = -e1; + } + e2 = p1*p4+q1*q4+r1*r4; + if( e2 < 0 ){ + e2 = -e2; + } + e3 = p2*p3+q2*q3+r2*r3; + if( e3 < 0 ){ + e3 = -e3; + } + e4 = p2*p4+q2*q4+r2*r4; + if( e4 < 0 ){ + e4 = -e4; + } + if( e1 < e2 ) { + if( e1 < e3 ) { + if( e1 < e4 ) { + rot[0][0] = p1; + rot[0][1] = q1; + rot[0][2] = r1; + rot[1][0] = p3; + rot[1][1] = q3; + rot[1][2] = r3; + } + else { + rot[0][0] = p2; + rot[0][1] = q2; + rot[0][2] = r2; + rot[1][0] = p4; + rot[1][1] = q4; + rot[1][2] = r4; + } + } + else { + if( e3 < e4 ) { + rot[0][0] = p2; + rot[0][1] = q2; + rot[0][2] = r2; + rot[1][0] = p3; + rot[1][1] = q3; + rot[1][2] = r3; + } + else { + rot[0][0] = p2; + rot[0][1] = q2; + rot[0][2] = r2; + rot[1][0] = p4; + rot[1][1] = q4; + rot[1][2] = r4; + } + } + } + else { + if( e2 < e3 ) { + if( e2 < e4 ) { + rot[0][0] = p1; + rot[0][1] = q1; + rot[0][2] = r1; + rot[1][0] = p4; + rot[1][1] = q4; + rot[1][2] = r4; + } + else { + rot[0][0] = p2; + rot[0][1] = q2; + rot[0][2] = r2; + rot[1][0] = p4; + rot[1][1] = q4; + rot[1][2] = r4; + } + } + else { + if( e3 < e4 ) { + rot[0][0] = p2; + rot[0][1] = q2; + rot[0][2] = r2; + rot[1][0] = p3; + rot[1][1] = q3; + rot[1][2] = r3; + } + else { + rot[0][0] = p2; + rot[0][1] = q2; + rot[0][2] = r2; + rot[1][0] = p4; + rot[1][1] = q4; + rot[1][2] = r4; + } + } + } + return 0; + } + +} diff --git a/src/jp/nyatla/nyartoolkit/core/NyARVec.java b/src/jp/nyatla/nyartoolkit/core/NyARVec.java new file mode 100644 index 0000000..53a22ff --- /dev/null +++ b/src/jp/nyatla/nyartoolkit/core/NyARVec.java @@ -0,0 +1,243 @@ +/* + * PROJECT: NyARToolkit + * -------------------------------------------------------------------------------- + * This work is based on the original ARToolKit developed by + * Hirokazu Kato + * Mark Billinghurst + * HITLab, University of Washington, Seattle + * http://www.hitl.washington.edu/artoolkit/ + * + * The NyARToolkit is Java version ARToolkit class library. + * Copyright (C)2008 R.Iizuka + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this framework; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * For further information please contact. + * http://nyatla.jp/nyatoolkit/ + * + * + */ +package jp.nyatla.nyartoolkit.core; + +import jp.nyatla.nyartoolkit.NyARException; + + + +public class NyARVec +{ + private int clm; + private NyARVec(double[] i_array) + { + v=i_array; + clm=v.length; + } + public NyARVec(int i_clm) + { + v=new double[i_clm]; + clm=i_clm; + } + private double[] v; + public int getClm() + { + return clm; + } + public double[] getArray() + { + return v; + } + public static NyARVec wrap(double[] i_array) + { + return new NyARVec(i_array); + } + /** + * arVecDispの代替品 + * @param v + * @return + */ + public int arVecDisp() throws NyARException + { + NyARException.trap("未チェックのパス"); + System.out.println(" === vector ("+clm+") ===\n");//printf(" === vector (%d) ===\n", v->clm); + System.out.print(" |");//printf(" |"); + for(int c = 0; c < clm; c++ ){//for( c = 0; c < v->clm; c++ ){ + System.out.print(" "+v[c]);//printf( " %10g", v->v[c] ); + } + System.out.println(" |");//printf(" |\n"); + System.out.println(" ===================");//printf(" ===================\n"); + return 0; + } + /** + * arVecInnerproduct関数の代替品 + * @param x + * @param y + * @param i_start + * 演算開始列(よくわからないけどarVecTridiagonalizeの呼び出し元でなんかしてる) + * @return + * @throws NyARException + */ + private static double vecInnerproduct(NyARVec x,NyARVec y,int i_start) throws NyARException + { + NyARException.trap("この関数は動作確認できていません。"); + double result = 0.0; +// double[] x_array=x.v;.getArray(); +// double[] y_array=y.getArray(); + + if(x.clm!= y.clm){ + throw new NyARException();//exit(); + } + for(int i = i_start; i < x.clm; i++ ) { + NyARException.trap("未チェックのパス"); + result += x.v[i] * y.v[i];//result += x->v[i] * y->v[i]; + } + return result; + } + /** + * double arVecHousehold関数の代替品 + * @param x + * @param i_start + * 演算開始列(よくわからないけどarVecTridiagonalizeの呼び出し元でなんかしてる) + * @return + * @throws NyARException + */ + private static double vecHousehold(NyARVec x,int i_start) throws NyARException + { + NyARException.trap("この関数は動作確認できていません。"); + double s, t; + s = Math.sqrt(vecInnerproduct(x,x,i_start)); +// double[] x_array=x.getArray(); + if( s != 0.0 ){ + NyARException.trap("未チェックのパス"); + if(x.v[i_start]< 0){ + s = -s; + } + NyARException.trap("未チェックのパス");{ + x.v[i_start]+=s;//x->v[0] += s; + t = 1 / Math.sqrt(x.v[i_start]* s);//t = 1 / sqrt(x->v[0] * s); + } + for(int i = i_start; i < x.clm; i++){ + NyARException.trap("未チェックのパス"); + x.v[i]*=t;//x->v[i] *= t; + } + } + return -s; + } + /** + * arVecTridiagonalize関数の代替品 + * a,d,e間で演算をしてる。何をどうしているかはさっぱりさっぱり + * @param a + * @param d + * @param e + * @param i_e_start + * 演算開始列(よくわからないけどarVecTridiagonalizeの呼び出し元でなんかしてる) + * @return + * @throws NyARException + */ + public static void vecTridiagonalize(NyARMat a, NyARVec d, NyARVec e,int i_e_start) throws NyARException + { + NyARVec vec,vec2; + double[][] a_array=a.getArray(); + double s, t, p, q; + int dim; + + if(a.getClm()!=a.getRow()){ + throw new NyARException(); + } + if(a.getClm() != d.clm){ + throw new NyARException(); + } + if(a.getClm() != e.clm){ + throw new NyARException(); + } + dim = a.getClm(); + + for(int k = 0; k < dim-2; k++ ){ + vec=a.getRowVec(k); +// double[] vec_array=vec.getArray(); + NyARException.trap("未チェックパス"); + d.v[k]=vec.v[k];//d.set(k,v.get(k)); //d->v[k] = v[k]; + + //wv1.clm = dim-k-1; + //wv1.v = &(v[k+1]); + NyARException.trap("未チェックパス"); + e.v[k+i_e_start]=vecHousehold(vec,k+1);//e->v[k] = arVecHousehold(&wv1); + if(e.v[k+i_e_start]== 0.0 ){ + continue; + } + + for(int i = k+1; i < dim; i++ ){ + s = 0.0; + for(int j = k+1; j < i; j++ ) { + NyARException.trap("未チェックのパス"); + s += a_array[j][i] * vec.v[j];//s += a.get(j*dim+i) * v.get(j);//s += a->m[j*dim+i] * v[j]; + } + for(int j = i; j < dim; j++ ) { + NyARException.trap("未チェックのパス"); + s += a_array[i][j] * vec.v[j];//s += a.get(i*dim+j) * v.get(j);//s += a->m[i*dim+j] * v[j]; + } + NyARException.trap("未チェックのパス"); + d.v[i]=s;//d->v[i] = s; + } + + + //wv1.clm = wv2.clm = dim-k-1; + //wv1.v = &(v[k+1]); + //wv2.v = &(d->v[k+1]); + vec=a.getRowVec(k); +// vec_array=vec.getArray(); + NyARException.trap("未チェックパス"); + t = vecInnerproduct(vec,d,k+1)/ 2; + for(int i = dim-1; i > k; i-- ) { + NyARException.trap("未チェックパス"); + p = vec.v[i];//p = v.get(i);//p = v[i]; + d.v[i]-=t*p;q=d.v[i];//q = d->v[i] -= t*p; + for(int j = i; j < dim; j++ ){ + NyARException.trap("未チェックパス"); + a_array[i][j]-=p*(d.v[j] + q*vec.v[j]);//a->m[i*dim+j] -= p*(d->v[j]) + q*v[j]; + } + } + } + + if( dim >= 2) { + d.v[dim-2]=a_array[dim-2][dim-2];//d->v[dim-2] = a->m[(dim-2)*dim+(dim-2)]; + e.v[dim-2+i_e_start]=a_array[dim-2][dim-1];//e->v[dim-2] = a->m[(dim-2)*dim+(dim-1)]; + } + + if( dim >= 1 ){ + d.v[dim-1]=a_array[dim-1][dim-1];//d->v[dim-1] = a->m[(dim-1)*dim+(dim-1)]; + } + + for(int k = dim-1; k >= 0; k--) { + vec=a.getRowVec(k);//v = a.getPointer(k*dim);//v = &(a->m[k*dim]); + if( k < dim-2 ) { + for(int i = k+1; i < dim; i++ ){ + //wv1.clm = wv2.clm = dim-k-1; + //wv1.v = &(v[k+1]); + //wv2.v = &(a->m[i*dim+k+1]); + vec2=a.getRowVec(i); + + t = vecInnerproduct(vec,vec2,k+1); + for(int j = k+1; j < dim; j++ ){ + NyARException.trap("未チェックパス"); + a_array[i][j]-=t*vec.v[j];//a.subValue(i*dim+j,t*v.get(j));//a->m[i*dim+j] -= t * v[j]; + } + } + } + for(int i = 0; i < dim; i++ ){ + vec.v[i]=0.0;//v.set(i,0.0);//v[i] = 0.0; + } + vec.v[k]=1;//v.set(k,1);//v[k] = 1; + } + } +} diff --git a/src/jp/nyatla/nyartoolkit/core/NyARVersion.java b/src/jp/nyatla/nyartoolkit/core/NyARVersion.java new file mode 100644 index 0000000..ef76572 --- /dev/null +++ b/src/jp/nyatla/nyartoolkit/core/NyARVersion.java @@ -0,0 +1,66 @@ +/* + * PROJECT: NyARToolkit + * -------------------------------------------------------------------------------- + * This work is based on the original ARToolKit developed by + * Hirokazu Kato + * Mark Billinghurst + * HITLab, University of Washington, Seattle + * http://www.hitl.washington.edu/artoolkit/ + * + * The NyARToolkit is Java version ARToolkit class library. + * Copyright (C)2008 R.Iizuka + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this framework; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * For further information please contact. + * http://nyatla.jp/nyatoolkit/ + * + * + */ +package jp.nyatla.nyartoolkit.core; + + +/** + * ARUint32 arGetVersion(char **versionStringRef); + * 関数の置き換え + */ +public class NyARVersion { + private static final int AR_HEADER_VERSION_MAJOR=2; //#define AR_HEADER_VERSION_MAJOR 2 + private static final int AR_HEADER_VERSION_MINOR=72;//#define AR_HEADER_VERSION_MINOR 72 + private static final int AR_HEADER_VERSION_TINY=0;//#define AR_HEADER_VERSION_TINY 0 + private static final int AR_HEADER_VERSION_BUILD=0;//#define AR_HEADER_VERSION_BUILD 0 + private static final String AR_HEADER_VERSION_STRING="2.72.0";//#define AR_HEADER_VERSION_STRING "2.72.0" + public static final boolean AR_HAVE_HEADER_VERSION_2=true;//#define AR_HAVE_HEADER_VERSION_2 + public static final boolean AR_HAVE_HEADER_VERSION_2_72=true;//#define AR_HAVE_HEADER_VERSION_2_72 + + public static String getARVersion() + { + return AR_HEADER_VERSION_STRING; + } + public static int getARVersionInt() + { + // Represent full version number (major, minor, tiny, build) in + // binary coded decimal. N.B: Integer division. + return (int)(0x10000000 * (AR_HEADER_VERSION_MAJOR / 10)) + + (int)(0x01000000 * (AR_HEADER_VERSION_MAJOR % 10)) + + (int)(0x00100000 * (AR_HEADER_VERSION_MINOR / 10)) + + (int)(0x00010000 * (AR_HEADER_VERSION_MINOR % 10)) + + (int)(0x00001000 * (AR_HEADER_VERSION_TINY / 10)) + + (int)(0x00000100 * (AR_HEADER_VERSION_TINY % 10)) + + (int)(0x00000010 * (AR_HEADER_VERSION_BUILD / 10)) + + (int)(0x00000001 * (AR_HEADER_VERSION_BUILD % 10)); + + } +} diff --git a/src/jp/nyatla/nyartoolkit/core/match/NyARMatchPatt.java b/src/jp/nyatla/nyartoolkit/core/match/NyARMatchPatt.java new file mode 100644 index 0000000..72b99e6 --- /dev/null +++ b/src/jp/nyatla/nyartoolkit/core/match/NyARMatchPatt.java @@ -0,0 +1,59 @@ +/* + * PROJECT: NyARToolkit + * -------------------------------------------------------------------------------- + * This work is based on the original ARToolKit developed by + * Hirokazu Kato + * Mark Billinghurst + * HITLab, University of Washington, Seattle + * http://www.hitl.washington.edu/artoolkit/ + * + * The NyARToolkit is Java version ARToolkit class library. + * Copyright (C)2008 R.Iizuka + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this framework; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * For further information please contact. + * http://nyatla.jp/nyatoolkit/ + * + * + */ +package jp.nyatla.nyartoolkit.core.match; + +import jp.nyatla.nyartoolkit.NyARException; +import jp.nyatla.nyartoolkit.core.NyARCode; +import jp.nyatla.nyartoolkit.core.NyARColorPatt; + + + + + +/** + * ARColorPattのマッチング計算をするインタフェイスです。 + * 基準Patに対して、計算済みのARCodeデータとの間で比較演算をします。 + * pattern_match関数を分解した3種類のパターン検出クラスを定義します。 + * + */ +interface ARMatchPatt{ + public double getConfidence(); + public int getDirection(); + public void evaluate(NyARCode i_code); + public void setPatt(NyARColorPatt i_target_patt) throws NyARException; +} + + + + + + diff --git a/src/jp/nyatla/nyartoolkit/core/match/NyARMatchPatt_BlackWhite.java b/src/jp/nyatla/nyartoolkit/core/match/NyARMatchPatt_BlackWhite.java new file mode 100644 index 0000000..0712557 --- /dev/null +++ b/src/jp/nyatla/nyartoolkit/core/match/NyARMatchPatt_BlackWhite.java @@ -0,0 +1,112 @@ +/* + * PROJECT: NyARToolkit + * -------------------------------------------------------------------------------- + * This work is based on the original ARToolKit developed by + * Hirokazu Kato + * Mark Billinghurst + * HITLab, University of Washington, Seattle + * http://www.hitl.washington.edu/artoolkit/ + * + * The NyARToolkit is Java version ARToolkit class library. + * Copyright (C)2008 R.Iizuka + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this framework; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * For further information please contact. + * http://nyatla.jp/nyatoolkit/ + * + * + */ +package jp.nyatla.nyartoolkit.core.match; + +import jp.nyatla.nyartoolkit.NyARException; +import jp.nyatla.nyartoolkit.core.*; + +/** + * AR_TEMPLATE_MATCHING_BWと同等のルールで + * マーカーを評価します。 + * + */ +public class NyARMatchPatt_BlackWhite implements ARMatchPatt{ + private double datapow; + private int width; + private int height; + private double cf=0; + private int dir=0; + private int ave; + private int[][][] input=new int[height][width][3]; + public void setPatt(NyARColorPatt i_target_patt) throws NyARException + { + width=i_target_patt.getWidth(); + height=i_target_patt.getHeight(); + short[][][] data=i_target_patt.getPatArray(); + input=new int[height][width][3]; + + int sum = ave = 0; + for(int i=0;i max ) { + max = sum2; + res = j; + } + } + dir=res; + cf=max; + } +} diff --git a/src/jp/nyatla/nyartoolkit/core/match/NyARMatchPatt_Color_WITHOUT_PCA.java b/src/jp/nyatla/nyartoolkit/core/match/NyARMatchPatt_Color_WITHOUT_PCA.java new file mode 100644 index 0000000..2d60521 --- /dev/null +++ b/src/jp/nyatla/nyartoolkit/core/match/NyARMatchPatt_Color_WITHOUT_PCA.java @@ -0,0 +1,118 @@ +/* + * PROJECT: NyARToolkit + * -------------------------------------------------------------------------------- + * This work is based on the original ARToolKit developed by + * Hirokazu Kato + * Mark Billinghurst + * HITLab, University of Washington, Seattle + * http://www.hitl.washington.edu/artoolkit/ + * + * The NyARToolkit is Java version ARToolkit class library. + * Copyright (C)2008 R.Iizuka + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this framework; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * For further information please contact. + * http://nyatla.jp/nyatoolkit/ + * + * + */ +package jp.nyatla.nyartoolkit.core.match; + +import jp.nyatla.nyartoolkit.NyARException; +import jp.nyatla.nyartoolkit.core.*; + +/** + * AR_TEMPLATE_MATCHING_COLORかつAR_MATCHING_WITHOUT_PCAと同等のルールで + * マーカーを評価します。 + * + */ +public class NyARMatchPatt_Color_WITHOUT_PCA implements ARMatchPatt{ + private int[][][] input; + private int ave; + private double datapow; + + private int width; + private int height; + private double cf=0; + private int dir=0; + public double getConfidence(){ + return cf; + } + public int getDirection(){ + return dir; + } + public void setPatt(NyARColorPatt i_target_patt) throws NyARException + { + width=i_target_patt.getWidth(); + height=i_target_patt.getHeight(); + short[][][] data=i_target_patt.getPatArray(); + + input=new int[height][width][3]; + int sum; + + sum = ave = 0; + for(int i=0;i max ){ + max = sum2; + res = j; + } + } + dir=res; + cf=max; + } +} \ No newline at end of file diff --git a/src/jp/nyatla/nyartoolkit/core/match/NyARMatchPatt_Color_WITH_PCA.java b/src/jp/nyatla/nyartoolkit/core/match/NyARMatchPatt_Color_WITH_PCA.java new file mode 100644 index 0000000..1fba3cc --- /dev/null +++ b/src/jp/nyatla/nyartoolkit/core/match/NyARMatchPatt_Color_WITH_PCA.java @@ -0,0 +1,146 @@ +/* + * PROJECT: NyARToolkit + * -------------------------------------------------------------------------------- + * This work is based on the original ARToolKit developed by + * Hirokazu Kato + * Mark Billinghurst + * HITLab, University of Washington, Seattle + * http://www.hitl.washington.edu/artoolkit/ + * + * The NyARToolkit is Java version ARToolkit class library. + * Copyright (C)2008 R.Iizuka + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this framework; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * For further information please contact. + * http://nyatla.jp/nyatoolkit/ + * + * + */ +package jp.nyatla.nyartoolkit.core.match; + +import jp.nyatla.nyartoolkit.NyARException; +import jp.nyatla.nyartoolkit.core.NyARCode; +import jp.nyatla.nyartoolkit.core.NyARColorPatt; + +/** + * AR_TEMPLATE_MATCHING_COLORかつAR_MATCHING_WITH_PCAと同等のルールで + * マーカーを評価します。 + * + */ +public class NyARMatchPatt_Color_WITH_PCA implements ARMatchPatt{ + private final int EVEC_MAX=10;//#define EVEC_MAX 10 + private int evec_dim;//static int evec_dim; + private int[][][] input; + private double[][][][] evec;//static double evec[EVEC_MAX][AR_PATT_SIZE_Y*AR_PATT_SIZE_X*3]; + private double[][] epat=new double[4][EVEC_MAX];//static double epat[AR_PATT_NUM_MAX][4][EVEC_MAX]; + private int ave; + private double datapow; + + private int width; + private int height; + private double cf=0; + private int dir=0;//向きか! + public double getConfidence(){ + return cf; + } + public int getDirection(){ + return dir; + } + public void setPatt(NyARColorPatt i_target_patt) throws NyARException + { + width=i_target_patt.getWidth(); + height=i_target_patt.getHeight(); + short[][][] data=i_target_patt.getPatArray(); + + input=new int[height][width][3]; + evec=new double[EVEC_MAX][height][width][3];//static double evec[EVEC_MAX][AR_PATT_SIZE_Y*AR_PATT_SIZE_X*3]; + int sum; + + sum = ave = 0; + for(int i=0;i + * + */ +package jp.nyatla.nyartoolkit.core.raster; + +public interface NyARRaster{ + //RGBの合計値を返す + public int getPixcelTotal(int i_x,int i_y); + public int getWidth(); + public int getHeight(); + public void pickRgbArray(int i_x,int i_y,int[] i_rgb); +} + + + diff --git a/src/jp/nyatla/nyartoolkit/core/raster/NyARRaster_BGRA.java b/src/jp/nyatla/nyartoolkit/core/raster/NyARRaster_BGRA.java new file mode 100644 index 0000000..81743a4 --- /dev/null +++ b/src/jp/nyatla/nyartoolkit/core/raster/NyARRaster_BGRA.java @@ -0,0 +1,70 @@ +/* + * PROJECT: NyARToolkit + * -------------------------------------------------------------------------------- + * This work is based on the original ARToolKit developed by + * Hirokazu Kato + * Mark Billinghurst + * HITLab, University of Washington, Seattle + * http://www.hitl.washington.edu/artoolkit/ + * + * The NyARToolkit is Java version ARToolkit class library. + * Copyright (C)2008 R.Iizuka + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this framework; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * For further information please contact. + * http://nyatla.jp/nyatoolkit/ + * + * + */ +package jp.nyatla.nyartoolkit.core.raster; + + +public class NyARRaster_BGRA implements NyARRaster +{ + private byte[] ref_buf; + int width; + int height; + public static NyARRaster_BGRA wrap(byte[] i_buffer,int i_width,int i_height) + { + NyARRaster_BGRA new_inst=new NyARRaster_BGRA(); + new_inst.ref_buf=i_buffer; + new_inst.width =i_width; + new_inst.height =i_height; + return new_inst; + } + //RGBの合計値を返す + public int getPixcelTotal(int i_x,int i_y) + { + int bp=(i_x+i_y*width)*4; + return (ref_buf[bp] & 0xff)+(ref_buf[bp+1] & 0xff)+(ref_buf[bp+2] & 0xff); + } + public int getWidth() + { + return width; + } + public int getHeight() + { + return height; + } + public void pickRgbArray(int i_x,int i_y,int[] i_rgb) + { + int bp=(i_x+i_y*width)*4; + i_rgb[0]=(ref_buf[bp+2] & 0xff);//R + i_rgb[1]=(ref_buf[bp+1] & 0xff);//G + i_rgb[2]=(ref_buf[bp+0] & 0xff);//B + } +} + diff --git a/src/jp/nyatla/nyartoolkit/core/raster/NyARRaster_Blank.java b/src/jp/nyatla/nyartoolkit/core/raster/NyARRaster_Blank.java new file mode 100644 index 0000000..0e268ce --- /dev/null +++ b/src/jp/nyatla/nyartoolkit/core/raster/NyARRaster_Blank.java @@ -0,0 +1,68 @@ +/* + * PROJECT: NyARToolkit + * -------------------------------------------------------------------------------- + * This work is based on the original ARToolKit developed by + * Hirokazu Kato + * Mark Billinghurst + * HITLab, University of Washington, Seattle + * http://www.hitl.washington.edu/artoolkit/ + * + * The NyARToolkit is Java version ARToolkit class library. + * Copyright (C)2008 R.Iizuka + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this framework; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * For further information please contact. + * http://nyatla.jp/nyatoolkit/ + * + * + */ +package jp.nyatla.nyartoolkit.core.raster; + + +/* + * 真っ黒の矩形を定義する。 + * + */ +public class NyARRaster_Blank implements NyARRaster +{ + int width; + int height; + public NyARRaster_Blank(int i_width,int i_height) + { + NyARRaster_BGRA new_inst=new NyARRaster_BGRA(); + new_inst.width =i_width; + new_inst.height =i_height; + } + //RGBの合計値を返す + public int getPixcelTotal(int i_x,int i_y) + { + return 0; + } + public int getWidth() + { + return width; + } + public int getHeight() + { + return height; + } + public void pickRgbArray(int i_x,int i_y,int[] i_rgb) + { + i_rgb[0]=0; + i_rgb[1]=0; + i_rgb[2]=0; + } +} diff --git a/src/jp/nyatla/nyartoolkit/core/raster/NyARRaster_RGB.java b/src/jp/nyatla/nyartoolkit/core/raster/NyARRaster_RGB.java new file mode 100644 index 0000000..c04621e --- /dev/null +++ b/src/jp/nyatla/nyartoolkit/core/raster/NyARRaster_RGB.java @@ -0,0 +1,70 @@ +/* + * PROJECT: NyARToolkit + * -------------------------------------------------------------------------------- + * This work is based on the original ARToolKit developed by + * Hirokazu Kato + * Mark Billinghurst + * HITLab, University of Washington, Seattle + * http://www.hitl.washington.edu/artoolkit/ + * + * The NyARToolkit is Java version ARToolkit class library. + * Copyright (C)2008 R.Iizuka + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this framework; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * For further information please contact. + * http://nyatla.jp/nyatoolkit/ + * + * + */ +package jp.nyatla.nyartoolkit.core.raster; + + +public class NyARRaster_RGB implements NyARRaster +{ + protected byte[] ref_buf; + protected int width; + protected int height; + public static NyARRaster_RGB wrap(byte[] i_buffer,int i_width,int i_height) + { + NyARRaster_RGB new_inst=new NyARRaster_RGB(); + new_inst.ref_buf=i_buffer; + new_inst.width =i_width; + new_inst.height =i_height; + return new_inst; + } + //RGBの合計値を返す + public int getPixcelTotal(int i_x,int i_y) + { + int bp=(i_x+i_y*width)*3; + return (ref_buf[bp] & 0xff)+(ref_buf[bp+1] & 0xff)+(ref_buf[bp+2] & 0xff); + } + public int getWidth() + { + return width; + } + public int getHeight() + { + return height; + } + public void pickRgbArray(int i_x,int i_y,int[] i_rgb) + { + int bp=(i_x+i_y*width)*3; + i_rgb[0]=(ref_buf[bp+0] & 0xff);//R + i_rgb[1]=(ref_buf[bp+1] & 0xff);//G + i_rgb[2]=(ref_buf[bp+2] & 0xff);//B + } +} + diff --git a/src/jp/nyatla/nyartoolkit/detector/NyARSingleDetectMarker.java b/src/jp/nyatla/nyartoolkit/detector/NyARSingleDetectMarker.java new file mode 100644 index 0000000..93104e0 --- /dev/null +++ b/src/jp/nyatla/nyartoolkit/detector/NyARSingleDetectMarker.java @@ -0,0 +1,175 @@ +/* + * PROJECT: NyARToolkit + * -------------------------------------------------------------------------------- + * This work is based on the original ARToolKit developed by + * Hirokazu Kato + * Mark Billinghurst + * HITLab, University of Washington, Seattle + * http://www.hitl.washington.edu/artoolkit/ + * + * The NyARToolkit is Java version ARToolkit class library. + * Copyright (C)2008 R.Iizuka + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this framework; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * For further information please contact. + * http://nyatla.jp/nyatoolkit/ + * + * + */ +package jp.nyatla.nyartoolkit.detector; + +import jp.nyatla.nyartoolkit.NyARException; +import jp.nyatla.nyartoolkit.core.*; +import jp.nyatla.nyartoolkit.core.match.NyARMatchPatt_Color_WITHOUT_PCA; +import jp.nyatla.nyartoolkit.core.raster.*; +/** + * 1個のマーカーに対する変換行列を計算するクラスです。 + * + */ +public class NyARSingleDetectMarker{ + private static final int AR_SQUARE_MAX=10; + private NyARParam param; + private NyARDetectSquare square; + private NyARCode code; + protected NyARTransMat transmat; + private double marker_width; + //検出結果の保存用 + private int detected_direction; + private double detected_confidence; + private NyARSquare detected_square; + public NyARSingleDetectMarker(NyARParam i_param,NyARCode i_code,double i_marker_width) + { + param=i_param; + //解析オブジェクトを作る + square=new NyARDetectSquare(AR_SQUARE_MAX,i_param); + transmat=new NyARTransMat(param); + //比較コードを保存 + code=i_code; + marker_width=i_marker_width; + + + } + /** + * i_imageにマーカー検出処理を実行して、結果を保持します。 + * @param dataPtr + * @param thresh + * @return + * マーカーが検出できたかを真偽値で返します。 + * @throws NyARException + */ + public boolean detectMarkerLite(NyARRaster i_image,int i_thresh) throws NyARException + { + detected_square=null; + //スクエアコードを探す + square.detectSquare(i_image, i_thresh); + int number_of_square=square.getSquareCount(); + //コードは見つかった? + if(number_of_square<1){ + return false; + } + + //コードの一致度を調べる準備 + NyARSquare[] squares=square.getSquareArray(); + //パターンホルダを作る + NyARColorPatt patt=new NyARColorPatt(code.getWidth(),code.getHeight()); + //評価基準になるパターンをイメージから切り出す + patt.pickFromRaster(i_image,squares[0].getMarker()); + + //パターンの評価オブジェクトを作る。 + NyARMatchPatt_Color_WITHOUT_PCA eva=new NyARMatchPatt_Color_WITHOUT_PCA(); + //パターンを評価器にセット + eva.setPatt(patt); + //コードと比較する + eva.evaluate(code); + int square_index=0; + int direction=eva.getDirection(); + double confidence=eva.getConfidence(); + for(int i=1;ic2){ + continue; + } + //もっと一致するマーカーがあったぽい + square_index=i; + direction=eva.getDirection(); + confidence=c2; + } + //マーカー情報を保存 + detected_square=squares[square_index]; + detected_direction=direction; + detected_confidence=confidence; + return true; + } + /** + * 変換行列を返します。直前に実行したdetectMarkerLiteが成功していないと使えません。 + * @param i_marker_width + * マーカーの大きさを指定します。 + * @return + * double[3][4]の変換行列を返します。 + * @throws NyARException + */ + public NyARMat getTransmationMatrix() throws NyARException + { + //一番一致したマーカーの位置とかその辺を計算 + transmat.transMat(detected_square,detected_direction,marker_width); + return transmat.getTransformationMatrix(); + } + public double getConfidence() + { + return detected_confidence; + } + public int getDirection() + { + return detected_direction; + } + + + + + + + +// public static class arUtil_c{ +// public static final int arFittingMode =Config.DEFAULT_FITTING_MODE; +// private static final int arImageProcMode =Config.DEFAULT_IMAGE_PROC_MODE; +// public static final int arTemplateMatchingMode =Config.DEFAULT_TEMPLATE_MATCHING_MODE; +// public static final int arMatchingPCAMode =Config.DEFAULT_MATCHING_PCA_MODE; + /*int arInitCparam( ARParam *param )*/ + + + +} + + + + + + + + + + + + + + + diff --git a/src/jp/nyatla/nyartoolkit/sample/RawFileTest.java b/src/jp/nyatla/nyartoolkit/sample/RawFileTest.java new file mode 100644 index 0000000..d251e11 --- /dev/null +++ b/src/jp/nyatla/nyartoolkit/sample/RawFileTest.java @@ -0,0 +1,95 @@ +/* + * PROJECT: NyARToolkit + * -------------------------------------------------------------------------------- + * + * The NyARToolkit is Java version ARToolkit class library. + * Copyright (C)2008 R.Iizuka + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this framework; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * For further information please contact. + * http://nyatla.jp/nyatoolkit/ + * + * + */ +package jp.nyatla.nyartoolkit.sample; + + +import java.io.*; +import java.util.*; + +import jp.nyatla.nyartoolkit.core.*; +import jp.nyatla.nyartoolkit.core.raster.*; +import jp.nyatla.nyartoolkit.detector.*; + +/** + * 320x240のBGRA32で記録されたRAWイメージから、1種類のパターンを認識し、 + * その変換行列を求めます。 + * + * @author R.iizuka + * + */ +public class RawFileTest { + private final String code_file ="../Data/patt.hiro"; + private final String data_file ="../Data/320x240ABGR.raw"; + private final String camera_file="../Data/camera_para.dat"; + public RawFileTest() + { + } + public void Test_arDetectMarkerLite() throws Exception + { + //AR用カメラパラメタファイルをロード + NyARParam ap =new NyARParam(); + ap.loadFromARFile(camera_file); + ap.changeSize(320,240); + + //AR用のパターンコードを読み出し + NyARCode code=new NyARCode(16,16); + code.LoadFromARFile(code_file); + + //試験イメージの読み出し(320x240 BGRAのRAWデータ) + File f=new File(data_file); + FileInputStream fs=new FileInputStream(data_file); + byte[] buf=new byte[(int)f.length()]; + fs.read(buf); + NyARRaster_BGRA ra=NyARRaster_BGRA.wrap(buf, 320, 240); + // Blank_Raster ra=new Blank_Raster(320, 240); + + //1パターンのみを追跡するクラスを作成 + NyARSingleDetectMarker ar=new NyARSingleDetectMarker(ap,code,80.0); + + //マーカーを検出 + Date d2=new Date(); + for(int i=0;i<100;i++){ + ar.detectMarkerLite(ra,100); + //変換行列を取得 + double[][] tm=ar.getTransmationMatrix().getArray(); + } + Date d=new Date(); + System.out.println(d.getTime()-d2.getTime()); + } + public static void main(String[] args) + { + + try{ + RawFileTest t=new RawFileTest(); + //t.Test_arGetVersion(); + t.Test_arDetectMarkerLite(); + }catch(Exception e){ + e.printStackTrace(); + } + } + +} diff --git a/src/jp/nyatla/util/BytePointer.java b/src/jp/nyatla/util/BytePointer.java new file mode 100644 index 0000000..1de1df9 --- /dev/null +++ b/src/jp/nyatla/util/BytePointer.java @@ -0,0 +1,56 @@ +package jp.nyatla.util; + +public class BytePointer { + private byte[] array_ref; //配列 + private int array_offset; //配列に対する基準値 + private int position; //array_offsetに対する現在位置 + public static BytePointer wrap(byte[] i_array_ref,int i_offset) + { + return new BytePointer(i_array_ref,i_offset); + } + public void set(byte i_value) + { + array_ref[array_offset+position]=i_value; + } + public void set(int i_rel_positon,byte i_value) + { + array_ref[array_offset+position+i_rel_positon]=i_value; + } + /** + * カレント位置の値を取得する + * @return + */ + public byte get() + { + return array_ref[array_offset+position]; + } + /** + * カレント位置から+i_slideの位置にある値を取得する。 + * @param i_step + * @return + */ + public byte get(int i_slide) + { + return array_ref[array_offset+position+i_slide]; + } + public void incPtr() + { + position++; + } + public void addPtr(int v) + { + position+=v; + } + private BytePointer(byte[] i_array_ref,int i_base_point) + { + array_offset =i_base_point; + array_ref =i_array_ref; + position =0; + } +// public BytePointer() +// { +// array_offset =0; +// array_ref =new int[1]; +// position =0; +// } +} diff --git a/src/jp/nyatla/util/DoublePointer.java b/src/jp/nyatla/util/DoublePointer.java new file mode 100644 index 0000000..223ab54 --- /dev/null +++ b/src/jp/nyatla/util/DoublePointer.java @@ -0,0 +1,106 @@ +package jp.nyatla.util; +/** + * double型ポインタのエミュレートクラス + * 対象のdouble配列を基点を基準に参照する。 + * @author atla + * + */ +public class DoublePointer +{ + private double[] array_ref; //配列 + private int array_offset; //配列に対する基準値 + private int position; //array_offsetに対する現在位置 + public static DoublePointer wrap(double[] i_array_ref,int i_offset) + { + return new DoublePointer(i_array_ref,i_offset); + } + public static DoublePointer wrap(DoublePointer i_inst) + { + return new DoublePointer(i_inst.array_ref,i_inst.getPtrArrayOffset()); + } + //現在位置からのサブシーケンスを返す。 + public DoublePointer slice(int i_offset) + { + return DoublePointer.wrap(array_ref,array_offset+position+i_offset); + } + public void set(double i_value) + { + array_ref[array_offset+position]=i_value; + } + public void set(int i_rel_positon,double i_value) + { + array_ref[array_offset+position+i_rel_positon]=i_value; + } + /** + * カレント位置の値を取得する + * @return + */ + public double get() + { + return array_ref[array_offset+position]; + } + /** + * カレント位置から+i_slideの位置にある値を取得する。 + * @param i_step + * @return + */ + public double get(int i_slide) + { + return array_ref[array_offset+position+i_slide]; + } + public void incPtr() + { + position++; + } + public void addPtr(int v) + { + position+=v; + } + public double[] array() + { + return array_ref; + } + public void subValue(double i_val) + { + array_ref[array_offset+position]-=i_val; + } + public void subValue(int i_step,double i_val) + { + array_ref[array_offset+position+i_step]-=i_val; + } + public void addValue(double i_val) + { + array_ref[array_offset+position]+=i_val; + } + public void addValue(int i_step,double i_val) + { + array_ref[array_offset+position+i_step]+=i_val; + } + + /** + * 現在位置のオフセット位置を返す。 + * @return + */ + public int getPtrArrayOffset() + { + return array_offset+position; + } + private DoublePointer(double[] i_array_ref,int i_base_point) + { + array_offset =i_base_point; + array_ref =i_array_ref; + position =0; + } + public DoublePointer(int i_length) + { + array_offset =0; + array_ref =new double[i_length]; + position =0; + } + public DoublePointer() + { + array_offset =0; + array_ref =new double[1]; + position =0; + } +} \ No newline at end of file diff --git a/src/jp/nyatla/util/DoubleValue.java b/src/jp/nyatla/util/DoubleValue.java new file mode 100644 index 0000000..83a26f1 --- /dev/null +++ b/src/jp/nyatla/util/DoubleValue.java @@ -0,0 +1,14 @@ +package jp.nyatla.util; + +public class DoubleValue { + private double v; + public void set(double i_v){ + v=i_v; + } + public double get(){ + return v; + } + public void add(double i_v){ + v+=i_v; + } +} \ No newline at end of file diff --git a/src/jp/nyatla/util/IntPointer.java b/src/jp/nyatla/util/IntPointer.java new file mode 100644 index 0000000..e655913 --- /dev/null +++ b/src/jp/nyatla/util/IntPointer.java @@ -0,0 +1,64 @@ +package jp.nyatla.util; + +public class IntPointer { + private int[] array_ref; //配列 + private int array_offset; //配列に対する基準値 + private int position; //array_offsetに対する現在位置 + public static IntPointer wrap(int[] i_array_ref,int i_offset) + { + return new IntPointer(i_array_ref,i_offset); + } + public void set(int i_value) + { + array_ref[array_offset+position]=i_value; + } + public void set(int i_rel_positon,int i_value) + { + array_ref[array_offset+position+i_rel_positon]=i_value; + } + /** + * カレント位置の値を取得する + * @return + */ + public int get() + { + return array_ref[array_offset+position]; + } + /** + * カレント位置から+i_slideの位置にある値を取得する。 + * @param i_step + * @return + */ + public int get(int i_slide) + { + return array_ref[array_offset+position+i_slide]; + } + public void incPtr() + { + position++; + } + public void addPtr(int v) + { + position+=v; + } + public void addValue(int i_val) + { + array_ref[array_offset+position]+=i_val; + } + public void addValue(int i_step,int i_val) + { + array_ref[array_offset+position+i_step]+=i_val; + } + private IntPointer(int[] i_array_ref,int i_base_point) + { + array_offset =i_base_point; + array_ref =i_array_ref; + position =0; + } + public IntPointer() + { + array_offset =0; + array_ref =new int[1]; + position =0; + } +} diff --git a/src/jp/nyatla/util/IntValue.java b/src/jp/nyatla/util/IntValue.java new file mode 100644 index 0000000..7743171 --- /dev/null +++ b/src/jp/nyatla/util/IntValue.java @@ -0,0 +1,17 @@ +package jp.nyatla.util; + +public class IntValue { + private int v; + public void set(int i_v){ + v=i_v; + } + public int get(){ + return v; + } + public void inc(){ + v++; + } + public void add(int i_v){ + v+=i_v; + } +} diff --git a/src/jp/nyatla/util/ShortPointer.java b/src/jp/nyatla/util/ShortPointer.java new file mode 100644 index 0000000..7c75a1a --- /dev/null +++ b/src/jp/nyatla/util/ShortPointer.java @@ -0,0 +1,60 @@ +package jp.nyatla.util; + +public class ShortPointer { + private short[] array_ref; //配列 + private int array_offset; //配列に対する基準値 + private int position; //array_offsetに対する現在位置 + public static ShortPointer wrap(short[] i_array_ref,int i_offset) + { + return new ShortPointer(i_array_ref,i_offset); + } + public static ShortPointer wrap(ShortPointer i_inst,int i_offset) + { + return new ShortPointer(i_inst.array_ref,i_inst.array_offset+i_inst.position+i_offset); + } + public void set(short i_value) + { + array_ref[array_offset+position]=i_value; + } + public void set(int i_rel_positon,short i_value) + { + array_ref[array_offset+position+i_rel_positon]=i_value; + } + /** + * カレント位置の値を取得する + * @return + */ + public short get() + { + return array_ref[array_offset+position]; + } + /** + * カレント位置から+i_slideの位置にある値を取得する。 + * @param i_step + * @return + */ + public short get(int i_slide) + { + return array_ref[array_offset+position+i_slide]; + } + public void incPtr() + { + position++; + } + public void addPtr(int v) + { + position+=v; + } + private ShortPointer(short[] i_array_ref,int i_base_point) + { + array_offset =i_base_point; + array_ref =i_array_ref; + position =0; + } +// public BytePointer() +// { +// array_offset =0; +// array_ref =new int[1]; +// position =0; +// } +} diff --git a/src/jp/nyatla/util/StringPointer.java b/src/jp/nyatla/util/StringPointer.java new file mode 100644 index 0000000..dc2244b --- /dev/null +++ b/src/jp/nyatla/util/StringPointer.java @@ -0,0 +1,25 @@ +package jp.nyatla.util; + +public class StringPointer { + private String[] array_ref;//配列 + private int array_offset; //配列に対する基準値 + private int position; //array_offsetに対する現在位置 + public void set(String i_value) + { + array_ref[array_offset+position]=i_value; + } + public String get() + { + return array_ref[array_offset+position]; + } + public String toString() + { + return get(); + } + public StringPointer() + { + array_ref=new String[1]; + array_offset=0; + position=0; + } +} diff --git a/src/jp/nyatla/util/StringValue.java b/src/jp/nyatla/util/StringValue.java new file mode 100644 index 0000000..47102c6 --- /dev/null +++ b/src/jp/nyatla/util/StringValue.java @@ -0,0 +1,25 @@ +package jp.nyatla.util; + +public class StringValue { + private String v; + public StringValue() + { + v=""; + } + public StringValue(String i_v) + { + v=i_v; + } + public void set(String i_v) + { + v=i_v; + } + public String get() + { + return v; + } + public String toString() + { + return v; + } +} -- 2.11.0