forked from seqscope/spatula
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcmd_draw_3way.cpp
More file actions
222 lines (193 loc) · 10.1 KB
/
Copy pathcmd_draw_3way.cpp
File metadata and controls
222 lines (193 loc) · 10.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
#include "spatula.h"
#include "qgenlib/dataframe.h"
#include "qgenlib/tsv_reader.h"
#include "qgenlib/qgen_error.h"
#include <cmath>
#include <ctime>
#include <set>
#include <sys/stat.h>
#include <sys/types.h>
#include <algorithm>
#define cimg_display 0 // remove the need for X11 library
#include "cimg/CImg.h"
/////////////////////////////////////////////////////////////////////////
// draw-xy : Draw the single-color image of points in 2D space
////////////////////////////////////////////////////////////////////////
int32_t cmdDraw3way(int32_t argc, char **argv)
{
std::string manifestf;
std::string nbcdf;
std::vector<std::string> nmatchfs;
std::string ngebcdf;
double coord_per_pixel = 1000.; // 1 pixel = 1000 units = 1 um
int32_t icol_x_nbcd = 3;
int32_t icol_y_nbcd = 4;
int32_t icol_x_nmatch = 3;
int32_t icol_y_nmatch = 4;
int32_t icol_x_ngebcd = 5;
int32_t icol_y_ngebcd = 6;
int32_t icol_gene_ngebcd = 7;
int32_t isubcol_gene_ngebcd = 0;
std::string color_nbcd = "#000032";
std::string color_nmatch = "#004800";
std::string color_ngebcd = "#640000";
std::string id_manifest = "1_1";
std::string outf;
paramList pl;
BEGIN_LONG_PARAMS(longParameters)
LONG_PARAM_GROUP("Input files", NULL)
LONG_STRING_PARAM("manifest", &manifestf, "Manifest file from combine-sbcds. xmin/xmax/ymin/ymax will be automatically detected")
LONG_STRING_PARAM("nbcd", &nbcdf, "Spatial barcode dictionary generated from 'combine-sbcds' command")
LONG_MULTI_STRING_PARAM("nmatch", &nmatchfs, "Spatial barcode dictionary generated from 'match-sbcds' command")
LONG_STRING_PARAM("ngebcd", &ngebcdf, "Spatial barcode dictionary generated from alignment pipeline")
LONG_PARAM_GROUP("Input options", NULL)
LONG_INT_PARAM("icol-x-nbcd", &icol_x_nbcd, "0-based index of the column for x in nbcd")
LONG_INT_PARAM("icol-y-nbcd", &icol_y_nbcd, "0-based index of the column for y in nbcd")
LONG_INT_PARAM("icol-x-nmatch", &icol_x_nmatch, "0-based index of the column for x in nmatch")
LONG_INT_PARAM("icol-y-nmatch", &icol_y_nmatch, "0-based index of the column for y in nmatch")
LONG_INT_PARAM("icol-x-ngebcd", &icol_x_ngebcd, "0-based index of the column for x in ngebcd")
LONG_INT_PARAM("icol-y-ngebcd", &icol_y_ngebcd, "0-based index of the column for y in ngebcd")
LONG_INT_PARAM("icol-gene-ngebcd", &icol_gene_ngebcd, "0-based index of the column for gene count in ngebcd")
LONG_INT_PARAM("isubcol-gene-ngebcd", &isubcol_gene_ngebcd, "0-based index of the sub column for gene count in ngebcd (0: Gene, 1: GeneFull, ...)")
LONG_STRING_PARAM("id-manifest", &id_manifest, "ID of the tile in the manifest file to use")
LONG_PARAM_GROUP("Output options", NULL)
LONG_DOUBLE_PARAM("coord-per-pixel", &coord_per_pixel, "Number of coordinate units per pixel")
LONG_STRING_PARAM("color-nbcd", &color_nmatch, "RGB hex color code for nbcd per observation")
LONG_STRING_PARAM("color-nmatch", &color_ngebcd, "RGB hex color code for nmatch per observation")
LONG_STRING_PARAM("color-nge", &color_nbcd, "RGB hex color code for nge per observation")
LONG_PARAM_GROUP("Output Options", NULL)
LONG_STRING_PARAM("out", &outf, "Output file name")
END_LONG_PARAMS();
pl.Add(new longParams("Available Options", longParameters));
pl.Read(argc, argv);
pl.Status();
if ( manifestf.empty() || outf.empty() )
error("--manifest and --out must be specified");
notice("Analysis started");
// read the manifest file and determine the xmin/xmax/ymin/ymax
uint64_t xmin = 0, xmax = 0, ymin = 0, ymax = 0;
dataframe_t df(manifestf.c_str());
if ( df.nrows == 0 )
error("Empty dataframe %s", manifestf.c_str());
int32_t i_id = df.get_colidx("id");
if ( i_id < 0 )
error("Cannot find the column 'id' in %s", manifestf.c_str());
for(int32_t i=0; df.nrows; ++i) {
// find the matching tile
if ( df.get_str_elem(i, i_id).compare(id_manifest) == 0 ) {
// parse xmin/xmax/ymin/ymax
int32_t i_xmin = df.get_colidx("xmin");
int32_t i_xmax = df.get_colidx("xmax");
int32_t i_ymin = df.get_colidx("ymin");
int32_t i_ymax = df.get_colidx("ymax");
if ( i_xmin < 0 || i_xmax < 0 || i_ymin < 0 || i_ymax < 0 )
error("Cannot find the columns 'xmin', 'xmax', 'ymin', 'ymax' in %s", manifestf.c_str());
xmin = df.get_uint64_elem(i, i_xmin);
xmax = df.get_uint64_elem(i, i_xmax);
ymin = df.get_uint64_elem(i, i_ymin);
ymax = df.get_uint64_elem(i, i_ymax);
break;
}
}
// parse input colors
uint32_t rgb_nbcd[3], rgb_nmatch[3], rgb_ngebcd[3];
sscanf(color_nbcd.c_str() + 1, "%02x%02x%02x", &rgb_nbcd[0], &rgb_nbcd[1], &rgb_nbcd[2]);
sscanf(color_nmatch.c_str() + 1, "%02x%02x%02x", &rgb_nmatch[0], &rgb_nmatch[1], &rgb_nmatch[2]);
sscanf(color_ngebcd.c_str() + 1, "%02x%02x%02x", &rgb_ngebcd[0], &rgb_ngebcd[1], &rgb_ngebcd[2]);
uint64_t width = (uint64_t)(ceil((double)(xmax - xmin + 1.0) / (double)coord_per_pixel));
uint64_t height = (uint64_t)(ceil((double)(ymax - ymin + 1.0) / (double)coord_per_pixel));
if ( width == 0 || height == 0 )
error("Invalid width/height: %llu/%llu", width, height);
else
notice("Detected image size is (%llu x %llu)", width, height);
// create and image
cimg_library::CImg<unsigned char> image(width, height, 1, 3, 0);
// read the nbcd file
int32_t intervals = 1000000;
if ( !nbcdf.empty() ) {
tsv_reader nbcd(nbcdf.c_str());
uint64_t nlines = 0;
uint64_t npx_nbcd = 0;
notice("Processing %s...", nbcdf.c_str());
while ( nbcd.read_line() ) {
if ( nbcd.nfields <= icol_x_nbcd || nbcd.nfields <= icol_y_nbcd )
error("Input file %s does not have enough columns - only %d", nbcdf.c_str(), nbcd.nfields);
++nlines;
if ( nlines % intervals == 0 )
notice("Processing %llu nbcd lines... %.3lf%% of pixels (n=%llu) have positive values", nlines, (double)npx_nbcd*100.0/(double)width/(double)height, npx_nbcd);
uint64_t x = nbcd.uint64_field_at(icol_x_nbcd);
uint64_t y = nbcd.uint64_field_at(icol_y_nbcd);
int32_t ix = (int32_t)((x - xmin) / coord_per_pixel);
int32_t iy = (int32_t)((y - ymin) / coord_per_pixel);
if ( ix > width || iy > height )
error("Out of range point detected (%llu -> %d, %llu ->%d): width = %llu, height = %llu, coord_per_pixel = %lf", x, ix, y, iy, width, height, coord_per_pixel);
bool zero = true;
for(int32_t i=0; i < 3; ++i) {
int32_t c = image(ix, iy, i);
if ( c > 0 ) zero = false;
if ( c + rgb_nbcd[i] > 255 ) c = 255;
else c += rgb_nbcd[i];
image(ix, iy, i) = c;
}
if ( zero ) ++npx_nbcd;
}
notice("Finished processing %llu nbcd lines... %.3lf%% of pixels (n=%llu) have positive values", nlines, (double)npx_nbcd*100.0/(double)width/(double)height, npx_nbcd);
}
// read the nmatch files
uint64_t nlines = 0;
for(int32_t i=0; i < nmatchfs.size(); ++i) {
tsv_reader tf(nmatchfs[i].c_str());
while ( tf.read_line() ) {
if ( tf.nfields <= icol_x_nmatch || tf.nfields <= icol_y_nmatch )
error("Input file %s does not have enough columns - only %d", nmatchfs[i].c_str(), tf.nfields);
++nlines;
if ( nlines % intervals == 0 )
notice("Processing %llu lines... from nmatch - currently reading %s", nlines, nmatchfs[i].c_str());
uint64_t x = tf.uint64_field_at(icol_x_nmatch);
uint64_t y = tf.uint64_field_at(icol_y_nmatch);
int32_t ix = (int32_t)((x - xmin)/ coord_per_pixel);
int32_t iy = (int32_t)((y - ymin) / coord_per_pixel);
if ( ix > width || iy > height )
error("Out of range point detected (%llu -> %d, %llu ->%d): width = %llu, height = %llu, coord_per_pixel = %lf", x, ix, y, iy, width, height, coord_per_pixel);
for(int32_t i=0; i < 3; ++i) {
int32_t c = image(ix, iy, i);
if ( c + rgb_nmatch[i] > 255 ) c = 255;
else c += rgb_nmatch[i];
image(ix, iy, i) = c;
}
}
notice("Finished processing %llu lines from nmatch", nlines);
}
// read the nge barcode files
if ( ! ngebcdf.empty() ) {
nlines = 0;
tsv_reader tf(ngebcdf.c_str());
while ( tf.read_line() ) {
if ( tf.nfields <= icol_x_ngebcd || tf.nfields <= icol_y_ngebcd || tf.nfields <= icol_gene_ngebcd )
error("Input file %s does not have enough columns - only %d", ngebcdf.c_str(), tf.nfields);
++nlines;
if ( nlines % intervals == 0 )
notice("Processing %llu lines... from nmatch - currently reading %s", nlines, ngebcdf.c_str());
double x = tf.double_field_at(icol_x_ngebcd);
double y = tf.double_field_at(icol_y_ngebcd);
int32_t ix = (int32_t)((x - xmin)/ coord_per_pixel);
int32_t iy = (int32_t)((y - ymin) / coord_per_pixel);
const char* s = tf.str_field_at(icol_gene_ngebcd);
for(int32_t i=0; i < isubcol_gene_ngebcd; ++i) {
s = strchr(s, ',') + 1;
}
int32_t gene_count = atoi(s);
if ( ix > width || iy > height )
error("Out of range point detected (%llu -> %d, %llu ->%d): width = %llu, height = %llu, coord_per_pixel = %lf", x, ix, y, iy, width, height, coord_per_pixel);
for(int32_t i=0; i < 3; ++i) {
int32_t c = image(ix, iy, i);
if ( c + rgb_ngebcd[i] * gene_count > 255 ) c = 255;
else c += ( rgb_ngebcd[i] * gene_count );
image(ix, iy, i) = c;
}
}
notice("Finished processing %llu lines... from %s", nlines, ngebcdf.c_str());
}
image.save_png(outf.c_str());
return 0;
}