Simple scatter method in 2d picture(Qt)
Result:
grayMap:
MathTools:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
// // Created by Administrator on 2017/8/17. // #ifndef QTSCATTER_MATHTOOLS_H #define QTSCATTER_MATHTOOLS_H #include <string> #include <vector> #include <iostream> #include <sstream> using namespace std; namespace TopVertex { class GLY_MATH { public: template<typename T> static T min(T a, T b) { if (a > b) { return b; } else { return a; } } template<typename T> static T max(T a, T b) { if (a > b) { return a; } else { return b; } } template<typename T> static bool zero_compare(T a, double tol = 0.00001) { return a >= -tol && a <= tol; } // DO NOT USE THIS FIT TO FIT VECTOR VALUE template<typename T> static T fit(T var, T omin, T omax, T nmin, T nmax) { T d = omax - omin; if (zero_compare(d)) { return (nmin + nmax) * 0.5; } if (omin < omax) { if (var < omin) return nmin; if (var > omax) return nmax; } else { if (var < omax) return nmax; if (var > omin) return nmin; } return nmin + (nmax - nmin) * (var - omin) / d; } //return -1 to 1 template<typename T> static T fit_negate(T var, T omin, T omax) { return fit(var, omin, omax, -1.0, 1.0); } //string split static std::vector<std::string> split_string(std::string &inputString, char &split_char) { std::stringstream ss(inputString); std::string sub_str; std::vector<std::string> sp_strPath; sp_strPath.clear(); while (getline(ss, sub_str, split_char)) { sp_strPath.push_back(sub_str); } return sp_strPath; } //value to string template<typename T> // T must be a value int/float/double static std::string value_to_str(T &value) { std::ostringstream os; os << value; return os.str(); } static int wang_inthash(int key) { // From http://www.concentric.net/~Ttwang/tech/inthash.htm key += ~(key << 16); key ^= (key >> 5); key += (key << 3); key ^= (key >> 13); key += ~(key << 9); key ^= (key >> 17); return key; } static int fastRandomInt(int seed) { int nseed = seed * 1664525+0XFFFFFFF; return wang_inthash(nseed); } static float fastRandom01(int seed) { return float(fastRandomInt(seed) % 1000000) / 1000000.0f; } }; } #endif //QTSCATTER_MATHTOOLS_H
点位移类,用于后续动画
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
// // Created by Administrator on 2017/8/18. // #ifndef QTSCATTER_POINTMOTION_H #define QTSCATTER_POINTMOTION_H namespace TopVertex { // our default motion template <typename T> class PolicyMotion { public: template <typename addVal> static void advect(T &point,const addVal &val,int loopId = 0) { // not implement } }; // per step motion template <typename opType=int, template <typename> class Policy = PolicyMotion> class Motion { public: template <typename T> static void advect_motion(T begin, T end,float time) { int loopId = 0; while(begin!= end) { Policy<opType>::advect(*begin,time,loopId); begin++; loopId++; } } }; } #endif //QTSCATTER_POINTMOTION_H
QImage分析像素:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
// // Created by Administrator on 2017/8/17. // #ifndef QTSCATTER_IMAGEPARSE_H #define QTSCATTER_IMAGEPARSE_H #include <QImage> #include <QString> #include <QPointF> #include <QVector> #include <QDebug> #include <vector> namespace TopVertex { class ImageParse { public: enum PARSETYPE{UNIFORM=0x00,STEP=0x01}; ImageParse() = default; explicit ImageParse(const QString &path); ImageParse(const QString &path,const int &ScatterNum); // parse image to data void parse(PARSETYPE parseFlag=UNIFORM); // our pixels positions std::vector<QPointF> &getParsePixelsPos(); // load image inline void loadImage(const QString &path) { if(!mImage.load(path)) qDebug() << "Image load " << path << " error"; } // set scatter num inline void setScatterNum(int pts) { mScatterNum = pts; } // get image QImage &getImage(){return mImage;} private: // Store image to parse QImage mImage; void parseUniform(); void parseStep(); private: // store our data in here std::vector<QPointF> mPixelsPos; // read Image path QString mImagePath; // scatter counts int mScatterNum; }; } #endif //QTSCATTER_IMAGEPARSE_H
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
// // Created by Administrator on 2017/8/17. // #include "ImageParse.h" #include <QDebug> #include <QColor> #include <algorithm> #include "MathTools.h" #include "PointMotion.h" // some function move points namespace TopVertex { // our default motion template <typename T> class randomMotion { public: template <typename addVal> static void advect(T &point,const addVal &val,int loopId) { auto rd01 = GLY_MATH::fastRandom01(loopId+1000); auto fitvar = GLY_MATH::fit<float>(rd01,0,1,-1,1); // random move 1 pixel pos point.rx() += fitvar*2; point.ry() += fitvar*2; } }; } // some function move points using namespace TopVertex; ImageParse::ImageParse(const QString &path): mImagePath(path), mScatterNum(0){ loadImage(path); } ImageParse::ImageParse(const QString &path,const int &ScatterNum): mImagePath(path), mScatterNum(ScatterNum){ loadImage(path); } std::vector<QPointF> & ImageParse::getParsePixelsPos() { return mPixelsPos; } void ImageParse::parse(PARSETYPE parseFlag) { if(parseFlag == UNIFORM) parseUniform(); else parseStep(); } template <typename T> static void RandomSelectByCount(int count, T &cont, T &desCont) { for(int k = 0;k<count;k++) { auto rd01 = GLY_MATH::fastRandom01(k); auto numChoice = abs(int(rd01 * cont.size())-1) ; desCont.emplace_back(cont[numChoice]); } } void ImageParse::parseUniform() { qDebug() << "w/h:"<<mImage.width() << " "<< mImage.height() ; auto wdt = mImage.width(); auto hdt = mImage.height(); vector<QPointF> storePos; for(int ht = 0 ; ht < hdt; ht ++) { for(int wt = 0; wt< wdt ; wt ++) { QColor rgb = QColor(mImage.pixel(wt,ht)); if (rgb.red()<=10) continue; storePos.emplace_back(QPointF(wt,ht)); } } RandomSelectByCount(mScatterNum,storePos,mPixelsPos); Motion<QPointF,randomMotion>::advect_motion(mPixelsPos.begin(),mPixelsPos.end(),0.0); } void ImageParse::parseStep() { auto wdt = mImage.width(); auto hdt = mImage.height(); int count_10_50 = floor(mScatterNum * 0.10); int count_50_100 = floor(mScatterNum * 0.15); int count_100_200 = floor(mScatterNum * 0.35); int count_200_255 = floor(mScatterNum * 0.45); qDebug() << "10-50 " << count_10_50; qDebug() << "50-100 " << count_50_100; qDebug() << "100-200 " << count_100_200; qDebug() << "200-255 " << count_200_255; vector<QPointF> part1; vector<QPointF> part2; vector<QPointF> part3; vector<QPointF> part4; for(int ht = 0 ; ht < hdt; ht ++) { for (int wt = 0; wt < wdt; wt++) { QColor rgb = QColor(mImage.pixel(wt, ht)); if (rgb.red() <= 10) continue; if(rgb.red() >=11 && rgb.red()<50) { part1.emplace_back(QPointF(wt,ht)); } if(rgb.red() >=50 && rgb.red()<100) { part2.emplace_back(QPointF(wt,ht)); } if(rgb.red() >=100 && rgb.red()<200) { part3.emplace_back(QPointF(wt,ht)); } if(rgb.red() >=200 && rgb.red()<=255) { part4.emplace_back(QPointF(wt,ht)); } } } RandomSelectByCount(count_10_50, part1, mPixelsPos); RandomSelectByCount(count_50_100, part2, mPixelsPos); RandomSelectByCount(count_100_200, part3, mPixelsPos); RandomSelectByCount(count_200_255, part4, mPixelsPos); // random move points Motion<QPointF,randomMotion>::advect_motion(mPixelsPos.begin(),mPixelsPos.end(),0.0); }
显示窗口:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
// // Created by Administrator on 2017/8/17. // #ifndef QTSCATTER_VIDEOWIDGET_H #define QTSCATTER_VIDEOWIDGET_H #include <QWidget> #include "ImageParse.h" #include <QResizeEvent> namespace TopVertex { class VideoWidget:public QWidget { Q_OBJECT public: explicit VideoWidget(QWidget *parent = nullptr):QWidget(parent) { resize(1280,720); setMinimumSize(QSize(50,50)); mImageParse.loadImage("./gray4.jpg"); mImageParse.setScatterNum(1000); mImageParse.parse(ImageParse::STEP); // mainwindow background picture mBgImage.load("./dp.jpg"); mInitW = width(); mInitH = height(); } private: ImageParse mImageParse; int mInitW; int mInitH; QImage mBgImage; protected: void paintEvent(QPaintEvent *e) override ; void resizeEvent(QResizeEvent *e) override; }; } #endif //QTSCATTER_VIDEOWIDGET_H
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
// // Created by Administrator on 2017/8/17. // #include "VideoWidget.h" #include <algorithm> #include "MathTools.h" using namespace TopVertex; #include <QPainter> using namespace std; template <typename T> static void paintGlow(QPainter &painter, QPen &pen, T &cont, float glowRadius = 1.2, float glowTint = 1, int maxSample=10) { for(int i=0;i<maxSample;i++) { auto alpha = (maxSample-i) * glowTint; pen.setColor(QColor(255,0,0,alpha)); pen.setWidthF(i * glowRadius); painter.setPen(pen); auto iter = cont.begin(); for_each(iter,cont.end(),[&painter](const QPointF&pf){painter.drawPoint(pf);}); } } void VideoWidget::paintEvent(QPaintEvent *e) { QPainter painter(this); painter.drawImage(this->rect(),mBgImage); painter.setRenderHint(QPainter::HighQualityAntialiasing); QPen pen(QColor(255,0,0,255)); pen.setWidthF(1.0); painter.setBrush(Qt::blue); painter.setPen(pen); auto &pos = mImageParse.getParsePixelsPos(); auto iter = pos.begin(); for_each(iter,pos.end(),[&painter](const QPointF&pf){painter.drawPoint(pf);}); paintGlow(painter,pen,mImageParse.getParsePixelsPos()); } void VideoWidget::resizeEvent(QResizeEvent *e) { // orig image width height auto iw = mInitW; auto ih = mInitH; // current width and height auto ww = this->width(); auto wh = this->height(); auto &pos = mImageParse.getParsePixelsPos(); auto iter = pos.begin(); auto resize = [&ww,&wh,&iw,&ih](QPointF&pf) { auto newx = GLY_MATH::fit<float>(float(pf.x()),0,iw,0,ww); auto newy = GLY_MATH::fit<float>(float(pf.y()),0,ih,0,wh); pf.rx() = newx; pf.ry() = newy; }; for_each(iter,pos.end(),resize); mInitW = width(); mInitH = height(); }
main.cpp
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include <QApplication> #include "ImageParse.h" #include <QDebug> #include "VideoWidget.h" using namespace TopVertex; int main(int argc, char *argv[]) { QApplication a(argc, argv); VideoWidget w; w.show(); return a.exec(); }