[gimp-gap] fixes from test with gimp-2.9.2 DetailTracking fixes
- From: Wolfgang Hofer <wolfgangh src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gimp-gap] fixes from test with gimp-2.9.2 DetailTracking fixes
- Date: Sun, 27 Mar 2016 18:47:19 +0000 (UTC)
commit 52f57cdc840648d15cd534b0badd593cc07f3551
Author: Wolfgang Hofer <wolfgangh svn gnome org>
Date: Sun Mar 27 20:47:00 2016 +0200
fixes from test with gimp-2.9.2 DetailTracking fixes
ChangeLog | 41 ++-
gap/gap_detail_tracking_exec.c | 1312 +++++++++++++++++++++++++++++++++-------
gap/gap_detail_tracking_exec.h | 32 +-
gap/gap_filter_foreach.c | 7 +-
gap/gap_filter_pdb.c | 34 -
gap/gap_filter_pdb.h | 1 -
gap/gap_lib.c | 50 +-
gap/gap_locate2.c | 228 +++++---
8 files changed, 1325 insertions(+), 380 deletions(-)
---
diff --git a/ChangeLog b/ChangeLog
index f54d510..7fee62f 100755
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,4 +1,43 @@
-2015-19-19 Wolfgang Hofer <hof gimp org>
+2016-03-27 Wolfgang Hofer <hof gimp org>
+
+- Fixed issues from test with GIMP-2.9.2
+ Replaced explicite pdb call to "gimp_xcf_save" procedure by
+ by gimp_file_save that shall do the same (since .xcf extension is used)
+ Note that gimp-2.9.2 now performs checks for a valid drawale_id provided to the save procedure
+ (even in case saving to XCF that should ignore the drawable_id as gimp-2.8.x did)
+ that behavior caused GIMP-GAP automatic save to XCF fail ....
+ The implented fix now picks the active layer (or the layer on stackposition 0 in case there is no active
layer)
+ for the gimp_file_save call and makes GIMP-GAP automatic save now work both with gimp-2.8.x and 2.9.x
+ (but still fails in gimp-2.9.x when the image has no layer at all)
+
+
+
+- DetailTracking now provides the option to track more details
+ (up to 12 points are captured from the active path)
+ and the best matching one (or two) points are picked for logging to the XML file
+
+ DetailTracking configure dialog supports the new parameter numPointsSelect
+ that can be set to 1 (for simple movement x/y compensation)
+ or set to 2 (for movemnet, scale and rotation compensation)
+
+- Detail Tracking renders reference and tracked points as vectors object path
+ Hidden experimental feature to save tracked frames as sequence of frame images
+ (using the extension .XCF instead of .xml for the xml logging file triggers this feature)
+
+- DetailAlign filter extended for parsing additional XML attributes
+ recorded by DetailTracking.
+
+
+ * gap/gap_detail_align_exec.c
+ * gap/gap_detail_tracking_exec.c [.h]
+ * gap/gap_detail_tracking_main.c
+ * gap/gap_filter_foreach.c
+ * gap/gap_filter_pdb.c [.h]
+ * gap/gap_lib.c
+ * gap/gap_locate2.c
+
+
+2015-09-19 Wolfgang Hofer <hof gimp org>
Merged MovePath dialog resize fix from gap-2.8 branch to master.
diff --git a/gap/gap_detail_tracking_exec.c b/gap/gap_detail_tracking_exec.c
index c44725b..9caf4bf 100644
--- a/gap/gap_detail_tracking_exec.c
+++ b/gap/gap_detail_tracking_exec.c
@@ -42,6 +42,7 @@ extern int gap_debug;
#include "gap_base.h"
#include "gap_libgapbase.h"
+#include "gap_lib.h"
#include "gap_locate.h"
#include "gap_locate2.h"
#include "gap_colordiff.h"
@@ -62,6 +63,46 @@ extern int gap_debug;
#define DEFAULT_removeMidlayers TRUE
#define DEFAULT_bgLayerIsReference TRUE
+#define NUMBER_OF_COORDS 12
+
+static gdouble p_calculate_angle_in_degree(gint p1x, gint p1y, gint p2x, gint p2y);
+static gdouble p_calculate_scale_factor(gint p1x, gint p1y, gint p2x, gint p2y
+ , gint p3x, gint p3y, gint p4x, gint p4y);
+static gdouble p_calculateSqrDist(PixelCoords *coordA, PixelCoords *coordB);
+static gdouble p_getPixelCoordsQuality(PixelCoords *coords);
+static void p_capture_n_vector_points(gint32 imageId, PixelCoordsArray *pixelCoordsArray, gint
ncoordsToCapture, gchar *vectorsName);
+static void p_copy_src_to_dst_coords(PixelCoords *srcCoords, PixelCoords *dstCoords);
+static void p_copy_src_to_dst_coords_array(PixelCoordsArray *srcCoordsArray, PixelCoordsArray
*dstCoordsArray);
+static void p_locate_target(gint32 refLayerId, PixelCoords *refCoords
+ , gint32 targetLayerId, PixelCoords *targetCoords
+ , gint32 locateOffsetX, gint32 locateOffsetY
+ , FilterValues *valPtr);
+static void p_write_xml_header(FILE *l_fp, gboolean center, gint width, gint height, gint numFrames);
+static void p_write_xml_footer(FILE *l_fp);
+static gboolean p_log_to_file(const char *filename, const char *logString
+ , gint32 frameNr, gboolean center, gint width, gint height);
+static void p_coords_logging(gint32 frameNr, PixelCoords *currCoords, PixelCoords *currCoords2
+ , PixelCoords *startCoords, PixelCoords *startCoords2, FilterValues *valPtr
+ , gint32 imageId
+ );
+static void p_select_best_coords(gint32 frameNr, PixelCoordsArray *currCoordsArray
+ , PixelCoordsArray *startCoordsArray, FilterValues *valPtr
+ , gint32 *bestIdx1
+ , gint32 *bestIdx2
+ );
+static gint32 p_selective_coords_logging(gint32 frameNr
+ , PixelCoordsArray *currCoordsArray
+ , PixelCoordsArray *startCoordsArray
+ , FilterValues *valPtr
+ , gint32 imageId
+ );
+static gint32 p_parse_frame_nr_from_layer_name(gint32 layerId);
+static void p_get_frameHistInfo(FrameHistInfo *frameHistInfo);
+static void p_set_frameHistInfo(FrameHistInfo *frameHistInfo);
+static void p_set_n_vector_points(gint32 imageId, PixelCoordsArray *targetCoordsArray, gchar
*vectorsName
+ , gboolean setVisible, gboolean setGuides, gint32 guideIdx);
+
+
/* -----------------------------------
* p_calculate_angle_in_degree
* -----------------------------------
@@ -146,26 +187,99 @@ p_calculate_scale_factor(gint p1x, gint p1y, gint p2x, gint p2y
+/* ----------------------------
+ * p_calculateSqrDist
+ * ----------------------------
+ * returns the square distance between coordA and coordB
+ *
+ */
+static gdouble
+p_calculateSqrDist(PixelCoords *coordA, PixelCoords *coordB)
+{
+ gdouble lenX;
+ gdouble lenY;
+ gdouble sqrDist;
+
+ lenX = coordA->px - coordB->px;
+ lenY = coordA->py - coordB->py;
+
+ sqrDist = (lenX * lenX) + (lenY * lenY);
+ return sqrDist;
+
+} /* end p_calculateSqrDist */
+
+/* ----------------------------
+ * p_getPixelCoordsQuality
+ * ----------------------------
+ * log the best 2 coordinates to stdout
+ * or to move-path controlpoint XML file.
+ *
+ * weight depends on average colordiff (while locating the coordinate)
+ * and distance (the longer the better for good precision
+ * while calcualting rotation angle and scaling)
+ */
+static gdouble
+p_getPixelCoordsQuality(PixelCoords *coords)
+{
+ gdouble qaulity;
+ if (coords->valid)
+ {
+ qaulity = CLAMP((1.0 - coords->avgColorDiff), 0.0, 1.0);
+ }
+ else
+ {
+ qaulity = 0.0;
+ }
+ return qaulity;
+
+} /* end p_getPixelCoordsQuality */
+
+
+
+
/* ------------------------------------------
- * p_capture_2_vector_points
+ * p_capture_n_vector_points
* ------------------------------------------
- * capture the first 2 points of the 1st stroke in the active path vectors
+ * capture the first n coords of the 1st stroke in the named path vectors
+ * (or active path vectors when vectorsName is null or not found)
*/
static void
-p_capture_2_vector_points(gint32 imageId, PixelCoords *coordPtr, PixelCoords *coordPtr2) {
+p_capture_n_vector_points(gint32 imageId, PixelCoordsArray *pixelCoordsArray, gint ncoordsToCapture, gchar
*vectorsName)
+{
+ gint nCoords;
+ gint idx;
gint32 activeVectorsId;
gint32 gx1;
gint32 gy1;
- gint32 gx2;
- gint32 gy2;
+ PixelCoords *coordPtr;
gx1 = -1;
gy1 = -1;
- gx2 = -1;
- gy2 = -1;
+
+ nCoords = CLAMP(ncoordsToCapture, 0, MAX_PIXEL_COORDS_ARRAY);
+ pixelCoordsArray->numberOfCoords = 0;
+ for(idx = 0; idx < nCoords; idx++)
+ {
+ coordPtr = &pixelCoordsArray->pixCoord[idx];
+ coordPtr->valid = FALSE;
+ coordPtr->avgColorDiff = 1.0; /* worst for invalid coords */
+ }
+
+ activeVectorsId = -1;
+ if (vectorsName != NULL)
+ {
+ if (*vectorsName != '\0')
+ {
+ activeVectorsId = gimp_image_get_vectors_by_name(imageId, vectorsName);
+ }
+ }
+ if (activeVectorsId < 0)
+ {
+ activeVectorsId = gimp_image_get_active_vectors(imageId);
+ }
+
- activeVectorsId = gimp_image_get_active_vectors(imageId);
if(activeVectorsId >= 0)
{
gint num_strokes;
@@ -196,16 +310,39 @@ p_capture_2_vector_points(gint32 imageId, PixelCoords *coordPtr, PixelCoords *co
if (type == GIMP_VECTORS_STROKE_TYPE_BEZIER)
{
- if(num_points >= 6)
+ /* attempt to pick the first nCoords from the vectors path */
+ for(idx = 0; idx < nCoords; idx++)
{
- gx1 = points[0];
- gy1 = points[1];
- }
- if(num_points >= 12)
- {
- gx2 = points[6];
- gy2 = points[7];
+ /* for GIMP_VECTORS_STROKE_TYPE_BEZIER there are 6 points per coordinate
+ * where
+ * point[0] is the x coordinate of the 1st pathpoint, point[6] holds x coordinate of the 2nd
pathpoint
+ * point[1] is the y coordinate of the 1st pathpoint, point[7] holds y coordinate of the 2nd
pathpoint
+ * point[2] [3] [4] [5] contain other information that is not used here.
+ */
+ if(num_points > idx * 6)
+ {
+ gint idxOffset;
+
+ idxOffset = idx *6;
+ gx1 = points[0 + idxOffset];
+ gy1 = points[1 + idxOffset];
+ if((gx1 >= 0) && (gy1 >= 0))
+ {
+ coordPtr = &pixelCoordsArray->pixCoord[idx];
+ coordPtr->px = gx1;
+ coordPtr->py = gy1;
+ coordPtr->valid = TRUE;
+ coordPtr->avgColorDiff = 0.0; /* best for reference coords */
+ pixelCoordsArray->numberOfCoords++;
+ }
+ else
+ {
+ break; /* stop at the first invalid point */
+ }
+ }
+
}
+
}
if(points)
{
@@ -219,38 +356,24 @@ p_capture_2_vector_points(gint32 imageId, PixelCoords *coordPtr, PixelCoords *co
}
- coordPtr->valid = FALSE;
- coordPtr2->valid = FALSE;
-
- if((gx1 >= 0) && (gy1 >= 0))
+ if(gap_debug)
{
- if(gap_debug)
- {
- printf("\nPathPoints: x1=%d; y1=%d x2=%d; y2=%d\n"
- , gx1
- , gy1
- , gx2
- , gy2
+ printf("\np_capture_n_vector_points PathPoints: %d\n", pixelCoordsArray->numberOfCoords);
+ for(idx = 0; idx < pixelCoordsArray->numberOfCoords; idx++)
+ {
+ coordPtr = &pixelCoordsArray->pixCoord[idx];
+ printf("x[%d]=%d; y[%d]=%d\n"
+ , idx
+ , coordPtr->px
+ , idx
+ , coordPtr->py
);
- }
-
- coordPtr->px = gx1;
- coordPtr->py = gy1;
- coordPtr->valid = TRUE;
-
- if((gx2 != gx1) || (gy2 != gy1))
- {
- if ((gx2 >= 0) && (gy2 >= 0))
- {
- coordPtr2->px = gx2;
- coordPtr2->py = gy2;
- coordPtr2->valid = TRUE;
- }
- }
-
+ }
+ printf(" numberOfCoords:%d\n", pixelCoordsArray->numberOfCoords);
}
-} /* end p_capture_2_vector_points */
+
+} /* end p_capture_n_vector_points */
/* ------------------------------------
@@ -261,10 +384,35 @@ static void
p_copy_src_to_dst_coords(PixelCoords *srcCoords, PixelCoords *dstCoords)
{
dstCoords->valid = srcCoords->valid;
+ dstCoords->avgColorDiff = srcCoords->avgColorDiff;
dstCoords->px = srcCoords->px;
dstCoords->py = srcCoords->py;
}
+
+/* ------------------------------------
+ * p_copy_src_to_dst_coords_array
+ * ------------------------------------
+ */
+static void
+p_copy_src_to_dst_coords_array(PixelCoordsArray *srcCoordsArray, PixelCoordsArray *dstCoordsArray)
+{
+ gint idx;
+ gint numberOfCoords;
+
+ numberOfCoords = CLAMP(srcCoordsArray->numberOfCoords, 0, MAX_PIXEL_COORDS_ARRAY);
+ dstCoordsArray->numberOfCoords = numberOfCoords;
+
+ for(idx = 0; idx < numberOfCoords; idx++)
+ {
+ p_copy_src_to_dst_coords(&srcCoordsArray->pixCoord[idx]
+ ,&dstCoordsArray->pixCoord[idx]
+ );
+ }
+}
+
+
+
/* ------------------------------------
* p_locate_target
* ------------------------------------
@@ -273,7 +421,7 @@ static void
p_locate_target(gint32 refLayerId, PixelCoords *refCoords
, gint32 targetLayerId, PixelCoords *targetCoords
, gint32 locateOffsetX, gint32 locateOffsetY
- , FilterValues *valPtr, gint32 *lostTraceCount)
+ , FilterValues *valPtr)
{
gdouble colordiffLocate;
gint ref_offset_x;
@@ -291,6 +439,7 @@ p_locate_target(gint32 refLayerId, PixelCoords *refCoords
gimp_drawable_offsets (targetLayerId, &target_offset_x, &target_offset_y);
targetCoords->valid = FALSE;
+
refX = refCoords->px - ref_offset_x;
refY = refCoords->py - ref_offset_y;
colordiffLocate =
@@ -305,6 +454,7 @@ p_locate_target(gint32 refLayerId, PixelCoords *refCoords
, locateOffsetX
, locateOffsetY
);
+ targetCoords->avgColorDiff = colordiffLocate; /* 0.0 indicates the best. 1.0 the worst locating quality
of the target coordinate */
targetCoords->px = targetX + target_offset_x;
targetCoords->py = targetY + target_offset_y;
@@ -317,12 +467,8 @@ p_locate_target(gint32 refLayerId, PixelCoords *refCoords
*/
targetCoords->valid = TRUE;
}
- else
- {
- (*lostTraceCount) += 1;
- }
- if(gap_debug)
+ //if(gap_debug)
{
printf("p_locate_target: refX:%d refY:%d locateOffsetX:%d locateOffsetY:%d\n"
" targetX:%d targetY:%d targetCoords->px:%d py:%d avgColodiff:%.5f valid:%d\n"
@@ -334,7 +480,7 @@ p_locate_target(gint32 refLayerId, PixelCoords *refCoords
,(int)targetY
,(int)targetCoords->px
,(int)targetCoords->py
- ,(float)colordiffLocate
+ ,(float)targetCoords->avgColorDiff
, targetCoords->valid
);
}
@@ -609,6 +755,7 @@ p_coords_logging(gint32 frameNr, PixelCoords *currCoords, PixelCoords *currCoor
py = py1 + valPtr->offsY;
if ((valPtr->coordsRelToFrame1)
+ && (valPtr->numPointsSelect > 1)
&& (startCoords2->valid == TRUE))
{
px2 = startCoords2->px -px2;
@@ -617,6 +764,7 @@ p_coords_logging(gint32 frameNr, PixelCoords *currCoords, PixelCoords *currCoor
}
if((currCoords2->valid == TRUE)
+ && (valPtr->numPointsSelect > 1)
&& (startCoords2->valid == TRUE)
&& (startCoords->valid == TRUE))
{
@@ -663,7 +811,8 @@ p_coords_logging(gint32 frameNr, PixelCoords *currCoords, PixelCoords *currCoor
logString = NULL;
- if(currCoords2->valid == TRUE)
+ if((currCoords2->valid == TRUE)
+ && (valPtr->numPointsSelect > 1))
{
/* double point detail coordinate tracking (allows calculation of rotate and scale factors) */
gchar *scaleValueAsString;
@@ -676,7 +825,7 @@ p_coords_logging(gint32 frameNr, PixelCoords *currCoords, PixelCoords *currCoor
if(valPtr->enableScaling == TRUE)
{
- logString = g_strdup_printf(" <controlpoint px=\"%04d\" py=\"%04d\" rotation=\"%s\"
width_resize=\"%s\" height_resize=\"%s\" keyframe_abs=\"%d\" p1x=\"%04d\" p1y=\"%04d\" p2x=\"%04d\"
p2y=\"%04d\"/>"
+ logString = g_strdup_printf(" <controlpoint px=\"%04d\" py=\"%04d\" rotation=\"%s\"
width_resize=\"%s\" height_resize=\"%s\" keyframe_abs=\"%d\" p1x=\"%04d\" p1y=\"%04d\" p2x=\"%04d\"
p2y=\"%04d\" s1x=\"%04d\" s1y=\"%04d\" s2x=\"%04d\" s2y=\"%04d\"/>"
, px
, py
, rotValueAsString
@@ -687,11 +836,15 @@ p_coords_logging(gint32 frameNr, PixelCoords *currCoords, PixelCoords *currCoor
, currCoords->py
, currCoords2->px
, currCoords2->py
+ , startCoords->px
+ , startCoords->py
+ , startCoords2->px
+ , startCoords2->py
);
}
else
{
- logString = g_strdup_printf(" <controlpoint px=\"%04d\" py=\"%04d\" rotation=\"%s\"
keyframe_abs=\"%d\" p1x=\"%04d\" p1y=\"%04d\" p2x=\"%04d\" p2y=\"%04d\"/>"
+ logString = g_strdup_printf(" <controlpoint px=\"%04d\" py=\"%04d\" rotation=\"%s\"
keyframe_abs=\"%d\" p1x=\"%04d\" p1y=\"%04d\" p2x=\"%04d\" p2y=\"%04d\" s1x=\"%04d\" s1y=\"%04d\"
s2x=\"%04d\" s2y=\"%04d\"/>"
, px
, py
, rotValueAsString
@@ -700,6 +853,10 @@ p_coords_logging(gint32 frameNr, PixelCoords *currCoords, PixelCoords *currCoor
, currCoords->py
, currCoords2->px
, currCoords2->py
+ , startCoords->px
+ , startCoords->py
+ , startCoords2->px
+ , startCoords2->py
);
}
@@ -712,12 +869,15 @@ p_coords_logging(gint32 frameNr, PixelCoords *currCoords, PixelCoords *currCoor
/* single point detail coordinate tracking */
if (valPtr->offsRotate == 0.0)
{
- logString = g_strdup_printf(" <controlpoint px=\"%04d\" py=\"%04d\" keyframe_abs=\"%d\"
p1x=\"%04d\" p1y=\"%04d\"/>"
+ logString = g_strdup_printf(" <controlpoint px=\"%04d\" py=\"%04d\" keyframe_abs=\"%d\"
p1x=\"%04d\" p1y=\"%04d\" s1x=\"%04d\" s1y=\"%04d\"/>"
, px
, py
, frameNr
, currCoords->px
, currCoords->py
+
+ , startCoords->px
+ , startCoords->py
);
}
else
@@ -725,13 +885,15 @@ p_coords_logging(gint32 frameNr, PixelCoords *currCoords, PixelCoords *currCoor
rotValueAsString = gap_base_gdouble_to_ascii_string(valPtr->offsRotate, precision_digits);
precision_digits = 7;
- logString = g_strdup_printf(" <controlpoint px=\"%04d\" py=\"%04d\" rotation=\"%s\"
keyframe_abs=\"%d\" p1x=\"%04d\" p1y=\"%04d\"/>"
+ logString = g_strdup_printf(" <controlpoint px=\"%04d\" py=\"%04d\" rotation=\"%s\"
keyframe_abs=\"%d\" p1x=\"%04d\" p1y=\"%04d\" s1x=\"%04d\" s1y=\"%04d\"/>"
, px
, py
, rotValueAsString
, frameNr
, currCoords->px
, currCoords->py
+ , startCoords->px
+ , startCoords->py
);
g_free(rotValueAsString);
}
@@ -762,13 +924,363 @@ p_coords_logging(gint32 frameNr, PixelCoords *currCoords, PixelCoords *currCoor
} /* end p_coords_logging */
+/* ----------------------------
+ * p_select_best_coords
+ * ----------------------------
+ * pick the two best matching coordinates.
+ *
+ * weight depends on average colordiff (while locating the coordinate)
+ * and distance (the longer the better for good precision
+ * while calcualting rotation angle and scaling)
+ */
+static void
+p_select_best_coords(gint32 frameNr, PixelCoordsArray *currCoordsArray
+ , PixelCoordsArray *startCoordsArray, FilterValues *valPtr
+ , gint32 *bestIdx1
+ , gint32 *bestIdx2
+ )
+{
+#define MAX_AVG_LOOPS 4
+ gint idx1;
+ gint idx2;
+ gint soloPickIdx1;
+ gint pickIdx1;
+ gint pickIdx2;
+ gint numPoints;
+ gdouble sqrDistance;
+ gdouble quality; /* 1.0 is best 0.0 is worst quality */
+ gdouble soloQuality; /* 1.0 is best 0.0 is worst quality */
+ gdouble weight;
+ gdouble maxWeight;
+ gdouble maxSoloQuality;
+ gint32 sumOffsX;
+ gint32 sumOffsY;
+ gint32 numValidOffsets;
+ gdouble avgOffsX;
+ gdouble avgOffsY;
+ gint32 numValidOffsets1;
+ gdouble avgOffsX1;
+ gdouble avgOffsY1;
+ gint32 pixelMovementTolerance;
+ PixelCoords *currCoords;
+ PixelCoords *startCoords;
+ gint32 moveOffsetX;
+ gint32 moveOffsetY;
+ gint32 validPointsCount;
+
+ maxWeight = 0.0;
+ maxSoloQuality = 0.0;
+
+ soloPickIdx1 = 0;
+ pickIdx1 = 0;
+ pickIdx2 = 1;
+ numPoints = MIN(currCoordsArray->numberOfCoords , startCoordsArray->numberOfCoords);
+
+ /* calculate average offsets (movemnet of x and y axis)
+ * in the 2nd and further outer loops eliminate extreme values
+ */
+ pixelMovementTolerance = MAX(5, valPtr->targetMoveRadius / 8);
+ avgOffsX = 0.0;
+ avgOffsY = 0.0;
+ for(idx2 = 0; idx2 < MAX_AVG_LOOPS; idx2++)
+ {
+ sumOffsX = 0;
+ sumOffsY = 0;
+ numValidOffsets = 0;
+ validPointsCount = 0;
+ for(idx1 = 0; idx1 < numPoints; idx1++)
+ {
-/* -------------------------------
- * p_parse_frame_nr_from_layerId
- * -------------------------------
+ currCoords = &currCoordsArray->pixCoord[idx1];
+ startCoords = &startCoordsArray->pixCoord[idx1];
+ if (startCoords->valid)
+ {
+ if(currCoords->valid)
+ {
+ gboolean isPlausible;
+ moveOffsetX = currCoords->px - startCoords->px;
+ moveOffsetY = currCoords->py - startCoords->py;
+
+ validPointsCount++;
+ isPlausible = FALSE;
+ if (idx2 == 0)
+ {
+ /* in the 1st outer loop all valid coords are plausible
+ * for calculation of an an initial average value
+ */
+ isPlausible = TRUE;
+ }
+ else if ( (abs(moveOffsetX - avgOffsX) < pixelMovementTolerance)
+ && (abs(moveOffsetY - avgOffsY) < pixelMovementTolerance))
+ {
+ /* only coordinates that have similar movement vektors as the average
+ * are part of the final average calculation (that eliminates extreme values)
+ */
+ isPlausible = TRUE;
+ }
+
+ if (isPlausible)
+ {
+ sumOffsX += moveOffsetX;
+ sumOffsY += moveOffsetY;
+ numValidOffsets++;
+ }
+ }
+ }
+ }
+ if (numValidOffsets > 0)
+ {
+ avgOffsX = (gdouble)sumOffsX / (gdouble)numValidOffsets;
+ avgOffsY = (gdouble)sumOffsY / (gdouble)numValidOffsets;
+
+ if (idx2 == 0)
+ {
+ avgOffsX1 = avgOffsX;
+ avgOffsY1 = avgOffsY;
+ numValidOffsets1 = numValidOffsets;
+ }
+ else if (numValidOffsets > 2)
+ {
+ /* we have average X/Y values based on more than 2 points
+ * that shall be sufficient to detect points with extreme movement
+ */
+ break;
+ }
+ else if (idx2 == MAX_AVG_LOOPS -1)
+ {
+ /* no more attempts planed, therefore keep the current pixelMovementTolerance */
+ break;
+ }
+ else
+ {
+ /* the average offsets is based on just one or two points.
+ * which is not a good base for detection of extreme movement values.
+ * in this case try another loop with increased tolerance
+ * to get more points involved in the average calculation.
+ */
+ pixelMovementTolerance += (pixelMovementTolerance /2);
+ }
+ }
+ else
+ {
+ /* no points are available for average movement calculation */
+ if (validPointsCount > 1)
+ {
+ /* none of the valid points has movement vektor near the average value
+ * therefore try another loop with increased tolerance
+ */
+ pixelMovementTolerance += (pixelMovementTolerance /2);
+ }
+ else
+ {
+ break; /* escape from loop because less than 2 valid point are available */
+ }
+ }
+ }
+
+ currCoordsArray->numValidOffsets = numValidOffsets;
+ currCoordsArray->avgOffsX = avgOffsX;
+ currCoordsArray->avgOffsY = avgOffsY;
+
+ /* weight calculation loops to select best matching coordinates */
+ for(idx1 = 0; idx1 < numPoints; idx1++)
+ {
+
+ soloQuality = p_getPixelCoordsQuality(&currCoordsArray->pixCoord[idx1])
+ * p_getPixelCoordsQuality(&startCoordsArray->pixCoord[idx1]);
+ if (soloQuality <= 0.0)
+ {
+ continue;
+ }
+
+ if (numValidOffsets > 1)
+ {
+ /* there are 2 or more valid coords available
+ * in this case eliminate coords with extreme different movement vektors
+ */
+ currCoords = &currCoordsArray->pixCoord[idx1];
+ startCoords = &startCoordsArray->pixCoord[idx1];
+ moveOffsetX = currCoords->px - startCoords->px;
+ moveOffsetY = currCoords->py - startCoords->py;
+ if ( (abs(moveOffsetX - avgOffsX) > pixelMovementTolerance)
+ || (abs(moveOffsetY - avgOffsY) > pixelMovementTolerance))
+ {
+ continue;
+ }
+ }
+
+
+
+ if (soloQuality > maxSoloQuality)
+ {
+ maxSoloQuality = soloQuality;
+ soloPickIdx1 = idx1;
+ }
+
+ if (currCoordsArray->pixCoord[idx1].valid != TRUE)
+ {
+ continue; /* skip further weight checks when invalid points are involved */
+ }
+
+ for(idx2 = idx1 + 1; idx2 < numPoints; idx2++)
+ {
+ if (currCoordsArray->pixCoord[idx2].valid)
+ {
+ if (numValidOffsets > 1)
+ {
+ /* there are 2 or more valid coords available
+ * in this case eliminate coords with extreme different movement vektors
+ */
+ currCoords = &currCoordsArray->pixCoord[idx2];
+ startCoords = &startCoordsArray->pixCoord[idx2];
+ moveOffsetX = currCoords->px - startCoords->px;
+ moveOffsetY = currCoords->py - startCoords->py;
+ if ( (abs(moveOffsetX - avgOffsX) > pixelMovementTolerance)
+ || (abs(moveOffsetY - avgOffsY) > pixelMovementTolerance))
+ {
+ continue;
+ }
+ }
+
+
+
+ quality = soloQuality * p_getPixelCoordsQuality(&currCoordsArray->pixCoord[idx2])
+ * p_getPixelCoordsQuality(&startCoordsArray->pixCoord[idx2]);
+ sqrDistance = p_calculateSqrDist(&currCoordsArray->pixCoord[idx1],
&currCoordsArray->pixCoord[idx2]);
+
+ /* operate with the square distance for performance reason
+ * therfore also use square quality to compensate..
+ */
+ weight = sqrDistance * quality * quality;
+ if (weight > maxWeight)
+ {
+ maxWeight = weight;
+ pickIdx1 = idx1;
+ pickIdx2 = idx2;
+ }
+ }
+ }
+ }
+
+ if (maxWeight <= 0.0)
+ {
+ pickIdx1 = soloPickIdx1;
+ }
+
+
+ *bestIdx1 = pickIdx1;
+ *bestIdx2 = pickIdx2;
+
+ // if(gap_debug)
+ {
+ for(idx1 = 0; idx1 < numPoints; idx1++)
+ {
+ gdouble qp1;
+ gdouble qs1;
+ qp1 = p_getPixelCoordsQuality(&currCoordsArray->pixCoord[idx1]);
+ qs1 = p_getPixelCoordsQuality(&startCoordsArray->pixCoord[idx1]);
+
+ currCoords = &currCoordsArray->pixCoord[idx1];
+ startCoords = &startCoordsArray->pixCoord[idx1];
+ moveOffsetX = currCoords->px - startCoords->px;
+ moveOffsetY = currCoords->py - startCoords->py;
+
+ printf("p_select_best_coords [%d] curr[].px:%d curr[].py:%d curr[].quality:%.4f "
+ " start[].px:%d start[].py:%d start[].quality:%.4f mvX:%d mvY:%d"
+ , (int) idx1
+ , currCoordsArray->pixCoord[idx1].px
+ , currCoordsArray->pixCoord[idx1].py
+ , (float)qp1
+ , startCoordsArray->pixCoord[idx1].px
+ , startCoordsArray->pixCoord[idx1].py
+ , (float)qs1
+ , (int)moveOffsetX
+ , (int)moveOffsetY
+ );
+
+ if ((qp1 <= 0.0) || (qs1 <= 0.0))
+ {
+ printf("\n");
+ }
+ else if ( (abs(moveOffsetX - avgOffsX) > pixelMovementTolerance)
+ || (abs(moveOffsetY - avgOffsY) > pixelMovementTolerance))
+ {
+ printf("(Movement extremeValue > Tolerance: %d) \n"
+ ,(int)pixelMovementTolerance
+ );
+ }
+ else
+ {
+ printf("(Movement typical) \n");
+ }
+
+ }
+
+ printf("p_select_best_coords: frameNr:%d bestIdx1:%d bestIdx2:%d maxWeight:%.4f avgOffst1 X:%.4f Y:%.4f
numValidOffsets:%d avgOffst X:%.4f Y:%.4f\n"
+ , (int)frameNr
+ , (int)pickIdx1
+ , (int)pickIdx2
+ , (float)maxWeight
+ , (float)avgOffsX1
+ , (float)avgOffsY1
+ , (int)numValidOffsets
+ , (float)avgOffsX
+ , (float)avgOffsY
+ );
+ }
+
+} /* end p_select_best_coords */
+
+
+/* ----------------------------
+ * p_selective_coords_logging
+ * ----------------------------
+ * log the best 2 coordinates to stdout
+ * or to move-path controlpoint XML file.
+ *
+ * weight depends on average colordiff (while locating the coordinate)
+ * and distance (the longer the better for good precision
+ * while calcualting rotation angle and scaling)
*/
static gint32
-p_parse_frame_nr_from_layerId(gint32 layerId)
+p_selective_coords_logging(gint32 frameNr
+ , PixelCoordsArray *currCoordsArray
+ , PixelCoordsArray *startCoordsArray
+ , FilterValues *valPtr
+ , gint32 imageId
+ )
+{
+ gint32 bestIdx1;
+ gint32 bestIdx2;
+
+ p_select_best_coords(frameNr
+ , currCoordsArray
+ , startCoordsArray
+ , valPtr
+ , &bestIdx1
+ , &bestIdx2
+ );
+
+ p_coords_logging(frameNr
+ , &currCoordsArray->pixCoord[bestIdx1]
+ , &currCoordsArray->pixCoord[bestIdx2]
+ , &startCoordsArray->pixCoord[bestIdx1]
+ , &startCoordsArray->pixCoord[bestIdx2]
+ , valPtr
+ , imageId
+ );
+
+ return (bestIdx1);
+
+} /* end p_selective_coords_logging */
+
+
+/* --------------------------------
+ * p_parse_frame_nr_from_layer_name
+ * --------------------------------
+ */
+static gint32
+p_parse_frame_nr_from_layer_name(gint32 layerId)
{
char *layername;
gint32 frameNr;
@@ -798,7 +1310,7 @@ p_parse_frame_nr_from_layerId(gint32 layerId)
return (frameNr);
-} /* end p_parse_frame_nr_from_layerId */
+} /* end p_parse_frame_nr_from_layer_name */
/* -------------------------------
@@ -809,13 +1321,19 @@ static void
p_get_frameHistInfo(FrameHistInfo *frameHistInfo)
{
int l_len;
+ PixelCoords *startCoords;
+
frameHistInfo->workImageId = -1;
frameHistInfo->frameNr = 0;
- frameHistInfo->startCoords.valid = FALSE;
- frameHistInfo->startCoords.px = 0;
- frameHistInfo->startCoords.py = 0;
+ frameHistInfo->trackedFramesCount = 0;
frameHistInfo->lostTraceCount = 0;
+ frameHistInfo->startCoordsArray.numberOfCoords = 0;
+
+ startCoords = &frameHistInfo->startCoordsArray.pixCoord[0];
+ startCoords->valid = FALSE;
+ startCoords->px = 0;
+ startCoords->py = 0;
l_len = gimp_get_data_size (GAP_DETAIL_FRAME_HISTORY_INFO);
@@ -834,19 +1352,25 @@ p_get_frameHistInfo(FrameHistInfo *frameHistInfo)
gimp_get_data(GAP_DETAIL_FRAME_HISTORY_INFO, frameHistInfo);
- if(gap_debug)
+ //if(gap_debug)
{
+ PixelCoords *prevCoords;
+
+ startCoords = &frameHistInfo->startCoordsArray.pixCoord[0];
+ prevCoords = &frameHistInfo->prevCoordsArray.pixCoord[0];
+
printf("p_get_frameHistInfo: %s frameNr:%d px:%d py:%d valid:%d\n"
- " prevPx:%d prevPy:%d prevValid:%d lostTraceCount:%d\n"
+ " prevPx:%d prevPy:%d prevValid:%d lostTraceCount:%d
trackedFramesCount:%d\n"
, GAP_DETAIL_FRAME_HISTORY_INFO
, (int)frameHistInfo->frameNr
- , (int)frameHistInfo->startCoords.px
- , (int)frameHistInfo->startCoords.py
- , (int)frameHistInfo->startCoords.valid
- , (int)frameHistInfo->prevCoords.px
- , (int)frameHistInfo->prevCoords.py
- , (int)frameHistInfo->prevCoords.valid
+ , (int)startCoords->px
+ , (int)startCoords->py
+ , (int)startCoords->valid
+ , (int)prevCoords->px
+ , (int)prevCoords->py
+ , (int)prevCoords->valid
, (int)frameHistInfo->lostTraceCount
+ , (int)frameHistInfo->trackedFramesCount
);
}
@@ -866,15 +1390,21 @@ p_set_frameHistInfo(FrameHistInfo *frameHistInfo)
{
if(gap_debug)
{
+ PixelCoords *startCoords;
+ PixelCoords *prevCoords;
+
+ startCoords = &frameHistInfo->startCoordsArray.pixCoord[0];
+ prevCoords = &frameHistInfo->prevCoordsArray.pixCoord[0];
+
printf("p_SET_frameHistInfo: %s frameNr:%d px:%d py:%d valid:%d prevPx:%d prevPy:%d prevValid:%d\n\n"
, GAP_DETAIL_FRAME_HISTORY_INFO
, (int)frameHistInfo->frameNr
- , (int)frameHistInfo->startCoords.px
- , (int)frameHistInfo->startCoords.py
- , (int)frameHistInfo->startCoords.valid
- , (int)frameHistInfo->prevCoords.px
- , (int)frameHistInfo->prevCoords.py
- , (int)frameHistInfo->prevCoords.valid
+ , (int)startCoords->px
+ , (int)startCoords->py
+ , (int)startCoords->valid
+ , (int)prevCoords->px
+ , (int)prevCoords->py
+ , (int)prevCoords->valid
);
}
@@ -886,88 +1416,155 @@ p_set_frameHistInfo(FrameHistInfo *frameHistInfo)
/* -------------------------------
- * p_set_2_vector_points
+ * p_set_n_vector_points
* -------------------------------
- * remove all strokes from the active path vectors
- * and set a new stroke containing targetCoords (one or 2 depend on the valid flag)
- * For better visualisation set guide lines crossing at the first target coords.
+ * if the image already contains a vectors object with the specified vectorsName
+ * then remove all strokes from the vectors object and replace it with the
+ * points in the targetCoordsArray.
+ *
+ * in case there is no vectors object with the specified vectorsName create it and add it to the image.
+ *
+ * if setGuides is TRUE
+ * then set guide lines crossing at the target coords[guideIdx] for better visualisation.
+ * (note that path vectors will be not visible in case the path contains just one single point)
*/
static void
-p_set_2_vector_points(gint32 imageId, PixelCoords *targetCoords, PixelCoords *targetCoords2)
+p_set_n_vector_points(gint32 imageId, PixelCoordsArray *targetCoordsArray, gchar *vectorsName
+ , gboolean setVisible, gboolean setGuides, gint32 guideIdx)
{
- gint32 activeVectorsId;
+ gint32 vectorsId;
/* gint newStrokeId; */
+ gint32 showGuideIdx;
gdouble *points;
gint num_points;
+ gint l_idx;
gboolean closed;
GimpVectorsStrokeType type;
+ PixelCoords *targetCoords;
+
+
+ showGuideIdx = CLAMP(guideIdx, 0, targetCoordsArray->numberOfCoords -1);
+ targetCoords = &targetCoordsArray->pixCoord[showGuideIdx];
-
- gimp_image_add_hguide(imageId, targetCoords->py);
- gimp_image_add_vguide(imageId, targetCoords->px);
+ if(setGuides == TRUE)
+ {
+ gimp_image_add_hguide(imageId, targetCoords->py);
+ gimp_image_add_vguide(imageId, targetCoords->px);
+ }
- activeVectorsId = gimp_image_get_active_vectors(imageId);
- if(activeVectorsId >= 0)
+ //if(gap_debug)
+ if(setGuides)
{
- gint num_strokes;
- gint *strokes;
-
- strokes = gimp_vectors_get_strokes (activeVectorsId, &num_strokes);
- if(strokes)
+ printf("\np_set_n_vector_points vectorsName:%s\n numberOfCoords:%d guideIdx:%d showGuideIdx:%d\n"
+ , vectorsName
+ , (int)targetCoordsArray->numberOfCoords
+ , (int)guideIdx
+ , (int)showGuideIdx
+ );
+ for(l_idx = 0; l_idx < targetCoordsArray->numberOfCoords; l_idx++)
{
- if(num_strokes > 0)
+ gdouble pdx;
+ gdouble pdy;
+ targetCoords = &targetCoordsArray->pixCoord[l_idx];
+ if (targetCoords->valid)
{
- gint ii;
- for(ii=0; ii < num_strokes; ii++)
- {
- gimp_vectors_remove_stroke(activeVectorsId, strokes[ii]);
- }
+ pdx = targetCoords->px;
+ pdy = targetCoords->py;
}
- g_free(strokes);
-
+ else
+ {
+ pdx = 0;
+ pdy = 0;
+ }
+
+ printf("pdx[%d] : %.2f pdy[%d] : %.2f\n"
+ , l_idx
+ , (float)pdx
+ , l_idx
+ , (float)pdy
+ );
+
}
+ }
- if (targetCoords->valid)
+ vectorsId = gimp_image_get_vectors_by_name(imageId, vectorsName);
+ if(vectorsId >= 0)
+ {
+// gint num_strokes;
+// gint *strokes;
+//
+// strokes = gimp_vectors_get_strokes (vectorsId, &num_strokes);
+// if(strokes)
+// {
+// if(num_strokes > 0)
+// {
+// gint ii;
+// for(ii=0; ii < num_strokes; ii++)
+// {
+// gimp_vectors_remove_stroke(vectorsId, strokes[ii]);
+// }
+// }
+// g_free(strokes);
+//
+// }
+ gimp_image_remove_vectors(imageId, vectorsId);
+ }
+
+ /* create new vectors path */
+ vectorsId = gimp_vectors_new(imageId, vectorsName);
+
+
+ if(vectorsId >= 0)
+ {
+ num_points = 6 * targetCoordsArray->numberOfCoords;
+ points = g_new (gdouble, num_points);
+
+ for(l_idx = 0; l_idx < targetCoordsArray->numberOfCoords; l_idx++)
{
- closed = FALSE;
- num_points = 6;
- if (targetCoords2->valid)
+ gdouble pdx;
+ gdouble pdy;
+ gint offset;
+
+ offset = l_idx * 6;
+ targetCoords = &targetCoordsArray->pixCoord[l_idx];
+ if (targetCoords->valid)
{
- num_points = 12;
+ pdx = targetCoords->px;
+ pdy = targetCoords->py;
}
- points = g_new (gdouble, num_points);
- points[0] = targetCoords->px;
- points[1] = targetCoords->py;
- points[2] = targetCoords->px;
- points[3] = targetCoords->py;
- points[4] = targetCoords->px;
- points[5] = targetCoords->py;
- if(targetCoords2->valid)
+ else
{
- points[6] = targetCoords2->px;
- points[7] = targetCoords2->py;
- points[8] = targetCoords2->px;
- points[9] = targetCoords2->py;
- points[10] = targetCoords2->px;
- points[11] = targetCoords2->py;
+ pdx = 0;
+ pdy = 0;
}
+ points[0 + offset] = pdx;
+ points[1 + offset] = pdy;
+ points[2 + offset] = pdx;
+ points[3 + offset] = pdy;
+ points[4 + offset] = pdx;
+ points[5 + offset] = pdy;
+ }
- type = GIMP_VECTORS_STROKE_TYPE_BEZIER;
- /* newStrokeId = */ gimp_vectors_stroke_new_from_points (activeVectorsId
+
+ closed = FALSE;
+ type = GIMP_VECTORS_STROKE_TYPE_BEZIER;
+ /* newStrokeId = */ gimp_vectors_stroke_new_from_points (vectorsId
, type
, num_points
, points
, closed
);
- g_free(points);
- }
-
+ g_free(points);
+
+ gimp_image_insert_vectors(imageId, vectorsId, -1, 0);
+ gimp_vectors_set_visible(vectorsId, setVisible);
}
-} /* end p_set_2_vector_points */
+} /* end p_set_n_vector_points */
+
/* -----------------------------------
@@ -978,34 +1575,53 @@ p_set_2_vector_points(gint32 imageId, PixelCoords *targetCoords, PixelCoords *ta
* This image has one layer at the first snapshot
* and each further snapshot adds one layer on top of the layerstack.
*
- * The start is detected when the image has only one layer.
+ * The start is detected when frameHistInfo->trackedFramesCount == 0
+ * or whenever the imageId has changed ( frameHistInfo->workImageId )
+ * or whenever the image has exactly one layer. (typical at 1st snapshot or when the user has deleted other
layers)
+ *
* optionally the numer of layers can be limted
- * to 2 (or more) layers.
+ * to 2 (or 3) layers.
+ *
+ * Detail tracking output depends on the specified vlPtr->moveLogFile
+ * o) output coordinates offests and rotation information
+ * is written in XML format to the moveLogFile
+ * if filename is present and has not .xcf extension
+ * o) XML output is written to stdout
+ * if moveLogFile is empty or starts with the '-' character.
+ * o) is rendered as vectors path (with the name "TrackingPoints")
+ * and saved as frame image
+ * if the specified moveLogFile has .xcf extension
*/
gint32
gap_track_detail_on_top_layers(gint32 imageId, gboolean doProgress, FilterValues *valPtr)
{
+ gint l_idx;
gint l_nlayers;
gint32 *l_layers_list;
FrameHistInfo frameHistInfoData;
FrameHistInfo *frameHistInfo;
- PixelCoords currCoords;
- PixelCoords currCoords2;
- PixelCoords targetCoords;
- PixelCoords targetCoords2;
- gint32 locateOffsetX;
- gint32 locateOffsetY;
- gint32 locateOffsetX2;
- gint32 locateOffsetY2;
- gint32 lostTraceCount;
-
- if(gap_debug)
+ PixelCoordsArray currCoordsArray;
+ PixelCoordsArray targetCoordsArray;
+ gint32 locateOffsetX[MAX_PIXEL_COORDS_ARRAY];
+ gint32 locateOffsetY[MAX_PIXEL_COORDS_ARRAY];
+ gint32 successfulTracedPointsCount;
+ gint32 previousLostTraceCount;
+ gint32 currFrameNr;
+ gchar *l_extension;
+ gboolean isTrackingToFrameImage;
+ gint32 bestIdx1;
+
+
+
+
+ //if(gap_debug)
{
- printf("gap_track_detail_on_top_layers: START\n"
- " refShapeRadius:%d targetMoveRadius:%d locateColordiff:%.4f\n"
+ printf("\ngap_track_detail_on_top_layers: START\n"
+ " numPointsSelect:%d refShapeRadius:%d targetMoveRadius:%d locateColordiff:%.4f\n"
" coordsRelToFrame1:%d offsX:%d offsY:%d removeMidlayers:%d bgLayerIsReference:%d\n"
" moveLogFile:%s\n"
+ , (int)valPtr->numPointsSelect
, (int)valPtr->refShapeRadius
, (int)valPtr->targetMoveRadius
, (float)valPtr->loacteColodiffThreshold
@@ -1020,84 +1636,158 @@ gap_track_detail_on_top_layers(gint32 imageId, gboolean doProgress, FilterValues
frameHistInfo = &frameHistInfoData;
- currCoords.valid = FALSE;
- targetCoords.valid = FALSE;
+ successfulTracedPointsCount = 0;
+ previousLostTraceCount = 0;
+ bestIdx1 = 0;
+ currCoordsArray.numberOfCoords = 0;
+ currCoordsArray.numValidOffsets = 0;
+ targetCoordsArray.numValidOffsets = 0;
+ for (l_idx = 0; l_idx < MAX_PIXEL_COORDS_ARRAY; l_idx++)
+ {
+ currCoordsArray.pixCoord[l_idx].valid = FALSE;
+ targetCoordsArray.pixCoord[l_idx].valid = FALSE;
+ locateOffsetX[l_idx] = 0;
+ locateOffsetY[l_idx] = 0;
+ }
+
+ /* check if detail tracking output is logged to an XML file valPtr->moveLogFile
+ * or is written as vectors path and saved as frame image to an .XCF file (if extension .XCF is specified)
+ */
+ isTrackingToFrameImage = FALSE;
+ l_extension = NULL;
+ if (valPtr->moveLogFile[0] != '\0')
+ {
+ l_extension = gap_lib_alloc_extension(&valPtr->moveLogFile[0]);
+ }
+ if(l_extension != NULL)
+ {
+ if ((strcmp(".xcf", l_extension) == 0)
+ || (strcmp(".XCF", l_extension) == 0))
+ {
+ isTrackingToFrameImage = TRUE;
+ }
+ g_free(l_extension);
+ l_extension = NULL;
+ }
+
+
+ p_capture_n_vector_points(imageId, &currCoordsArray, NUMBER_OF_COORDS,
VECTORS_NAME_START_REFERENCE_POINTS);
+ if (currCoordsArray.numberOfCoords == 0)
+ {
+ //if(gap_debug)
+ {
+ printf("gap_track_detail_on_top_layers NO tracking possible because No vectors path was found\n");
+ }
+ return (imageId);
+ }
+
l_layers_list = gimp_image_get_layers(imageId, &l_nlayers);
if((l_layers_list != NULL)
&& (l_nlayers > 0))
{
gint32 topLayerId;
+ gint32 refLayerId;
topLayerId = l_layers_list[0];
- frameHistInfo->frameNr += 1;
+ refLayerId = l_layers_list[1];
+ if (valPtr->bgLayerIsReference == TRUE)
+ {
+ refLayerId = l_layers_list[l_nlayers -1];
+ }
+
+ /// frameHistInfo->frameNr += 1;
p_get_frameHistInfo(frameHistInfo);
- locateOffsetX = 0;
- locateOffsetY = 0;
- locateOffsetX2 = 0;
- locateOffsetY2 = 0;
- if (l_nlayers == 1)
- {
- p_capture_2_vector_points(imageId, &currCoords, &currCoords2);
+ if ((frameHistInfo->trackedFramesCount == 0)
+ || (frameHistInfo->workImageId != imageId)
+ || (l_nlayers == 1))
+ {
+ //if(gap_debug)
+ {
+ printf("(A) gap_track_detail_on_top_layers BEGIN tracking l_nlayers:%d\n"
+ ,(int)l_nlayers
+ );
+ }
+ /* start of detail tracking when no frame history available and whenever a new workImage was created */
frameHistInfo->lostTraceCount = 0;
- p_copy_src_to_dst_coords(&currCoords, &frameHistInfo->startCoords);
- p_copy_src_to_dst_coords(&currCoords2, &frameHistInfo->startCoords2);
- p_copy_src_to_dst_coords(&currCoords, &frameHistInfo->prevCoords);
- p_copy_src_to_dst_coords(&currCoords2, &frameHistInfo->prevCoords2);
+ frameHistInfo->trackedFramesCount = 0;
+ p_copy_src_to_dst_coords_array(&currCoordsArray, &frameHistInfo->startCoordsArray);
+ p_copy_src_to_dst_coords_array(&currCoordsArray, &frameHistInfo->prevCoordsArray);
+
+ /* create (or replace) reference vectors objects */
+ p_set_n_vector_points(imageId, &currCoordsArray, VECTORS_NAME_START_REFERENCE_POINTS, FALSE, FALSE, 0);
+ p_set_n_vector_points(imageId, &currCoordsArray, VECTORS_NAME_REFERENCE_POINTS, TRUE, FALSE, 0);
+
frameHistInfo->frameNr = 1;
- p_coords_logging(frameHistInfo->frameNr
- , &currCoords
- , &currCoords2
- , &frameHistInfo->startCoords
- , &frameHistInfo->startCoords2
+ frameHistInfo->workImageId = imageId;
+ if (isTrackingToFrameImage != TRUE)
+ {
+ bestIdx1 = p_selective_coords_logging(frameHistInfo->frameNr
+ , &currCoordsArray
+ , &frameHistInfo->startCoordsArray
, valPtr
, imageId
);
+ }
}
else
{
- gint32 refLayerId;
- refLayerId = l_layers_list[1];
- if (valPtr->bgLayerIsReference == TRUE)
+ //if(gap_debug)
{
- refLayerId = l_layers_list[l_nlayers -1];
- }
+ printf("(B) gap_track_detail_on_top_layers CONTINUE tracking l_nlayers:%d\n"
+ ,(int)l_nlayers
+ );
+ }
+
- if(frameHistInfo->startCoords.valid != TRUE)
+ /* (re)inital capture vector points if the start coords of first processed frame are not valid
+ * TODO detecting by valid startCoordsArray [0] is no longer sufficient
+ * for now re-init is hercoded disabled (not sure if that is needed ...)
+ */
+ if(FALSE) //// if (frameHistInfo->startCoordsArray.pixCoord[0].valid != TRUE)
{
- p_capture_2_vector_points(imageId, &currCoords, &currCoords2);
+ p_capture_n_vector_points(imageId, &currCoordsArray, NUMBER_OF_COORDS,
VECTORS_NAME_START_REFERENCE_POINTS);
frameHistInfo->lostTraceCount = 0;
- p_copy_src_to_dst_coords(&currCoords, &frameHistInfo->startCoords);
- p_copy_src_to_dst_coords(&currCoords2, &frameHistInfo->startCoords2);
- p_copy_src_to_dst_coords(&currCoords, &frameHistInfo->prevCoords);
- p_copy_src_to_dst_coords(&currCoords2, &frameHistInfo->prevCoords2);
-
- frameHistInfo->frameNr = p_parse_frame_nr_from_layerId(refLayerId);
- p_coords_logging(frameHistInfo->frameNr
- , &currCoords
- , &currCoords2
- , &frameHistInfo->startCoords
- , &frameHistInfo->startCoords2
+ p_copy_src_to_dst_coords_array(&currCoordsArray, &frameHistInfo->startCoordsArray);
+ p_copy_src_to_dst_coords_array(&currCoordsArray, &frameHistInfo->prevCoordsArray);
+
+ frameHistInfo->frameNr = p_parse_frame_nr_from_layer_name(refLayerId);
+ if (isTrackingToFrameImage != TRUE)
+ {
+ bestIdx1 = p_selective_coords_logging(frameHistInfo->frameNr
+ , &currCoordsArray
+ , &frameHistInfo->startCoordsArray
, valPtr
, imageId
);
+ }
}
else if (valPtr->bgLayerIsReference == TRUE)
{
+ gint32 sumOffsX;
+ gint32 sumOffsY;
+ gint32 numValidOffsets;
+
+ //if(gap_debug)
+ {
+ printf("(Bb) gap_track_detail_on_top_layers CONTINUE BG is referenence l_nlayers:%d\n"
+ ,(int)l_nlayers
+ );
+ }
+
/* when all trackings refere to initial BG layer (that is always kept as reference
- * for all further frames), we do not capture currCoords
+ * for all further frames), we do not capture currCoordsArray
* but copy the initial start values.
*/
- p_copy_src_to_dst_coords(&frameHistInfo->startCoords, &currCoords);
- p_copy_src_to_dst_coords(&frameHistInfo->startCoords2, &currCoords2);
+ p_copy_src_to_dst_coords_array(&frameHistInfo->startCoordsArray, &currCoordsArray);
/* locate shall start investigations at matching coordinates of the previous processed frame
* because the chance to find the detail near this postion is much greater than near
@@ -1106,77 +1796,163 @@ gap_track_detail_on_top_layers(gint32 imageId, gboolean doProgress, FilterValues
* but without the locateOffsets we might loose track of the detail when it moves outside the
targetRadius
* and increasing the targetRadius would also result in siginificant longer processing time)
*/
- if (frameHistInfo->prevCoords.valid)
+ sumOffsX = 0;
+ sumOffsY = 0;
+ numValidOffsets = 0;
+ for (l_idx = 0; l_idx < frameHistInfo->prevCoordsArray.numberOfCoords; l_idx++)
{
- locateOffsetX = frameHistInfo->prevCoords.px - frameHistInfo->startCoords.px;
- locateOffsetY = frameHistInfo->prevCoords.py - frameHistInfo->startCoords.py;
- }
- if (frameHistInfo->prevCoords2.valid)
+ PixelCoords *prevCoords;
+ PixelCoords *startCoords;
+
+ prevCoords = &frameHistInfo->prevCoordsArray.pixCoord[l_idx];
+ startCoords = &frameHistInfo->startCoordsArray.pixCoord[l_idx];
+ if (startCoords->valid)
+ {
+ if(prevCoords->valid)
+ {
+ locateOffsetX[l_idx] = prevCoords->px - startCoords->px;
+ locateOffsetY[l_idx] = prevCoords->py - startCoords->py;
+
+ sumOffsX += locateOffsetX[l_idx];
+ sumOffsY += locateOffsetY[l_idx];
+ numValidOffsets++;
+ }
+ }
+ }
+ if ((frameHistInfo->prevCoordsArray.numberOfCoords > numValidOffsets)
+ && (numValidOffsets > 0))
{
- locateOffsetX2 = frameHistInfo->prevCoords2.px - frameHistInfo->startCoords2.px;
- locateOffsetY2 = frameHistInfo->prevCoords2.py - frameHistInfo->startCoords2.py;
- }
+ /* for invalid coordinates use the average offests of all valid points
+ * as guess. This shall increase the chances to pick up a coordinate
+ * that has lost trace some frames ago.
+ * (Another option would be to increase the search radius in such cases
+ * but this would also significant increase the processing time)
+ */
+ for (l_idx = 0; l_idx < frameHistInfo->prevCoordsArray.numberOfCoords; l_idx++)
+ {
+ PixelCoords *prevCoords;
+ PixelCoords *startCoords;
+
+ prevCoords = &frameHistInfo->prevCoordsArray.pixCoord[l_idx];
+ startCoords = &frameHistInfo->startCoordsArray.pixCoord[l_idx];
+ if (startCoords->valid)
+ {
+ if(!prevCoords->valid)
+ {
+ locateOffsetX[l_idx] = sumOffsX / numValidOffsets;
+ locateOffsetY[l_idx] = sumOffsY / numValidOffsets;
+ }
+ }
+ }
+
+ }
+
}
else
{
- /* tracking is done with reference to the previous layer
- * therefore refresh capture. (currCoords are set to the targetCoords
- * that were calculated in previous processing step)
- */
- p_capture_2_vector_points(imageId, &currCoords, &currCoords2);
+ //if(gap_debug)
+ {
+ printf("(Bp) gap_track_detail_on_top_layers CONTINUE Previous Layer is referenence l_nlayers:%d\n"
+ ,(int)l_nlayers
+ );
+ }
+ /* tracking is done with reference to the previous layer
+ * therefore refresh capture. (currCoordsArray are set to the targetCoordsArray
+ * that were calculated in previous processing step)
+ */
+ p_capture_n_vector_points(imageId, &currCoordsArray, NUMBER_OF_COORDS,
VECTORS_NAME_REFERENCE_POINTS);
+
}
+
- lostTraceCount = frameHistInfo->lostTraceCount;
- if (currCoords.valid == TRUE)
+ }
+
+ /* ---- tracking requires at least 2 layers -------- */
+
+ if (l_nlayers > 1)
+ {
+ previousLostTraceCount = frameHistInfo->lostTraceCount;
+ targetCoordsArray.numberOfCoords = currCoordsArray.numberOfCoords;
+
+ //if(gap_debug)
{
- p_locate_target(refLayerId
- , &currCoords
- , topLayerId
- , &targetCoords
- , locateOffsetX
- , locateOffsetY
- , valPtr
- , &frameHistInfo->lostTraceCount
- );
+ printf("DetailTrack before locating %d coordinates\n", currCoordsArray.numberOfCoords);
}
-
- if (currCoords2.valid == TRUE)
+
+ for (l_idx = 0; l_idx < currCoordsArray.numberOfCoords; l_idx++)
{
- p_locate_target(refLayerId
- , &currCoords2
+ PixelCoords *currCoordsPtr;
+ PixelCoords *targetCoordsPtr;
+
+ currCoordsPtr = &currCoordsArray.pixCoord[l_idx];
+ targetCoordsPtr = &targetCoordsArray.pixCoord[l_idx];
+ targetCoordsPtr->valid = FALSE;
+ targetCoordsPtr->px = 0;
+ targetCoordsPtr->py = 0;
+
+ if (currCoordsPtr->valid == TRUE)
+ {
+ p_locate_target(refLayerId
+ , currCoordsPtr
, topLayerId
- , &targetCoords2
- , locateOffsetX2
- , locateOffsetY2
+ , targetCoordsPtr
+ , locateOffsetX[l_idx]
+ , locateOffsetY[l_idx]
, valPtr
- , &frameHistInfo->lostTraceCount
);
- }
-
+ if (targetCoordsPtr->valid == TRUE)
+ {
+ successfulTracedPointsCount++;
+ }
+ }
+ }
+ if (successfulTracedPointsCount < valPtr->numPointsSelect)
+ {
+ /* the required number of successful traces was not detected
+ * therefore increase the lostTraceCount in the frame history
+ */
+ frameHistInfo->lostTraceCount += 1;
+ }
}
-
-
- if (targetCoords.valid == TRUE)
+ gap_image_remove_all_guides(imageId);
+ currFrameNr = p_parse_frame_nr_from_layer_name(topLayerId);
+
+ /* here we could check if at least one points could be successfully located,
+ * but continue setting vectors and logging on the ivalid result may help
+ * analysis what went wrong when the xml includes the unusables results too.
+ * if ((successfulTracedPointsCount > 0)
+ * || (frameHistInfo->trackedFramesCount == 0))
+ */
+ if(TRUE)
{
- gap_image_remove_all_guides(imageId);
- p_set_2_vector_points(imageId, &targetCoords, &targetCoords2);
-
- p_copy_src_to_dst_coords(&targetCoords, &frameHistInfo->prevCoords);
- p_copy_src_to_dst_coords(&targetCoords2, &frameHistInfo->prevCoords2);
-
- frameHistInfo->frameNr = p_parse_frame_nr_from_layerId(topLayerId);
- p_coords_logging(frameHistInfo->frameNr
- , &targetCoords
- , &targetCoords2
- , &frameHistInfo->startCoords
- , &frameHistInfo->startCoords2
+ p_copy_src_to_dst_coords_array(&targetCoordsArray, &frameHistInfo->prevCoordsArray);
+
+ frameHistInfo->frameNr = currFrameNr;
+
+ if (isTrackingToFrameImage != TRUE)
+ {
+ bestIdx1 = p_selective_coords_logging(frameHistInfo->frameNr
+ , &targetCoordsArray
+ , &frameHistInfo->startCoordsArray
, valPtr
, imageId
);
-
+ }
+ else
+ {
+ gint32 bestIdx2;
+ p_select_best_coords(frameHistInfo->frameNr
+ , &targetCoordsArray
+ , &frameHistInfo->startCoordsArray
+ , valPtr
+ , &bestIdx1
+ , &bestIdx2
+ );
+ }
+ p_set_n_vector_points(imageId, &targetCoordsArray, VECTORS_NAME_TRACKING_POINTS, TRUE, TRUE, bestIdx1);
}
if (valPtr->removeMidlayers == TRUE)
@@ -1187,18 +1963,66 @@ gap_track_detail_on_top_layers(gint32 imageId, gboolean doProgress, FilterValues
);
}
+
+ /* optional save image_id as frame */
+ if (isTrackingToFrameImage == TRUE)
+ {
+ l_extension = gap_lib_alloc_extension(&valPtr->moveLogFile[0]);
+ if (l_extension != NULL)
+ {
+ gchar *frame_filename;
+ gchar *basename;
+ long number;
+ gint l_rc;
+
+ basename = gap_lib_alloc_basename(&valPtr->moveLogFile[0], &number);
+ frame_filename = gap_lib_alloc_fname_fixed_digits(basename, currFrameNr, l_extension, 6 /* digits*/
);
+
+ l_rc = gap_lib_save_named_frame(imageId, frame_filename);
+
+ g_free(basename);
+ g_free(frame_filename);
+
+ }
+ g_free(l_extension);
+ l_extension = NULL;
+
+ }
+
+
+ if((valPtr->bgLayerIsReference != TRUE)
+ && (successfulTracedPointsCount >= valPtr->numPointsSelect))
+ {
+ /* after save image as frame set current target vectors
+ * as reference for processing of the next frame (next call of this procedure)
+ */
+ p_set_n_vector_points(imageId, &targetCoordsArray, VECTORS_NAME_REFERENCE_POINTS, TRUE, FALSE, 0);
+ }
+
+ frameHistInfo->trackedFramesCount++;
p_set_frameHistInfo(frameHistInfo);
g_free(l_layers_list);
- if ((lostTraceCount == 0)
- && (frameHistInfo->lostTraceCount > 0))
+ if ((successfulTracedPointsCount < valPtr->numPointsSelect)
+ && (frameHistInfo->trackedFramesCount > 1)
+ && (previousLostTraceCount == 0))
{
+ // if (gap_debug)
+ {
+ printf("Detail Tracking Stopped at frameNr:%d previousLostTraceCount:%d
successfulTracedPointsCount:%d (required %d)\n"
+ , (int)frameHistInfo->frameNr
+ , (int)previousLostTraceCount
+ , (int)successfulTracedPointsCount
+ , (int)valPtr->numPointsSelect
+ );
+ }
/* trace lost the 1st time, display warning */
gap_arr_msg_popup(GIMP_RUN_INTERACTIVE
, _("Detail Tracking Stopped. (could not find corresponding detail)"));;
}
}
+
return(imageId);
} /* end gap_track_detail_on_top_layers */
@@ -1313,10 +2137,11 @@ gboolean
gap_detail_tracking_dialog(FilterValues *fiVals)
{
#define SPINBUTTON_ENTRY_WIDTH 80
-#define DETAIL_TRACKING_DIALOG_ARGC 12
+#define DETAIL_TRACKING_DIALOG_ARGC 13
static GapArrArg argv[DETAIL_TRACKING_DIALOG_ARGC];
gint ii;
+ gint ii_numPointsSelect;
gint ii_loacteColodiffThreshold;
gint ii_refShapeRadius;
gint ii_targetMoveRadius;
@@ -1334,6 +2159,22 @@ gap_detail_tracking_dialog(FilterValues *fiVals)
"to mark coordinate(s) to be tracked in the target frame(s)");
+ ii++; gap_arr_arg_init(&argv[ii], GAP_ARR_WGT_INT_PAIR); ii_numPointsSelect = ii;
+ argv[ii].label_txt = _("Select Points:");
+ argv[ii].help_txt = _("1: select only the best path point for movement detection, "
+ "2: select the best 2 points for movement,scale and rotation detection.");
+ argv[ii].constraint = FALSE;
+ argv[ii].int_min = 1;
+ argv[ii].int_max = 4;
+ argv[ii].umin = 1;
+ argv[ii].umax = 4;
+ argv[ii].int_ret = fiVals->numPointsSelect;
+ argv[ii].entry_width = SPINBUTTON_ENTRY_WIDTH;
+ argv[ii].has_default = TRUE;
+ argv[ii].int_default = 2;
+
+
+
ii++; gap_arr_arg_init(&argv[ii], GAP_ARR_WGT_FLT_PAIR); ii_loacteColodiffThreshold = ii;
argv[ii].constraint = TRUE;
argv[ii].label_txt = _("Locate colordiff Thres:");
@@ -1478,6 +2319,7 @@ gap_detail_tracking_dialog(FilterValues *fiVals)
_("Settings :"),
DETAIL_TRACKING_DIALOG_ARGC, argv))
{
+ fiVals->numPointsSelect = (gint32)(argv[ii_numPointsSelect].int_ret);
fiVals->refShapeRadius = (gint32)(argv[ii_refShapeRadius].int_ret);
fiVals->targetMoveRadius = (gint32)(argv[ii_targetMoveRadius].int_ret);
fiVals->loacteColodiffThreshold = (gdouble)(argv[ii_loacteColodiffThreshold].flt_ret);
diff --git a/gap/gap_detail_tracking_exec.h b/gap/gap_detail_tracking_exec.h
index d4fc144..a128827 100644
--- a/gap/gap_detail_tracking_exec.h
+++ b/gap/gap_detail_tracking_exec.h
@@ -55,10 +55,19 @@
#define GAP_DETAIL_FRAME_HISTORY_INFO "GAP_DETAIL_FRAME_HISTORY_INFO"
#define GAP_DETAIL_TRACKING_PLUG_IN_NAME "gap-detail-tracking"
+#define VECTORS_NAME_TRACKING_POINTS "TrackingPoints"
+#define VECTORS_NAME_REFERENCE_POINTS "ReferencePoints"
+#define VECTORS_NAME_START_REFERENCE_POINTS "StartReferencePoints"
+
+
typedef struct FilterValues {
gint32 refShapeRadius;
gint32 targetMoveRadius;
gdouble loacteColodiffThreshold;
+ gint32 numPointsSelect; /* 1,2 or 4 maximum number of recorded controlpoints
+ * (the best n points out of all available will be selected for
+ * logging and camera shake compensation)
+ */
gboolean coordsRelToFrame1; /* subtract coords of frame 1 when logging coords */
gint32 offsX; /* add this value when logging coords */
@@ -75,24 +84,33 @@ typedef struct PixelCoords
gboolean valid;
gint32 px;
gint32 py;
+ gdouble avgColorDiff; // 0 = best quality, 1 = worst quality
} PixelCoords;
+#define MAX_PIXEL_COORDS_ARRAY 32
+
+typedef struct PixelCoordsArray
+{
+ PixelCoords pixCoord[MAX_PIXEL_COORDS_ARRAY];
+ int numberOfCoords; /* number of used pixelCoord elements in the array */
+ gint32 numValidOffsets; /* number of valid coords involved in average Offset calculation */
+ gdouble avgOffsX; /* average horizontal movement vektor (extreme values are not
included) */
+ gdouble avgOffsY; /* average vertical movement vektor (extreme values are not
included) */
+} PixelCoordsArray;
+
typedef struct FrameHistInfo
{
gint32 workImageId;
gint32 frameNr; /* last handled frameNr */
- PixelCoords startCoords; /* coords of first processed frame */
- PixelCoords startCoords2; /* 2nd detail coords of first processed frame */
-
- PixelCoords prevCoords; /* coords of the previous processed frame */
- PixelCoords prevCoords2; /* 2nd detail coords of the previous processed frame */
+ PixelCoordsArray startCoordsArray; /* coords of first processed frame */
+ PixelCoordsArray prevCoordsArray; /* coords of the previous processed frame */
- gint32 lostTraceCount;
+ gint32 lostTraceCount; /* count frames where the required number of detailspoints could not
be located */
+ gint32 trackedFramesCount;
} FrameHistInfo;
-
/* -----------------------------------
* gap_track_detail_on_top_layers
* -----------------------------------
diff --git a/gap/gap_filter_foreach.c b/gap/gap_filter_foreach.c
index 8b7c942..d410b96 100644
--- a/gap/gap_filter_foreach.c
+++ b/gap/gap_filter_foreach.c
@@ -654,7 +654,12 @@ p_foreach_multilayer2(GimpRunMode run_mode, gint32 image_id,
{
printf ("Saving image to backupfile:%s step = %d\n",
l_step_backup_file, (int)l_idx);
- gap_filt_pdb_save_xcf(image_id, l_step_backup_file);
+ gimp_file_save(GIMP_RUN_NONINTERACTIVE
+ , image_id
+ , gap_image_get_any_layer(image_id)
+ , l_step_backup_file
+ , l_step_backup_file
+ );
}
}
diff --git a/gap/gap_filter_pdb.c b/gap/gap_filter_pdb.c
index e5beec1..009346e 100644
--- a/gap/gap_filter_pdb.c
+++ b/gap/gap_filter_pdb.c
@@ -234,40 +234,6 @@ gap_filt_pdb_call_plugin(char *plugin_name, gint32 image_id, gint32 layer_id, Gi
/* ------------------------
- * gap_filt_pdb_save_xcf
- * ------------------------
- */
-int
-gap_filt_pdb_save_xcf(gint32 image_id, char *sav_name)
-{
- GimpParam* l_params;
- gint l_retvals;
- gint l_rc;
-
- /* save current image as xcf file
- * xcf_save does operate on the complete image,
- * the drawable is ignored. (we can supply a dummy value)
- */
- l_params = gimp_run_procedure ("gimp_xcf_save",
- &l_retvals,
- GIMP_PDB_INT32, GIMP_RUN_NONINTERACTIVE,
- GIMP_PDB_IMAGE, image_id,
- GIMP_PDB_DRAWABLE, 0,
- GIMP_PDB_STRING, sav_name,
- GIMP_PDB_STRING, sav_name, /* raw name ? */
- GIMP_PDB_END);
- l_rc = -1;
- if (l_params[0].data.d_status == GIMP_PDB_SUCCESS)
- {
- l_rc = 0; /* OK */
- }
- gimp_destroy_params (l_params, l_retvals);
-
- return (l_rc);
-} /* end gap_filt_pdb_save_xcf */
-
-
-/* ------------------------
* gap_filt_pdb_get_data
* ------------------------
* gap_filt_pdb_get_data
diff --git a/gap/gap_filter_pdb.h b/gap/gap_filter_pdb.h
index d66fe36..3e53e5d 100644
--- a/gap/gap_filter_pdb.h
+++ b/gap/gap_filter_pdb.h
@@ -40,7 +40,6 @@ typedef enum
*/
gint gap_filt_pdb_call_plugin(char *plugin_name, gint32 image_id, gint32 layer_id, GimpRunMode run_mode);
-int gap_filt_pdb_save_xcf(gint32 image_id, char *sav_name);
gint gap_filt_pdb_get_data(char *key);
void gap_filt_pdb_set_data(char *key, gint plugin_data_len);
gint gap_filt_pdb_procedure_available(char *proc_name, GapFiltPdbProcType ptype);
diff --git a/gap/gap_lib.c b/gap/gap_lib.c
index 3a3ebbf..53ae3fc 100644
--- a/gap/gap_lib.c
+++ b/gap/gap_lib.c
@@ -277,7 +277,6 @@ gap_lib_layer_tracking(gint32 image_id
} /* end gap_lib_layer_tracking */
-
/* ---------------------------------
* p_set_or_pick_active_layer_by_pos
* ---------------------------------
@@ -2868,19 +2867,17 @@ gap_lib_save_named_image(gint32 image_id, const char *sav_name, GimpRunMode run_
int
gap_lib_save_named_frame(gint32 image_id, char *sav_name)
{
- GimpParam *l_params;
gchar *l_ext;
gchar *l_lowerExt;
char *l_tmpname;
- gint l_retvals;
- int l_gzip;
- int l_xcf;
+ gboolean isGzip;
+ gboolean isXcf;
int l_rc;
l_tmpname = NULL;
l_rc = -1;
- l_gzip = 0; /* dont zip */
- l_xcf = 0; /* assume no xcf format (1==xcf, 0==any other) */
+ isGzip = FALSE; /* dont zip */
+ isXcf = FALSE; /* assume no xcf format (1==xcf, 0==any other) */
/* check extension to decide if saved file will be zipped */
l_ext = gap_lib_alloc_extension(sav_name);
@@ -2897,16 +2894,16 @@ gap_lib_save_named_frame(gint32 image_id, char *sav_name)
if(0 == strcmp(l_lowerExt, ".xcf"))
{
- l_xcf = 1;
+ isXcf = TRUE;
}
else if(0 == strcmp(l_lowerExt, ".xcfgz"))
{
- l_gzip = 1; /* zip it */
- l_xcf = 1;
+ isGzip = TRUE; /* zip it */
+ isXcf = TRUE;
}
else if(0 == strcmp(l_lowerExt, ".gz"))
{
- l_gzip = 1; /* zip it */
+ isGzip = TRUE; /* zip it */
}
/* find a temp name
@@ -2937,37 +2934,38 @@ gap_lib_save_named_frame(gint32 image_id, char *sav_name)
}
- if(l_xcf != 0)
+ if(isXcf == TRUE)
{
+ gboolean isSaveOk;
if(gap_debug)
{
- printf("DEBUG: gap_lib_save_named_frame before gimp_xcf_save on file: '%s'\n"
+ printf("DEBUG: gap_lib_save_named_frame before XCF gimp_file_save on file: '%s'\n"
, l_tmpname
);
}
/* save current frame as xcf image
* xcf_save does operate on the complete image,
- * the drawable is ignored. (we can supply a dummy value)
+ * the drawable was is ignored in gimp-2.8.x
+ * but is now checked for a valid id in gimp-2.9.2
+ * (we can't supply a dummy value but must provide a vaild id now!
+ * therefore gap automatic save will fail for images without any layer with gimp-2.9.x)
*/
- l_params = gimp_run_procedure ("gimp_xcf_save",
- &l_retvals,
- GIMP_PDB_INT32, GIMP_RUN_NONINTERACTIVE,
- GIMP_PDB_IMAGE, image_id,
- GIMP_PDB_DRAWABLE, 0,
- GIMP_PDB_STRING, l_tmpname,
- GIMP_PDB_STRING, l_tmpname, /* raw name ? */
- GIMP_PDB_END);
+ isSaveOk = gimp_file_save(GIMP_RUN_NONINTERACTIVE
+ , image_id
+ , gap_image_get_any_layer(image_id)
+ , l_tmpname
+ , l_tmpname
+ );
if(gap_debug)
{
printf("DEBUG: after xcf gap_lib_save_named_frame: '%s'\n", l_tmpname);
}
- if (l_params[0].data.d_status == GIMP_PDB_SUCCESS)
+ if (isSaveOk == TRUE)
{
l_rc = image_id;
}
- gimp_destroy_params (l_params, l_retvals);
}
else
{
@@ -3001,14 +2999,14 @@ gap_lib_save_named_frame(gint32 image_id, char *sav_name)
{
g_remove(l_tmpname);
g_free(l_tmpname); /* free temporary name */
- if((l_xcf == 0) && (l_rc == SAVE_NON_XCF_FAKE_OK_FOR_READ_ONLY_TYPES))
+ if((isXcf != TRUE) && (l_rc == SAVE_NON_XCF_FAKE_OK_FOR_READ_ONLY_TYPES))
{
return (0); /* FAKE save OK */
}
return l_rc;
}
- if(l_gzip == 0)
+ if(isGzip != TRUE)
{
/* remove sav_name, then rename tmpname ==> sav_name */
g_remove(sav_name);
diff --git a/gap/gap_locate2.c b/gap/gap_locate2.c
index 8e6eb5b..5072932 100644
--- a/gap/gap_locate2.c
+++ b/gap/gap_locate2.c
@@ -51,10 +51,12 @@ typedef struct Context {
gint32 bestY;
gint32 px;
gint32 py;
+ gint32 rowsProcessedCount; /* debug information (for development/debug purpose) */
gint32 cancelAttemptCount; /* debug information (for development/debug purpose) */
- gboolean cancelAttemptFlag; /* indicator to cancel current attempt */
+ gboolean cancelAttemptFlag; /* indicator to cancel current evaluation attempt */
gboolean isFinishedFlag; /* indicator to cancel all further evaluations */
- gint32 requiredPixelCount; /* number of pixels minimum required for plausible area comparison (1/4
of full area size)*/
+ gint32 requiredPixelCount; /* number of pixels minimum required for plausible area comparison (30
% of full area size)*/
+ gint32 almostFullAreaPixelCount; /* number of pixels (90 % of full area size)*/
gint32 involvedPixelCount; /* number of pixels involved in current comparison attempt */
gdouble sumDiffValue; /* summ of the RGB channel differences of the involved pixels in the
current attempt */
gdouble currentDistance; /* square distance from reference coords to the offset of the current
attempt */
@@ -62,19 +64,25 @@ typedef struct Context {
gdouble bestMatchingDistance; /* square distance from reference coords to the offset of the best
matching attempt */
gdouble bestMatchingSumDiffValue; /* summ of the RGB channel differences of the best matching attempt */
gdouble veryNearDistance; /* square of near radius to stop evaluation when exactly matching area
is detected */
- gdouble bestMatchingAvgColordiff; /* average colordiff at best matching attempt */
GimpDrawable *refDrawable;
GimpDrawable *targetDrawable;
} Context;
+static gdouble p_calculate_average_colordiff(gdouble sumDiffValue, gint32 pixelCount);
+static void p_compare_regions (const GimpPixelRgn *refPR
+ ,const GimpPixelRgn *targetPR
+ ,Context *context);
+static gdouble p_calculate_distance_to_ref_coord(Context *context, gint32 px, gint32 py);
+static void p_attempt_locate_at_current_offset(Context *context, gint32 px, gint32 py);
+
/* ---------------------------------
* p_calculate_average_colordiff
* ---------------------------------
* calculate the average color difference for given sum of value differnces and pixel count
*/
-static inline gdouble
+static gdouble
p_calculate_average_colordiff(gdouble sumDiffValue, gint32 pixelCount)
{
if (pixelCount > 0)
@@ -105,10 +113,16 @@ p_compare_regions (const GimpPixelRgn *refPR
guint row;
guchar* ref = refPR->data; /* the reference drawable */
guchar* target = targetPR->data; /* the target drawable */
+
+ guint commonWidth;
+ guint commonHeight;
+
+ commonWidth = MIN(targetPR->w, refPR->w);
+ commonHeight = MIN(targetPR->h, refPR->h);
// if(gap_debug)
// {
-// printf("region REF x:%d y:%d w:%d h:%d TARGET x:%d y:%d w:%d h:%d px:%d py:%d\n"
+// printf("region REF x:%d y:%d w:%d h:%d TARGET x:%d y:%d w:%d h:%d Common w:%d h:%d px:%d py:%d\n"
// , (int)refPR->x
// , (int)refPR->y
// , (int)refPR->w
@@ -117,48 +131,37 @@ p_compare_regions (const GimpPixelRgn *refPR
// , (int)targetPR->y
// , (int)targetPR->w
// , (int)targetPR->h
+// , (int)commonWidth
+// , (int)commonHeight
// , (int)context->px
// , (int)context->py
// );
// }
- for (row = 0; row < targetPR->h; row++)
+ for (row = 0; row < commonHeight; row++)
{
guint col;
guint idxref;
guint idxtarget;
- if (row >= refPR->h)
- {
- continue;
- }
-
+// context->rowsProcessedCount += 1;
idxref = 0;
idxtarget = 0;
- for(col = 0; col < targetPR->w; col++)
+ for(col = 0; col < commonWidth; col++)
{
gboolean isCompareable;
isCompareable = TRUE;
- if (col < refPR->w)
+ if(refPR->bpp > 3)
{
- if(refPR->bpp > 3)
+ if(ref[idxref +3] < OPACITY_LEVEL_UCHAR)
{
- if(ref[idxref +3] < OPACITY_LEVEL_UCHAR)
- {
- /* transparent reference pixel is not compared */
- isCompareable = FALSE;
- }
+ /* transparent reference pixel is not compared */
+ isCompareable = FALSE;
}
}
- else
- {
- isCompareable = FALSE;
- }
-
-
if(targetPR->bpp > 3)
{
@@ -176,23 +179,6 @@ p_compare_regions (const GimpPixelRgn *refPR
context->sumDiffValue += abs(ref[idxref +1] - target[idxtarget +1]);
context->sumDiffValue += abs(ref[idxref +2] - target[idxtarget +2]);
- if (context->sumDiffValue > context->bestMatchingSumDiffValue)
- {
- gdouble avgColodiff;
-
- avgColodiff =
- p_calculate_average_colordiff(context->sumDiffValue
- , context->involvedPixelCount
- );
- if(avgColodiff > context->bestMatchingAvgColordiff)
- {
- /* stop evaluating area at current offset on worse results */
- context->cancelAttemptFlag = TRUE;
- context->cancelAttemptCount += 1;
- return;
- }
- }
-
}
@@ -200,6 +186,43 @@ p_compare_regions (const GimpPixelRgn *refPR
idxtarget += targetPR->bpp;
}
+ /* check for early escape possibilty when current sumDiffValue gets too large
+ * Preformance note:
+ * we could do this check already in the inner column based loop to detect escape
+ * possibilities a little earlier, but i guess
+ * overall performance might be better when cecks are done only once per row.
+ */
+ if (context->sumDiffValue > context->bestMatchingSumDiffValue)
+ {
+ if (context->bestMatchingPixelCount >= context->almostFullAreaPixelCount)
+ {
+ /* The (so far best matching) result was based on comparison
+ * of almost all area pixels involved.
+ * (Less pixels may be involved when:
+ * o) there are transparent pixels in the compared areas OR
+ * o) the compared areas are clipped at layer borders
+ * )
+ * At this point there is nearly no more chance for a better matching
+ * average color diff in the current attempt, and we can
+ * stop evaluating area at current offset for performance reasons.
+ */
+ context->cancelAttemptFlag = TRUE;
+ //context->cancelAttemptCount += 1;
+ return;
+ }
+ else if (context->sumDiffValue >= (context->bestMatchingSumDiffValue +
context->bestMatchingSumDiffValue) )
+ {
+ /* the sumDiffValue is now alredy 2 times worse then the best match.
+ * and the chances are low to get a better average color diff in the current attempt
+ */
+ context->cancelAttemptFlag = TRUE;
+ //context->cancelAttemptCount += 1;
+ return;
+ }
+ }
+
+
+
ref += refPR->rowstride;
target += targetPR->rowstride;
@@ -230,7 +253,7 @@ p_calculate_distance_to_ref_coord(Context *context, gint32 px, gint32 py)
* p_attempt_locate_at_current_offset
* --------------------------------------------
*/
-void
+static void
p_attempt_locate_at_current_offset(Context *context, gint32 px, gint32 py)
{
GimpPixelRgn refPR;
@@ -239,6 +262,8 @@ p_attempt_locate_at_current_offset(Context *context, gint32 px, gint32 py)
gint rx1, ry1, rWidth, rHeight;
gint tx1, ty1, tWidth, tHeight;
gint commonAreaWidth, commonAreaHeight;
+ gint fullShapeWidth, fullShapeHeight;
+ gint rWidthRequired, rHeightRequired;
gboolean isIntersect;
gint leftShapeRadius;
@@ -249,14 +274,17 @@ p_attempt_locate_at_current_offset(Context *context, gint32 px, gint32 py)
{
return;
}
+
+ fullShapeWidth = (2 * context->refShapeRadius);
+ fullShapeHeight = (2 * context->refShapeRadius);
- /* calculate processing relevant interecting reference / target rectangles */
+ /* calculate processing relevant intersecting reference / target rectangles */
isIntersect =
gimp_rectangle_intersect((context->refX - context->refShapeRadius) /* origin1 */
, (context->refY - context->refShapeRadius)
- , (2 * context->refShapeRadius) /* width1 */
- , (2 * context->refShapeRadius) /* height1 */
+ , fullShapeWidth /* width1 */
+ , fullShapeHeight /* height1 */
,0
,0
,context->refDrawable->width
@@ -270,7 +298,7 @@ p_attempt_locate_at_current_offset(Context *context, gint32 px, gint32 py)
{
return;
}
-
+
leftShapeRadius = context->refX - rx1;
upperShapeRadius = context->refY - ry1;
@@ -292,9 +320,25 @@ p_attempt_locate_at_current_offset(Context *context, gint32 px, gint32 py)
{
return;
}
-
+
commonAreaWidth = tWidth;
commonAreaHeight = tHeight;
+
+ // TODO test if 2/3 of the fullShapeWidth and fullShapeHeight is sufficient for usable results.
+ // alternative1: maybe require rWidth and rHeight
+ // alternative2: maybe require fullShapeWidth and fullShapeHeight
+ rWidthRequired = (fullShapeWidth * 2) / 3;
+ rHeightRequired = (fullShapeHeight * 2) / 3;
+
+ if ((commonAreaWidth < rWidthRequired)
+ || (commonAreaHeight < rHeightRequired))
+ {
+ /* the common area is significant smaller than the reference shape
+ * skip the compare attempt in this case to avoid unpredictable results (near borders)
+ */
+ return;
+ }
+
// if(gap_debug)
@@ -360,7 +404,7 @@ p_attempt_locate_at_current_offset(Context *context, gint32 px, gint32 py)
* early escaping from the loop with pr != NULL
* leads to memory leaks due to unbalanced tile ref/unref calls.
* the call to gap_gimp_pixel_rgns_unref cals unref on the current tile
- * (in th same way as gimp_pixel_rgns_process does)
+ * (in the same way as gimp_pixel_rgns_process does)
* but does not ref another available tile.
*/
gap_gimp_pixel_rgns_unref (pr);
@@ -380,13 +424,14 @@ p_attempt_locate_at_current_offset(Context *context, gint32 px, gint32 py)
context->bestMatchingPixelCount = context->involvedPixelCount;
context->bestX = px;
context->bestY = py;
- context->bestMatchingAvgColordiff =
- p_calculate_average_colordiff(context->bestMatchingSumDiffValue
- , context->bestMatchingPixelCount
- );
-
+
if(gap_debug)
{
+ gdouble bestMatchingAvgColordiff;
+
+ bestMatchingAvgColordiff = p_calculate_average_colordiff(context->bestMatchingSumDiffValue
+ , context->bestMatchingPixelCount
+ );
printf("FOUND: bestX:%d bestY:%d squareDist:%d\n"
" sumDiffValues:%d pixelCount:%d bestMatchingAvgColordiff:%.5f\n"
, (int)context->bestX
@@ -394,7 +439,7 @@ p_attempt_locate_at_current_offset(Context *context, gint32 px, gint32 py)
, (int)context->bestMatchingDistance
, (int)context->bestMatchingSumDiffValue
, (int)context->bestMatchingPixelCount
- , (float)context->bestMatchingAvgColordiff
+ , (float)bestMatchingAvgColordiff
);
}
@@ -439,14 +484,19 @@ gap_locateAreaWithinRadiusWithOffset(gint32 refDrawableId
Context *context;
gdouble averageColorDiff;
gboolean isFinishedFlag;
- gint idx;
- gint idy;
gdouble maxPixelCount;
+ gint32 shapeDiameter;
+ gint32 fullAreaPixelCount;
+ gint32 dx;
+ gint32 dy;
*targetX = refX;
*targetY = refY;
+ shapeDiameter = 1 + (refShapeRadius + refShapeRadius);
+ fullAreaPixelCount = (shapeDiameter) * (shapeDiameter);
+
/* init Context */
context = &contextData;
context->refShapeRadius = refShapeRadius;
@@ -455,9 +505,11 @@ gap_locateAreaWithinRadiusWithOffset(gint32 refDrawableId
context->bestX = refX;
context->bestY = refY;
context->cancelAttemptCount = 0;
+ context->rowsProcessedCount = 0;
context->cancelAttemptFlag = FALSE;
context->isFinishedFlag = FALSE;
- context->requiredPixelCount = MAX(1, ((refShapeRadius * refShapeRadius) / 4));
+ context->requiredPixelCount = (fullAreaPixelCount * 30) / 100;
+ context->almostFullAreaPixelCount = (fullAreaPixelCount * 90) / 100;
context->involvedPixelCount = 0;
context->sumDiffValue = 0;
context->currentDistance = 0;
@@ -472,51 +524,63 @@ gap_locateAreaWithinRadiusWithOffset(gint32 refDrawableId
context->bestMatchingSumDiffValue = maxPixelCount * MAX_DIFF_VALUE_PER_PIXEL;
context->bestMatchingDistance = maxPixelCount;
- context->bestMatchingAvgColordiff = 1.0;
averageColorDiff = 1.0;
- for(idx = 0; idx <= targetMoveRadius; idx ++)
+ if(gap_debug)
+ {
+ printf("gap_locateAreaWithinRadiusWithOffset START: refDrawableId:%d targetDrawableId:%d\n"
+ " refX:%d refY:%d refShapeRadius:%d\n"
+ " requiredPixelCount:%d almostFullAreaPixelCount:%d
fullAreaPixelCount:%d\n"
+ , (int)refDrawableId
+ , (int)targetDrawableId
+ , (int)context->refX
+ , (int)context->refY
+ , (int)refShapeRadius
+ , (int)context->requiredPixelCount
+ , (int)context->almostFullAreaPixelCount
+ , (int)fullAreaPixelCount
+ );
+ }
+
+
+ for(dx = 0; dx <= targetMoveRadius; dx ++)
{
if (context->isFinishedFlag)
{
break;
}
- for(idy = 0; idy <= targetMoveRadius; idy++)
+ for(dy = 0; dy <= targetMoveRadius; dy++)
{
- gint32 dx;
- gint32 dy;
-
- dx = idx;
- dy = idy;
- p_attempt_locate_at_current_offset(context, (offsetX + refX) + dx, (offsetY + refY) +dy);
+
+ p_attempt_locate_at_current_offset(context, (offsetX + refX) + dx, (offsetY + refY) + dy);
if (isFinishedFlag)
{
break;
}
- if (idx > 0)
+ if (dx > 0)
{
- p_attempt_locate_at_current_offset(context, (offsetX + refX) - dx, (offsetY + refY) +dy);
+ p_attempt_locate_at_current_offset(context, (offsetX + refX) - dx, (offsetY + refY) + dy);
if (context->isFinishedFlag)
{
break;
}
}
- if (idy > 0)
+ if (dy > 0)
{
- p_attempt_locate_at_current_offset(context, (offsetX + refX) + dx, (offsetY + refY) -dy);
+ p_attempt_locate_at_current_offset(context, (offsetX + refX) + dx, (offsetY + refY) - dy);
if (context->isFinishedFlag)
{
break;
}
}
- if ((idx > 0) && (idy > 0))
+ if ((dx > 0) && (dy > 0))
{
- p_attempt_locate_at_current_offset(context, (offsetX + refX) - dx, (offsetY + refY) -dy);
+ p_attempt_locate_at_current_offset(context, (offsetX + refX) - dx, (offsetY + refY) - dy);
if (context->isFinishedFlag)
{
break;
@@ -530,13 +594,17 @@ gap_locateAreaWithinRadiusWithOffset(gint32 refDrawableId
{
*targetX = context->bestX;
*targetY = context->bestY;
- averageColorDiff = context->bestMatchingAvgColordiff;
+ averageColorDiff = p_calculate_average_colordiff(context->bestMatchingSumDiffValue
+ , context->bestMatchingPixelCount
+ );
+
if(gap_debug)
{
printf("gap_locateAreaWithinRadiusWithOffset Result: bestX:%d bestY:%d averageColorDiff:%.5f\n"
" sumDiffValues:%d pixelCount:%d\n"
- " refX:%d refY:%d cancelAttemptCount:%d\n"
+ " refX:%d refY:%d cancelAttemptCount:%d rowsProcessedCount:%d\n"
+ " requiredPixelCount:%d almostFullAreaPixelCount:%d\n"
, (int)context->bestX
, (int)context->bestY
, (float)averageColorDiff
@@ -545,9 +613,19 @@ gap_locateAreaWithinRadiusWithOffset(gint32 refDrawableId
, (int)context->refX
, (int)context->refY
, (int)context->cancelAttemptCount
+ , (int)context->rowsProcessedCount
+ , (int)context->requiredPixelCount
+ , (int)context->almostFullAreaPixelCount
);
}
}
+ else
+ {
+ if(gap_debug)
+ {
+ printf("gap_locateAreaWithinRadiusWithOffset * NOTHING FOUND *\n");
+ }
+ }
if(context->refDrawable != NULL)
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]