contrek 1.2.4 → 1.2.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +14 -1
- data/Gemfile.lock +1 -1
- data/ext/cpp_polygon_finder/PolygonFinder/src/Tests.cpp +33 -12
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/bitmaps/RawBitmap.cpp +4 -4
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/bitmaps/RawBitmap.h +1 -1
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/bitmaps/spng.c +17 -1
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/Node.h +1 -0
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/PolygonFinder.cpp +1 -1
- data/ext/cpp_polygon_finder/PolygonFinder/src/polygon/finder/PolygonFinder.h +25 -35
- data/lib/contrek/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: ca0c4f7e37692b2d2e5b17556ed67bee4b624511ddf1dc3865ecf910e8f46aeb
|
|
4
|
+
data.tar.gz: 66b9af71020dc6a86da1abace1d0467807b9212e207b71a7f6337b2257a34192
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 63ba135c86c7849fda12e542af23a2746681f8aade888daa9269438c1601e8184215b3fe5081ee9caabce923e7dfafd8bcc3613fb06cf2ec9997193ff831e5c7
|
|
7
|
+
data.tar.gz: dd691e542a8722be9532d3c0f206d4b2a2499874f0be52786c99822036d3e0e463ffb6d301bceae44e8f487f434eeff98c9a931cffe27eab3ed5d249daf060e5
|
data/CHANGELOG.md
CHANGED
|
@@ -95,7 +95,20 @@ All notable changes to this project will be documented in this file.
|
|
|
95
95
|
|
|
96
96
|
## [1.2.3] - 2026-05-23
|
|
97
97
|
### Changed
|
|
98
|
-
### Changed
|
|
99
98
|
* **SVG Conversion:** Added utility methods to convert point coordinates directly into SVG paths.
|
|
100
99
|
* **Contrek API & RAII Architecture:** Refactored the Contrek API to utilize an RAII (Resource Acquisition Is Initialization) pattern, safely wrapping both the trace engine and the processing results within a unified context lifecycle shell.
|
|
101
100
|
* **ProcessResult Memory Management:** Updated `ProcessResult` to properly manage resource deallocation during cloning operations, ensuring deep-copied or moved internal points are automatically and safely freed when the context scope ends.
|
|
101
|
+
|
|
102
|
+
## [1.2.4] - 2026-05-26
|
|
103
|
+
### Changed
|
|
104
|
+
* Fixed an issue in connectivity8 tracing mode that, under specific rare conditions, disrupted the topological continuity of external contours in favor of internal ones.
|
|
105
|
+
|
|
106
|
+
## [1.2.5] - 2026-05-27
|
|
107
|
+
### Changed
|
|
108
|
+
- **Refactored `ProcessResult.clone()`:** Switched from fragmented dynamic allocation to a contiguous `std::vector` with explicit `.reserve()`. Eliminates heap fragmentation during high-res streaming.
|
|
109
|
+
|
|
110
|
+
## [1.2.6] - 2026-05-31
|
|
111
|
+
### Changed
|
|
112
|
+
- **Refactored `spng.c` function `rgb8_row_to_rgba8`:** Extended a loop counter to `size_t` (previously limited to `uint32_t`), which was causing segmentation faults when reading massive images (e.g., 81920x81920).
|
|
113
|
+
- **Refactored `RawBitmap.define` function:** Updated area and size calculations to use full 64-bit integers.
|
|
114
|
+
- **Refactored `PolygonFinder.to_svg_stream()` function:** Optimized performance to efficiently handle massive SVG streams of 2 GB and beyond.
|
data/Gemfile.lock
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
#include "Tests.h"
|
|
11
|
+
#include <sys/resource.h>
|
|
11
12
|
#include <string.h>
|
|
12
13
|
#include <iostream>
|
|
13
14
|
#include <list>
|
|
@@ -17,6 +18,7 @@
|
|
|
17
18
|
#include <cstring>
|
|
18
19
|
#include <algorithm>
|
|
19
20
|
#include <cstdio>
|
|
21
|
+
|
|
20
22
|
#include "polygon/finder/PolygonFinder.h"
|
|
21
23
|
#include "polygon/finder/concurrent/ClippedPolygonFinder.h"
|
|
22
24
|
#include "polygon/bitmaps/Bitmap.h"
|
|
@@ -260,6 +262,16 @@ void Tests::test_h()
|
|
|
260
262
|
delete right_result;
|
|
261
263
|
}
|
|
262
264
|
|
|
265
|
+
double get_peak_rss() {
|
|
266
|
+
struct rusage r_usage;
|
|
267
|
+
getrusage(RUSAGE_SELF, &r_usage);
|
|
268
|
+
#ifdef __APPLE__
|
|
269
|
+
return r_usage.ru_maxrss / (1024.0 * 1024.0);
|
|
270
|
+
#else
|
|
271
|
+
return r_usage.ru_maxrss / 1024.0;
|
|
272
|
+
#endif
|
|
273
|
+
}
|
|
274
|
+
|
|
263
275
|
/* In this example, PNG data is read by streaming into a user-defined buffer height.
|
|
264
276
|
Contrek scans each stripe and extracts the polygons. Finally, it merges all
|
|
265
277
|
polygons from every stripe as if they had been read from a single image and saves
|
|
@@ -267,7 +279,7 @@ void Tests::test_h()
|
|
|
267
279
|
This approach allows for processing large PNG files on systems where memory
|
|
268
280
|
would otherwise be insufficient.
|
|
269
281
|
*/
|
|
270
|
-
void stream_png_image(const std::string& filepath, uint32_t stripe_height) {
|
|
282
|
+
void stream_png_image(const std::string& filepath, uint32_t stripe_height, bool generate_svg = false, bool generate_png = false) {
|
|
271
283
|
std::vector<ProcessResult*> result_clones;
|
|
272
284
|
std::vector<std::string> varguments = {};
|
|
273
285
|
VerticalMerger vmerger(0, &varguments);
|
|
@@ -293,6 +305,7 @@ void stream_png_image(const std::string& filepath, uint32_t stripe_height) {
|
|
|
293
305
|
|
|
294
306
|
// allocates stripe buffer
|
|
295
307
|
RawBitmap stripe_bitmap;
|
|
308
|
+
std::cout << total_width << " " << stripe_height << std::endl;
|
|
296
309
|
stripe_bitmap.define(total_width, stripe_height, 4, true);
|
|
297
310
|
RGBNotMatcher not_matcher(-1);
|
|
298
311
|
|
|
@@ -303,7 +316,8 @@ void stream_png_image(const std::string& filepath, uint32_t stripe_height) {
|
|
|
303
316
|
}
|
|
304
317
|
|
|
305
318
|
size_t row_size = static_cast<size_t>(total_width) * 4;
|
|
306
|
-
|
|
319
|
+
int stripe_count = 0;
|
|
320
|
+
// main stripes loop
|
|
307
321
|
for (uint32_t current_y_offset = 0; current_y_offset < total_height; current_y_offset += stripe_height) {
|
|
308
322
|
int uncovered_height = total_height - current_y_offset;
|
|
309
323
|
|
|
@@ -332,12 +346,13 @@ void stream_png_image(const std::string& filepath, uint32_t stripe_height) {
|
|
|
332
346
|
PolygonFinder polygon_finder(&stripe_bitmap, ¬_matcher, nullptr, &finder_arguments);
|
|
333
347
|
ProcessResult *result = polygon_finder.process_info();
|
|
334
348
|
if (result) {
|
|
335
|
-
std::cout << "
|
|
349
|
+
std::cout << "stripe " << stripe_count << ": found polygons " << result->groups << std::endl;
|
|
336
350
|
ProcessResult* safe_result = result->clone();
|
|
337
351
|
result_clones.push_back(safe_result);
|
|
338
352
|
vmerger.add_tile(*safe_result);
|
|
339
353
|
delete result;
|
|
340
354
|
}
|
|
355
|
+
stripe_count++;
|
|
341
356
|
}
|
|
342
357
|
|
|
343
358
|
std::cout << "Merging polygons ..." << std::endl;
|
|
@@ -345,15 +360,20 @@ void stream_png_image(const std::string& filepath, uint32_t stripe_height) {
|
|
|
345
360
|
|
|
346
361
|
if (merged_result) {
|
|
347
362
|
std::cout << "Founds total polygons: " << merged_result->groups << std::endl;
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
363
|
+
if (generate_png) {
|
|
364
|
+
RawBitmap full_bitmap;
|
|
365
|
+
full_bitmap.define(total_width, total_height, 4, true);
|
|
366
|
+
full_bitmap.fill(255, 255, 255);
|
|
367
|
+
merged_result->draw_on_bitmap(full_bitmap);
|
|
368
|
+
std::cout << "Saving whole png ..." << std::endl;
|
|
369
|
+
if (full_bitmap.save_to_png("whole.png")) {
|
|
370
|
+
std::cout << "Png saved!" << std::endl;
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
if (generate_svg) {
|
|
374
|
+
merged_result->save_svg("whole.svg");
|
|
375
|
+
std::cout << "Svg saved!" << std::endl;
|
|
376
|
+
}
|
|
357
377
|
}
|
|
358
378
|
delete merged_result;
|
|
359
379
|
// frees memory
|
|
@@ -366,4 +386,5 @@ void stream_png_image(const std::string& filepath, uint32_t stripe_height) {
|
|
|
366
386
|
|
|
367
387
|
void Tests::test_i() {
|
|
368
388
|
stream_png_image("../images/graphs_1024x1024.png", 300);
|
|
389
|
+
std::cout << "Memory usage peak: " << get_peak_rss() << " MB" << std::endl;
|
|
369
390
|
}
|
|
@@ -56,16 +56,16 @@ void RawBitmap::draw_pixel(int x, int y, unsigned char r, unsigned char g, unsig
|
|
|
56
56
|
if (bpp > 3) p[3] = a;
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
-
|
|
60
|
-
{ this->width
|
|
59
|
+
size_t RawBitmap::define(uint width, uint height, int bytes_per_pixel, bool clear)
|
|
60
|
+
{ this->width = width;
|
|
61
61
|
this->height = height;
|
|
62
62
|
this->bpp = bytes_per_pixel;
|
|
63
|
-
|
|
63
|
+
size_t dimension = static_cast<size_t>(width) * static_cast<size_t>(height) * static_cast<size_t>(bytes_per_pixel);
|
|
64
64
|
this->image = std::make_unique<unsigned char[]>(dimension);
|
|
65
65
|
if (clear) {
|
|
66
66
|
std::fill(image.get(), image.get() + dimension, 0);
|
|
67
67
|
}
|
|
68
|
-
return dimension;
|
|
68
|
+
return static_cast<uint32_t>(dimension);
|
|
69
69
|
}
|
|
70
70
|
|
|
71
71
|
void RawBitmap::fill(unsigned char r, unsigned char g, unsigned char b, unsigned char a) {
|
|
@@ -25,7 +25,7 @@ class RawBitmap : public Bitmap {
|
|
|
25
25
|
unsigned int rgb_value_at(int x, int y);
|
|
26
26
|
const unsigned char* get_row_ptr(int y) const;
|
|
27
27
|
int get_bytes_per_pixel() const;
|
|
28
|
-
|
|
28
|
+
size_t define(uint width, uint height, int bytes_per_pixel, bool clear = false);
|
|
29
29
|
void draw_pixel(int x, int y, unsigned char r, unsigned char g, unsigned char b, unsigned char a = 255);
|
|
30
30
|
bool save_to_png(const std::string& filename);
|
|
31
31
|
void fill(unsigned char r, unsigned char g, unsigned char b, unsigned char a = 255);
|
|
@@ -534,7 +534,7 @@ static void u16_row_to_bigendian(void *row, size_t size)
|
|
|
534
534
|
write_u16(&px[i], px[i]);
|
|
535
535
|
}
|
|
536
536
|
}
|
|
537
|
-
|
|
537
|
+
/*
|
|
538
538
|
static void rgb8_row_to_rgba8(const unsigned char *row, unsigned char *out, uint32_t n)
|
|
539
539
|
{
|
|
540
540
|
uint32_t i;
|
|
@@ -543,6 +543,22 @@ static void rgb8_row_to_rgba8(const unsigned char *row, unsigned char *out, uint
|
|
|
543
543
|
memcpy(out + i * 4, row + i * 3, 3);
|
|
544
544
|
out[i*4+3] = 255;
|
|
545
545
|
}
|
|
546
|
+
}*/
|
|
547
|
+
|
|
548
|
+
/* Fix overflow Gigapixel images */
|
|
549
|
+
static void rgb8_row_to_rgba8(const unsigned char *row, unsigned char *out, uint32_t n)
|
|
550
|
+
{
|
|
551
|
+
size_t i;
|
|
552
|
+
for(i=0; i < n; i++)
|
|
553
|
+
{
|
|
554
|
+
out[0] = row[0];
|
|
555
|
+
out[1] = row[1];
|
|
556
|
+
out[2] = row[2];
|
|
557
|
+
out[3] = 255;
|
|
558
|
+
|
|
559
|
+
out += 4;
|
|
560
|
+
row += 3;
|
|
561
|
+
}
|
|
546
562
|
}
|
|
547
563
|
|
|
548
564
|
static unsigned num_channels(const struct spng_ihdr *ihdr)
|
|
@@ -103,7 +103,6 @@ void PolygonFinder::scan() {
|
|
|
103
103
|
ProcessResult* PolygonFinder::process_info() {
|
|
104
104
|
ProcessResult *pr = new ProcessResult();
|
|
105
105
|
pr->groups = this->node_cluster->sequences.size();
|
|
106
|
-
pr->polygons = std::move(this->node_cluster->polygons);
|
|
107
106
|
pr->benchmarks = std::move(this->reports);
|
|
108
107
|
pr->treemap = this->node_cluster->treemap;
|
|
109
108
|
pr->width = this->source_bitmap->w();
|
|
@@ -124,6 +123,7 @@ ProcessResult* PolygonFinder::process_info() {
|
|
|
124
123
|
pr->named_sequence = sequence;
|
|
125
124
|
}
|
|
126
125
|
else pr->named_sequence = "";
|
|
126
|
+
pr->polygons = std::move(this->node_cluster->polygons);
|
|
127
127
|
return(pr);
|
|
128
128
|
}
|
|
129
129
|
|
|
@@ -63,7 +63,7 @@ struct ProcessResult {
|
|
|
63
63
|
std::list<Polygon> polygons;
|
|
64
64
|
std::string named_sequence;
|
|
65
65
|
std::vector<std::pair<int, int>> treemap;
|
|
66
|
-
std::vector<
|
|
66
|
+
std::vector<Point> cloned_points_storage;
|
|
67
67
|
|
|
68
68
|
void draw_on_bitmap(RawBitmap& canvas) const;
|
|
69
69
|
|
|
@@ -119,22 +119,25 @@ struct ProcessResult {
|
|
|
119
119
|
|
|
120
120
|
for (const auto& poly : this->polygons) {
|
|
121
121
|
Polygon new_poly;
|
|
122
|
-
// bounds
|
|
123
122
|
new_poly.bounds = poly.bounds;
|
|
123
|
+
new_poly.outer.reserve(poly.outer.size());
|
|
124
124
|
// outer
|
|
125
125
|
for (const Point* p : poly.outer) {
|
|
126
126
|
if (p) {
|
|
127
|
-
new_res->cloned_points_storage.push_back(
|
|
128
|
-
|
|
127
|
+
new_res->cloned_points_storage.push_back(Point(p->x, p->y));
|
|
128
|
+
size_t idx = new_res->cloned_points_storage.size() - 1;
|
|
129
|
+
new_poly.outer.push_back(&new_res->cloned_points_storage[idx]);
|
|
129
130
|
}
|
|
130
131
|
}
|
|
131
132
|
// inner
|
|
132
133
|
for (const auto& seq : poly.inner) {
|
|
133
134
|
std::vector<Point*> new_seq;
|
|
135
|
+
new_seq.reserve(seq.size());
|
|
134
136
|
for (const Point* p : seq) {
|
|
135
137
|
if (p) {
|
|
136
|
-
new_res->cloned_points_storage.push_back(
|
|
137
|
-
|
|
138
|
+
new_res->cloned_points_storage.push_back(Point(p->x, p->y));
|
|
139
|
+
size_t idx = new_res->cloned_points_storage.size() - 1;
|
|
140
|
+
new_seq.push_back(&new_res->cloned_points_storage[idx]);
|
|
138
141
|
}
|
|
139
142
|
}
|
|
140
143
|
new_poly.inner.push_back(new_seq);
|
|
@@ -144,50 +147,37 @@ struct ProcessResult {
|
|
|
144
147
|
return new_res;
|
|
145
148
|
}
|
|
146
149
|
|
|
147
|
-
std::
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
"<svg xmlns=\"http://www.w3.org/2000/svg\" "
|
|
151
|
-
"width=\"" + std::to_string(width) +
|
|
152
|
-
"\" height=\"" + std::to_string(height) + "\">");
|
|
150
|
+
void to_svg_stream(std::ostream& os) const {
|
|
151
|
+
os << "<svg xmlns=\"http://www.w3.org/2000/svg\" "
|
|
152
|
+
<< "width=\"" << width << "\" height=\"" << height << "\">\n";
|
|
153
153
|
for (const auto& poly : polygons) {
|
|
154
|
-
|
|
155
|
-
|
|
154
|
+
// --- OUTER (Red) ---
|
|
155
|
+
if (!poly.outer.empty()) {
|
|
156
|
+
os << "<polygon points=\"";
|
|
156
157
|
bool first = true;
|
|
157
158
|
for (const Point* p : poly.outer) {
|
|
158
159
|
if (!p) continue;
|
|
159
|
-
if (!first)
|
|
160
|
-
pts << " ";
|
|
160
|
+
if (!first) os << " ";
|
|
161
161
|
first = false;
|
|
162
|
-
|
|
162
|
+
os << p->x << "," << p->y;
|
|
163
163
|
}
|
|
164
|
-
|
|
165
|
-
"<polygon points=\"" + pts.str() +
|
|
166
|
-
"\" fill=\"none\" stroke=\"red\" stroke-width=\"1\"/>");
|
|
164
|
+
os << "\" fill=\"none\" stroke=\"red\" stroke-width=\"1\"/>\n";
|
|
167
165
|
}
|
|
168
|
-
//
|
|
166
|
+
// --- INNER (Green) ---
|
|
169
167
|
for (const auto& sequence : poly.inner) {
|
|
170
168
|
if (sequence.empty()) continue;
|
|
171
|
-
|
|
169
|
+
os << "<polygon points=\"";
|
|
172
170
|
bool first = true;
|
|
173
171
|
for (const Point* p : sequence) {
|
|
174
172
|
if (!p) continue;
|
|
175
|
-
if (!first)
|
|
173
|
+
if (!first) os << " ";
|
|
176
174
|
first = false;
|
|
177
|
-
|
|
175
|
+
os << p->x << "," << p->y;
|
|
178
176
|
}
|
|
179
|
-
|
|
180
|
-
"<polygon points=\"" + pts.str() +
|
|
181
|
-
"\" fill=\"none\" stroke=\"green\" stroke-width=\"1\"/>");
|
|
177
|
+
os << "\" fill=\"none\" stroke=\"green\" stroke-width=\"1\"/>\n";
|
|
182
178
|
}
|
|
183
179
|
}
|
|
184
|
-
|
|
185
|
-
std::ostringstream result;
|
|
186
|
-
for (size_t i = 0; i < lines.size(); ++i) {
|
|
187
|
-
result << lines[i];
|
|
188
|
-
if (i + 1 < lines.size()) result << "\n";
|
|
189
|
-
}
|
|
190
|
-
return result.str();
|
|
180
|
+
os << "</svg>";
|
|
191
181
|
}
|
|
192
182
|
|
|
193
183
|
void save_svg(const std::string& filename) const {
|
|
@@ -195,7 +185,7 @@ struct ProcessResult {
|
|
|
195
185
|
if (!file.is_open()) {
|
|
196
186
|
throw std::runtime_error("Unable to open SVG file: " + filename);
|
|
197
187
|
}
|
|
198
|
-
file
|
|
188
|
+
to_svg_stream(file);
|
|
199
189
|
file.close();
|
|
200
190
|
}
|
|
201
191
|
};
|
data/lib/contrek/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: contrek
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.2.
|
|
4
|
+
version: 1.2.6
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Emanuele Cesaroni
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-05-
|
|
11
|
+
date: 2026-05-31 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: rspec
|