annotate data/model/EditableDenseThreeDimensionalModel.cpp @ 534:6038cb6fcd30

* Some simple compression for dense 3d models that actually represent multirate data such as wavelet transform outputs. If a column has many elements at top or bottom that are the same as those of an earlier column, store a reference to that one and truncate the duplicate values.
author Chris Cannam
date Fri, 23 Jan 2009 13:31:51 +0000
parents 55ad231c9db7
children 3ccf48fb81d6
rev   line source
Chris@152 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@152 2
Chris@152 3 /*
Chris@152 4 Sonic Visualiser
Chris@152 5 An audio file viewer and annotation editor.
Chris@152 6 Centre for Digital Music, Queen Mary, University of London.
Chris@202 7 This file copyright 2006 Chris Cannam and QMUL.
Chris@152 8
Chris@152 9 This program is free software; you can redistribute it and/or
Chris@152 10 modify it under the terms of the GNU General Public License as
Chris@152 11 published by the Free Software Foundation; either version 2 of the
Chris@152 12 License, or (at your option) any later version. See the file
Chris@152 13 COPYING included with this distribution for more information.
Chris@152 14 */
Chris@152 15
Chris@152 16 #include "EditableDenseThreeDimensionalModel.h"
Chris@152 17
Chris@478 18 #include "base/LogRange.h"
Chris@478 19
Chris@152 20 #include <QTextStream>
Chris@387 21 #include <QStringList>
Chris@387 22
Chris@181 23 #include <iostream>
Chris@181 24
Chris@256 25 #include <cmath>
Chris@534 26 #include <cassert>
Chris@256 27
Chris@152 28 EditableDenseThreeDimensionalModel::EditableDenseThreeDimensionalModel(size_t sampleRate,
Chris@152 29 size_t resolution,
Chris@152 30 size_t yBinCount,
Chris@152 31 bool notifyOnAdd) :
Chris@152 32 m_sampleRate(sampleRate),
Chris@152 33 m_resolution(resolution),
Chris@152 34 m_yBinCount(yBinCount),
Chris@152 35 m_minimum(0.0),
Chris@152 36 m_maximum(0.0),
Chris@256 37 m_haveExtents(false),
Chris@152 38 m_notifyOnAdd(notifyOnAdd),
Chris@152 39 m_sinceLastNotifyMin(-1),
Chris@152 40 m_sinceLastNotifyMax(-1),
Chris@152 41 m_completion(100)
Chris@152 42 {
Chris@152 43 }
Chris@152 44
Chris@152 45 bool
Chris@152 46 EditableDenseThreeDimensionalModel::isOK() const
Chris@152 47 {
Chris@152 48 return true;
Chris@152 49 }
Chris@152 50
Chris@152 51 size_t
Chris@152 52 EditableDenseThreeDimensionalModel::getSampleRate() const
Chris@152 53 {
Chris@152 54 return m_sampleRate;
Chris@152 55 }
Chris@152 56
Chris@152 57 size_t
Chris@152 58 EditableDenseThreeDimensionalModel::getStartFrame() const
Chris@152 59 {
Chris@152 60 return 0;
Chris@152 61 }
Chris@152 62
Chris@152 63 size_t
Chris@152 64 EditableDenseThreeDimensionalModel::getEndFrame() const
Chris@152 65 {
Chris@152 66 return m_resolution * m_data.size() + (m_resolution - 1);
Chris@152 67 }
Chris@152 68
Chris@152 69 Model *
Chris@152 70 EditableDenseThreeDimensionalModel::clone() const
Chris@152 71 {
Chris@534 72 QMutexLocker locker(&m_mutex);
Chris@534 73
Chris@152 74 EditableDenseThreeDimensionalModel *model =
Chris@152 75 new EditableDenseThreeDimensionalModel
Chris@152 76 (m_sampleRate, m_resolution, m_yBinCount);
Chris@152 77
Chris@152 78 model->m_minimum = m_minimum;
Chris@152 79 model->m_maximum = m_maximum;
Chris@256 80 model->m_haveExtents = m_haveExtents;
Chris@152 81
Chris@152 82 for (size_t i = 0; i < m_data.size(); ++i) {
Chris@533 83 model->setColumn(i, m_data.at(i));
Chris@152 84 }
Chris@152 85
Chris@152 86 return model;
Chris@152 87 }
Chris@152 88
Chris@152 89 size_t
Chris@152 90 EditableDenseThreeDimensionalModel::getResolution() const
Chris@152 91 {
Chris@152 92 return m_resolution;
Chris@152 93 }
Chris@152 94
Chris@152 95 void
Chris@152 96 EditableDenseThreeDimensionalModel::setResolution(size_t sz)
Chris@152 97 {
Chris@152 98 m_resolution = sz;
Chris@152 99 }
Chris@152 100
Chris@152 101 size_t
Chris@182 102 EditableDenseThreeDimensionalModel::getWidth() const
Chris@182 103 {
Chris@182 104 return m_data.size();
Chris@182 105 }
Chris@182 106
Chris@182 107 size_t
Chris@182 108 EditableDenseThreeDimensionalModel::getHeight() const
Chris@152 109 {
Chris@152 110 return m_yBinCount;
Chris@152 111 }
Chris@152 112
Chris@152 113 void
Chris@182 114 EditableDenseThreeDimensionalModel::setHeight(size_t sz)
Chris@152 115 {
Chris@152 116 m_yBinCount = sz;
Chris@152 117 }
Chris@152 118
Chris@152 119 float
Chris@152 120 EditableDenseThreeDimensionalModel::getMinimumLevel() const
Chris@152 121 {
Chris@152 122 return m_minimum;
Chris@152 123 }
Chris@152 124
Chris@152 125 void
Chris@152 126 EditableDenseThreeDimensionalModel::setMinimumLevel(float level)
Chris@152 127 {
Chris@152 128 m_minimum = level;
Chris@152 129 }
Chris@152 130
Chris@152 131 float
Chris@152 132 EditableDenseThreeDimensionalModel::getMaximumLevel() const
Chris@152 133 {
Chris@152 134 return m_maximum;
Chris@152 135 }
Chris@152 136
Chris@152 137 void
Chris@152 138 EditableDenseThreeDimensionalModel::setMaximumLevel(float level)
Chris@152 139 {
Chris@152 140 m_maximum = level;
Chris@152 141 }
Chris@152 142
Chris@533 143 EditableDenseThreeDimensionalModel::Column
Chris@533 144 EditableDenseThreeDimensionalModel::getColumn(size_t index) const
Chris@152 145 {
Chris@152 146 QMutexLocker locker(&m_mutex);
Chris@534 147 if (index >= m_data.size()) return Column();
Chris@534 148 return expandAndRetrieve(index);
Chris@152 149 }
Chris@152 150
Chris@152 151 float
Chris@182 152 EditableDenseThreeDimensionalModel::getValueAt(size_t index, size_t n) const
Chris@152 153 {
Chris@534 154 Column c = getColumn(index);
Chris@534 155 if (n < c.size()) return s.at(n);
Chris@534 156 return m_minimum;
Chris@534 157 }
Chris@152 158
Chris@534 159 static int given = 0, stored = 0;
Chris@534 160
Chris@534 161 void
Chris@534 162 EditableDenseThreeDimensionalModel::truncateAndStore(size_t index,
Chris@534 163 const Column &values)
Chris@534 164 {
Chris@534 165 assert(index < m_data.size());
Chris@534 166
Chris@534 167 //std::cout << "truncateAndStore(" << index << ", " << values.size() << ")" << std::endl;
Chris@534 168
Chris@534 169 m_trunc[index] = 0;
Chris@534 170 if (index == 0 || values.size() != m_yBinCount) {
Chris@534 171 given += values.size();
Chris@534 172 stored += values.size();
Chris@534 173 m_data[index] = values;
Chris@534 174 return;
Chris@152 175 }
Chris@152 176
Chris@534 177 static int maxdist = 120;
Chris@534 178
Chris@534 179 bool known = false;
Chris@534 180 bool top = false;
Chris@534 181
Chris@534 182 int tdist = 1;
Chris@534 183 int ptrunc = m_trunc[index-1];
Chris@534 184 if (ptrunc < 0) {
Chris@534 185 top = false;
Chris@534 186 known = true;
Chris@534 187 tdist = -ptrunc + 1;
Chris@534 188 } else if (ptrunc > 0) {
Chris@534 189 top = true;
Chris@534 190 known = true;
Chris@534 191 tdist = ptrunc + 1;
Chris@534 192 }
Chris@534 193
Chris@534 194 Column p = expandAndRetrieve(index - tdist);
Chris@534 195 int h = m_yBinCount;
Chris@534 196
Chris@534 197 if (p.size() == h && tdist <= maxdist) {
Chris@534 198
Chris@534 199 int bcount = 0, tcount = 0;
Chris@534 200 if (!known || !top) {
Chris@534 201 for (int i = 0; i < h; ++i) {
Chris@534 202 if (values.at(i) == p.at(i)) ++bcount;
Chris@534 203 else break;
Chris@534 204 }
Chris@534 205 }
Chris@534 206 if (!known || top) {
Chris@534 207 for (int i = h; i > 0; --i) {
Chris@534 208 if (values.at(i-1) == p.at(i-1)) ++tcount;
Chris@534 209 else break;
Chris@534 210 }
Chris@534 211 }
Chris@534 212 if (!known) top = (tcount > bcount);
Chris@534 213
Chris@534 214 int limit = h / 4;
Chris@534 215 if ((top ? tcount : bcount) > limit) {
Chris@534 216
Chris@534 217 if (!top) {
Chris@534 218 Column tcol(h - bcount);
Chris@534 219 given += values.size();
Chris@534 220 stored += h - bcount;
Chris@534 221 for (int i = bcount; i < h; ++i) {
Chris@534 222 tcol[i - bcount] = values.at(i);
Chris@534 223 }
Chris@534 224 m_data[index] = tcol;
Chris@534 225 m_trunc[index] = -tdist;
Chris@534 226 //std::cout << "bottom " << bcount << " as col at " << -tdist << std::endl;
Chris@534 227 return;
Chris@534 228 } else {
Chris@534 229 Column tcol(h - tcount);
Chris@534 230 given += values.size();
Chris@534 231 stored += h - tcount;
Chris@534 232 for (int i = 0; i < h - tcount; ++i) {
Chris@534 233 tcol[i] = values.at(i);
Chris@534 234 }
Chris@534 235 m_data[index] = tcol;
Chris@534 236 m_trunc[index] = tdist;
Chris@534 237 //std::cout << "top " << tcount << " as col at " << -tdist << std::endl;
Chris@534 238 return;
Chris@534 239 }
Chris@534 240 }
Chris@534 241 }
Chris@534 242
Chris@534 243 given += values.size();
Chris@534 244 stored += values.size();
Chris@534 245
Chris@534 246 // std::cout << "given: " << given << ", stored: " << stored << " ("
Chris@534 247 // << ((float(stored) / float(given)) * 100.f) << "%)" << std::endl;
Chris@534 248
Chris@534 249 m_data[index] = values;
Chris@534 250 return;
Chris@534 251 }
Chris@534 252
Chris@534 253 EditableDenseThreeDimensionalModel::Column
Chris@534 254 EditableDenseThreeDimensionalModel::expandAndRetrieve(size_t index) const
Chris@534 255 {
Chris@534 256 assert(index < m_data.size());
Chris@534 257 Column c = m_data.at(index);
Chris@534 258 if (index == 0) {
Chris@534 259 return c;
Chris@534 260 }
Chris@534 261 int trunc = (int)m_trunc[index];
Chris@534 262 if (trunc == 0) {
Chris@534 263 return c;
Chris@534 264 }
Chris@534 265 bool top = true;
Chris@534 266 int tdist = trunc;
Chris@534 267 if (trunc < 0) { top = false; tdist = -trunc; }
Chris@534 268 Column p = expandAndRetrieve(index - tdist);
Chris@534 269 if (p.size() != m_yBinCount) {
Chris@534 270 std::cerr << "WARNING: EditableDenseThreeDimensionalModel::expandAndRetrieve: Trying to expand from incorrectly sized column" << std::endl;
Chris@534 271 }
Chris@534 272 if (top) {
Chris@534 273 for (int i = c.size(); i < p.size(); ++i) {
Chris@534 274 c.push_back(p.at(i));
Chris@534 275 }
Chris@534 276 } else {
Chris@534 277 for (int i = int(p.size()) - int(c.size()); i >= 0; --i) {
Chris@534 278 c.push_front(p.at(i));
Chris@534 279 }
Chris@534 280 }
Chris@534 281 return c;
Chris@152 282 }
Chris@152 283
Chris@152 284 void
Chris@182 285 EditableDenseThreeDimensionalModel::setColumn(size_t index,
Chris@182 286 const Column &values)
Chris@152 287 {
Chris@152 288 QMutexLocker locker(&m_mutex);
Chris@152 289
Chris@182 290 while (index >= m_data.size()) {
Chris@182 291 m_data.push_back(Column());
Chris@534 292 m_trunc.push_back(0);
Chris@152 293 }
Chris@152 294
Chris@152 295 bool allChange = false;
Chris@152 296
Chris@534 297 // if (values.size() > m_yBinCount) m_yBinCount = values.size();
Chris@439 298
Chris@152 299 for (size_t i = 0; i < values.size(); ++i) {
Chris@256 300 float value = values[i];
Chris@257 301 if (std::isnan(value) || std::isinf(value)) {
Chris@256 302 continue;
Chris@256 303 }
Chris@256 304 if (!m_haveExtents || value < m_minimum) {
Chris@256 305 m_minimum = value;
Chris@152 306 allChange = true;
Chris@152 307 }
Chris@256 308 if (!m_haveExtents || value > m_maximum) {
Chris@256 309 m_maximum = value;
Chris@152 310 allChange = true;
Chris@152 311 }
Chris@256 312 m_haveExtents = true;
Chris@152 313 }
Chris@152 314
Chris@534 315 truncateAndStore(index, values);
Chris@534 316
Chris@534 317 assert(values == expandAndRetrieve(index));
Chris@152 318
Chris@182 319 long windowStart = index;
Chris@182 320 windowStart *= m_resolution;
Chris@182 321
Chris@152 322 if (m_notifyOnAdd) {
Chris@152 323 if (allChange) {
Chris@152 324 emit modelChanged();
Chris@152 325 } else {
Chris@152 326 emit modelChanged(windowStart, windowStart + m_resolution);
Chris@152 327 }
Chris@152 328 } else {
Chris@152 329 if (allChange) {
Chris@152 330 m_sinceLastNotifyMin = -1;
Chris@152 331 m_sinceLastNotifyMax = -1;
Chris@152 332 emit modelChanged();
Chris@152 333 } else {
Chris@152 334 if (m_sinceLastNotifyMin == -1 ||
Chris@152 335 windowStart < m_sinceLastNotifyMin) {
Chris@152 336 m_sinceLastNotifyMin = windowStart;
Chris@152 337 }
Chris@152 338 if (m_sinceLastNotifyMax == -1 ||
Chris@152 339 windowStart > m_sinceLastNotifyMax) {
Chris@152 340 m_sinceLastNotifyMax = windowStart;
Chris@152 341 }
Chris@152 342 }
Chris@152 343 }
Chris@152 344 }
Chris@152 345
Chris@152 346 QString
Chris@152 347 EditableDenseThreeDimensionalModel::getBinName(size_t n) const
Chris@152 348 {
Chris@152 349 if (m_binNames.size() > n) return m_binNames[n];
Chris@152 350 else return "";
Chris@152 351 }
Chris@152 352
Chris@152 353 void
Chris@152 354 EditableDenseThreeDimensionalModel::setBinName(size_t n, QString name)
Chris@152 355 {
Chris@152 356 while (m_binNames.size() <= n) m_binNames.push_back("");
Chris@152 357 m_binNames[n] = name;
Chris@152 358 emit modelChanged();
Chris@152 359 }
Chris@152 360
Chris@152 361 void
Chris@152 362 EditableDenseThreeDimensionalModel::setBinNames(std::vector<QString> names)
Chris@152 363 {
Chris@152 364 m_binNames = names;
Chris@152 365 emit modelChanged();
Chris@152 366 }
Chris@152 367
Chris@478 368 bool
Chris@478 369 EditableDenseThreeDimensionalModel::shouldUseLogValueScale() const
Chris@478 370 {
Chris@534 371 QMutexLocker locker(&m_mutex);
Chris@534 372
Chris@533 373 QVector<float> sample;
Chris@533 374 QVector<int> n;
Chris@478 375
Chris@478 376 for (int i = 0; i < 10; ++i) {
Chris@478 377 size_t index = i * 10;
Chris@478 378 if (index < m_data.size()) {
Chris@533 379 const Column &c = m_data.at(index);
Chris@478 380 while (c.size() > sample.size()) {
Chris@478 381 sample.push_back(0.f);
Chris@478 382 n.push_back(0);
Chris@478 383 }
Chris@478 384 for (int j = 0; j < c.size(); ++j) {
Chris@533 385 sample[j] += c.at(j);
Chris@478 386 ++n[j];
Chris@478 387 }
Chris@478 388 }
Chris@478 389 }
Chris@478 390
Chris@478 391 if (sample.empty()) return false;
Chris@478 392 for (int j = 0; j < sample.size(); ++j) {
Chris@478 393 if (n[j]) sample[j] /= n[j];
Chris@478 394 }
Chris@478 395
Chris@533 396 return LogRange::useLogScale(sample.toStdVector());
Chris@478 397 }
Chris@478 398
Chris@152 399 void
Chris@333 400 EditableDenseThreeDimensionalModel::setCompletion(int completion, bool update)
Chris@152 401 {
Chris@152 402 if (m_completion != completion) {
Chris@152 403 m_completion = completion;
Chris@152 404
Chris@152 405 if (completion == 100) {
Chris@152 406
Chris@152 407 m_notifyOnAdd = true; // henceforth
Chris@152 408 emit modelChanged();
Chris@152 409
Chris@152 410 } else if (!m_notifyOnAdd) {
Chris@152 411
Chris@333 412 if (update &&
Chris@333 413 m_sinceLastNotifyMin >= 0 &&
Chris@152 414 m_sinceLastNotifyMax >= 0) {
Chris@152 415 emit modelChanged(m_sinceLastNotifyMin,
Chris@152 416 m_sinceLastNotifyMax + m_resolution);
Chris@152 417 m_sinceLastNotifyMin = m_sinceLastNotifyMax = -1;
Chris@152 418 } else {
Chris@152 419 emit completionChanged();
Chris@152 420 }
Chris@152 421 } else {
Chris@152 422 emit completionChanged();
Chris@152 423 }
Chris@152 424 }
Chris@152 425 }
Chris@152 426
Chris@318 427 QString
Chris@318 428 EditableDenseThreeDimensionalModel::toDelimitedDataString(QString delimiter) const
Chris@318 429 {
Chris@534 430 QMutexLocker locker(&m_mutex);
Chris@318 431 QString s;
Chris@318 432 for (size_t i = 0; i < m_data.size(); ++i) {
Chris@318 433 QStringList list;
Chris@533 434 for (size_t j = 0; j < m_data.at(i).size(); ++j) {
Chris@533 435 list << QString("%1").arg(m_data.at(i).at(j));
Chris@318 436 }
Chris@318 437 s += list.join(delimiter) + "\n";
Chris@318 438 }
Chris@318 439 return s;
Chris@318 440 }
Chris@318 441
Chris@152 442 void
Chris@152 443 EditableDenseThreeDimensionalModel::toXml(QTextStream &out,
Chris@314 444 QString indent,
Chris@314 445 QString extraAttributes) const
Chris@152 446 {
Chris@534 447 QMutexLocker locker(&m_mutex);
Chris@534 448
Chris@152 449 // For historical reasons we read and write "resolution" as "windowSize"
Chris@152 450
Chris@318 451 std::cerr << "EditableDenseThreeDimensionalModel::toXml" << std::endl;
Chris@318 452
Chris@314 453 Model::toXml
Chris@314 454 (out, indent,
Chris@314 455 QString("type=\"dense\" dimensions=\"3\" windowSize=\"%1\" yBinCount=\"%2\" minimum=\"%3\" maximum=\"%4\" dataset=\"%5\" %6")
Chris@152 456 .arg(m_resolution)
Chris@152 457 .arg(m_yBinCount)
Chris@152 458 .arg(m_minimum)
Chris@152 459 .arg(m_maximum)
Chris@152 460 .arg(getObjectExportId(&m_data))
Chris@152 461 .arg(extraAttributes));
Chris@152 462
Chris@152 463 out << indent;
Chris@152 464 out << QString("<dataset id=\"%1\" dimensions=\"3\" separator=\" \">\n")
Chris@152 465 .arg(getObjectExportId(&m_data));
Chris@152 466
Chris@152 467 for (size_t i = 0; i < m_binNames.size(); ++i) {
Chris@152 468 if (m_binNames[i] != "") {
Chris@152 469 out << indent + " ";
Chris@152 470 out << QString("<bin number=\"%1\" name=\"%2\"/>\n")
Chris@152 471 .arg(i).arg(m_binNames[i]);
Chris@152 472 }
Chris@152 473 }
Chris@152 474
Chris@152 475 for (size_t i = 0; i < m_data.size(); ++i) {
Chris@152 476 out << indent + " ";
Chris@152 477 out << QString("<row n=\"%1\">").arg(i);
Chris@533 478 for (size_t j = 0; j < m_data.at(i).size(); ++j) {
Chris@152 479 if (j > 0) out << " ";
Chris@533 480 out << m_data.at(i).at(j);
Chris@152 481 }
Chris@152 482 out << QString("</row>\n");
Chris@318 483 out.flush();
Chris@152 484 }
Chris@152 485
Chris@152 486 out << indent + "</dataset>\n";
Chris@152 487 }
Chris@152 488
Chris@152 489