1 /*
  2  * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
  3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  4  *
  5  * This code is free software; you can redistribute it and/or modify it
  6  * under the terms of the GNU General Public License version 2 only, as
  7  * published by the Free Software Foundation.  Oracle designates this
  8  * particular file as subject to the "Classpath" exception as provided
  9  * by Oracle in the LICENSE file that accompanied this code.
 10  *
 11  * This code is distributed in the hope that it will be useful, but WITHOUT
 12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 14  * version 2 for more details (a copy is included in the LICENSE file that
 15  * accompanied this code).
 16  *
 17  * You should have received a copy of the GNU General Public License version
 18  * 2 along with this work; if not, write to the Free Software Foundation,
 19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 20  *
 21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 22  * or visit www.oracle.com if you need additional information or have any
 23  * questions.
 24  */
 25 package violajones.attic;
 26 
 27 
 28 import hat.buffer.F32Array2D;
 29 import violajones.XMLHaarCascadeModel;
 30 import violajones.ifaces.Cascade;
 31 import violajones.ifaces.ResultTable;
 32 import violajones.ifaces.ScaleTable;
 33 
 34 
 35 public class ReferenceJavaViolaJones extends CoreJavaViolaJones {
 36     static boolean isAFaceStage(
 37             float scale, float invArea,
 38             int x,
 39             int y,
 40             float vnorm,
 41             F32Array2D integral,
 42             Cascade.Stage stage,
 43             Cascade cascade) {
 44         float sumOfThisStage = 0;
 45         int startTreeIdx = stage.firstTreeId();
 46         int endTreeIdx = startTreeIdx + stage.treeCount();
 47         for (int treeIdx = startTreeIdx; treeIdx < endTreeIdx; treeIdx++) {
 48             Cascade.Tree tree = cascade.tree(treeIdx);
 49             Cascade.Feature feature = cascade.feature(tree.firstFeatureId());
 50             while (feature != null) {
 51                 float featureGradientSum = .0f;
 52 
 53                 for (int r = 0; r < 3; r++) {
 54                     Cascade.Feature.Rect rect = feature.rect(r);
 55 
 56                     float g = gradient(integral,
 57                             x + (int) (rect.x() * scale), y + (int) (rect.y() * scale),
 58                             (int) (rect.width() * scale), (int) (rect.height() * scale)) *
 59                             rect.weight();  // we assume weight is 0 for unused ;)
 60                     featureGradientSum += g;
 61                 }
 62                 if ((featureGradientSum * invArea) < (feature.threshold() * vnorm)) {//left
 63                     var left = feature.left();
 64                     if (left.hasValue()) {
 65                         sumOfThisStage += left.anon().value();
 66                         feature = null;
 67                     } else {
 68                         feature = cascade.feature(tree.firstFeatureId() + left.anon().featureId());
 69 
 70                     }
 71                 } else {//right
 72                     var right = feature.right();
 73                     if (right.hasValue()) {
 74                         sumOfThisStage += right.anon().value();
 75                         feature = null;
 76                     } else {
 77                         feature = cascade.feature(tree.firstFeatureId() + right.anon().featureId());
 78                     }
 79                 }
 80             }
 81         }
 82         return sumOfThisStage > stage.threshold();
 83     }
 84 
 85     static boolean isAFaceStageHaar(
 86 
 87             float scale, float invArea,
 88             int x,
 89             int y,
 90             float vnorm,
 91             F32Array2D integral,
 92             XMLHaarCascadeModel.Stage stage,
 93             XMLHaarCascadeModel haarCascade
 94     ) {
 95         float sumOfThisStage = 0;
 96 
 97         int startTreeIdx = stage.firstTreeId();
 98         int endTreeIdx = startTreeIdx + stage.treeCount();
 99         for (int treeIdx = startTreeIdx; treeIdx < endTreeIdx; treeIdx++) {
100             XMLHaarCascadeModel.Tree tree = haarCascade.trees.get(treeIdx);
101             //  Cascade.Tree tree = cascade.tree(treeIdx);
102 
103 
104             XMLHaarCascadeModel.Feature feature = haarCascade.features.get(tree.firstFeatureId);
105             while (feature != null) {
106                 float featureGradientSum = .0f;
107                 for (XMLHaarCascadeModel.Feature.Rect r : feature.rects) {
108                     if (r != null) {
109                         float g = gradient(integral,
110                                 x + (int) (r.x() * scale), y + (int) (r.y() * scale),
111                                 (int) (r.width() * scale), (int) (r.height() * scale)) *
112                                 r.weight();
113                         featureGradientSum += g;
114                     }
115                 }
116                 if ((featureGradientSum * invArea) < (feature.threshold() * vnorm)) {//left
117                     if (feature.left.hasValue()) {
118                         sumOfThisStage += feature.left.value();
119                         feature = null;
120                     } else {
121                         feature = haarCascade.features.get(tree.firstFeatureId() + feature.left.featureId());
122                     }
123                 } else {//right
124                     if (feature.right.hasValue()) {
125                         sumOfThisStage += feature.right.value();
126                         feature = null;
127                     } else {
128                         feature = haarCascade.features.get(tree.firstFeatureId() + feature.right.featureId());
129                         //  feature = tree.features.get(feature.right.featureId());
130                     }
131                 }
132             }
133 
134         }
135         return sumOfThisStage > stage.threshold;
136     }
137 
138     static void findFeatures(int gid,
139                              Cascade cascade,
140                              F32Array2D integral,
141                              F32Array2D integralSq,
142                              ScaleTable scaleTable,
143                              ResultTable resultTable
144 
145     ) {
146         if (gid < scaleTable.multiScaleAccumulativeRange()) {
147 
148             int scalc = 0;
149             ScaleTable.Scale scale = scaleTable.scale(scalc++);
150             for (;
151                  gid >= scale.accumGridSizeMax();
152                  scale = scaleTable.scale(scalc++))
153                 ;
154             int scaleGid = gid - scale.accumGridSizeMin();
155             int x = (int) ((scaleGid % scale.gridWidth()) * scale.scaledXInc());
156             int y = (int) ((scaleGid / scale.gridWidth()) * scale.scaledYInc());
157             float integralGradient = gradient(integral, x, y, scale.scaledFeatureWidth(), scale.scaledFeatureHeight()) * scale.invArea();
158             float integralSqGradient = gradient(integralSq, x, y, scale.scaledFeatureWidth(), scale.scaledFeatureHeight()) * scale.invArea();
159 
160             float vnorm = integralSqGradient - integralGradient * integralGradient;
161             vnorm = (vnorm > 1) ? (float) Math.sqrt(vnorm) : 1;
162             boolean stillLooksLikeAFace = true;
163             if (cascade instanceof XMLHaarCascadeModel haarCascade) {
164                 for (XMLHaarCascadeModel.Stage stage : haarCascade.stages) {
165                     if (!(stillLooksLikeAFace = isAFaceStageHaar(
166                             scale.scaleValue(), scale.invArea(), x, y, vnorm, integral, stage, haarCascade))) {
167                         break;
168                     }
169                 }
170             } else {
171                 int stageCount = cascade.stageCount();
172                 for (int stagec = 0; stagec < stageCount; stagec++) {
173                     Cascade.Stage stage = cascade.stage(stagec);
174                     if (!(stillLooksLikeAFace = isAFaceStage(
175                             scale.scaleValue(), scale.invArea(), x, y, vnorm, integral, stage, cascade))) {
176 
177                         break;
178                     }
179                 }
180             }
181             if (stillLooksLikeAFace) {
182                 int index = resultTable.atomicResultTableCount();
183                 index++;
184                 resultTable.atomicResultTableCount(index);
185                 System.out.println("face at " + x + "," + y);
186                 if (index < resultTable.length()) {
187                     ResultTable.Result result = resultTable.result(index);
188                     result.x(x);
189                     result.y(y);
190                     result.width(scale.scaledFeatureWidth());
191                     result.height(scale.scaledFeatureWidth());
192                 }
193             }
194         }
195     }
196 
197 }