Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. How to improve anti-aliasing?
QtWS25 Last Chance

How to improve anti-aliasing?

Scheduled Pinned Locked Moved Solved General and Desktop
4 Posts 2 Posters 2.1k Views
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • C Offline
    C Offline
    CJha
    wrote on 28 Aug 2020, 12:51 last edited by CJha
    #1

    Hi! I am trying to make a line plot in QWidget. To obtain the line plot I map the X and Y points of my data point to its correct position in QWidget and then draw a line using QPainter's drawLine() function connecting previous point to the current point. The data points are huge (in tens or hundreds of thousands). What I get is the following:

    Aliased:
    Aliased.PNG

    Anti-alised:
    AntiAliased.PNG
    In both of the above examples, the pen width is 1 and 100,000 data points are plotted in a sine wave with frequency 10 and amplitude 1. There is one data point at the center which is of value 2 which stands out, this is because I need to accommodate spikes in my plot. Since I need to accommodate spikes, I cannot use any kind of algorithm based smoothing (such as using Bezier Curves). This leaves me with options such as anti-alising and painting tricks, but as you can see above anti-alising doesn't produce a good result. I have tried the following as well:

    • Using double precision instead of integer precision for mapping the X and Y data points to it's correct position on QWidget. This significantly increases the calculation time but the final plot looks the same as if I would have done the calculation with integer precision.
    • Painting my line plot on a QImage 4 times the size of the plot area, then applying anti-alising to it and then shrink it to correct size. This also increases calculation time significantly but the final plot again looks the same as above.

    I don't think using OpenGL Paint Engine is a good idea as explained in this QOpenGLPaintDevice Class document. Is there any way I can improve the rendering of my line plots?

    C 1 Reply Last reply 31 Aug 2020, 18:07
    0
    • C CJha
      28 Aug 2020, 12:51

      Hi! I am trying to make a line plot in QWidget. To obtain the line plot I map the X and Y points of my data point to its correct position in QWidget and then draw a line using QPainter's drawLine() function connecting previous point to the current point. The data points are huge (in tens or hundreds of thousands). What I get is the following:

      Aliased:
      Aliased.PNG

      Anti-alised:
      AntiAliased.PNG
      In both of the above examples, the pen width is 1 and 100,000 data points are plotted in a sine wave with frequency 10 and amplitude 1. There is one data point at the center which is of value 2 which stands out, this is because I need to accommodate spikes in my plot. Since I need to accommodate spikes, I cannot use any kind of algorithm based smoothing (such as using Bezier Curves). This leaves me with options such as anti-alising and painting tricks, but as you can see above anti-alising doesn't produce a good result. I have tried the following as well:

      • Using double precision instead of integer precision for mapping the X and Y data points to it's correct position on QWidget. This significantly increases the calculation time but the final plot looks the same as if I would have done the calculation with integer precision.
      • Painting my line plot on a QImage 4 times the size of the plot area, then applying anti-alising to it and then shrink it to correct size. This also increases calculation time significantly but the final plot again looks the same as above.

      I don't think using OpenGL Paint Engine is a good idea as explained in this QOpenGLPaintDevice Class document. Is there any way I can improve the rendering of my line plots?

      C Offline
      C Offline
      CJha
      wrote on 31 Aug 2020, 18:07 last edited by
      #4

      @CJha Found a solution. This works quite fast, 100k data points plotted in around 15 - 20 ms.

      void mainWindow::paintEvent(QPaintEvent *event)
      {
          /* Constructing Line Plot */
          double numOfPoints{100000}; // Data Length is 100 Thousand
          QVector<double> data; // Data Vector of 100 Thousand
          for(int ii = 0; ii < numOfPoints; ++ii)
          {
              // Sine Wave: Amplitude = 1; Frequency = 10
              data << (1 * sin (2 * M_PI * 5 * ii/numOfPoints));
          }
          int midPoint = round(numOfPoints/2);
          data[midPoint] = 2; // Adding a single point of amplitude 2 at 50 thousandth data point
          double dataMin{-1}; // Minimum amplitude of data
          double dataMax{2}; // Maximum amplitude of data
      
          /* Setting up Painter */
          QPainter p(this);
          p.setPen(Qt::blue);
          p.setRenderHint(QPainter::Antialiasing);
          QPolygonF polyline; // To draw lines to connect two points with different x-coordinates
          QPainterPath path; // To draw abstract lines with same x-coordinate but different y-coordinates
      
          /* NOTE: QPainter path is used because it has a moveTo functionality i.e. path's coordinates can be changed without creating a line from previous coordinate to new one. Same is not true for QPolygonF and so it is used to draw the continuous line */
      
          /************************************************ 
           ***** Mapping Data to rect() of mainWindow *****
           ************************************************/
          QVector<int> py; // Point Y
          QVector<int> sy; // Serial Y
          QVector<int> sySize; // Sizes of sy
          QVector<double> val; // Y Value for different groups of data
          int x_cur{0}; // x-value for current data set
          int x_prv{0}; // x-value for previous data set
          int y{0}; 
          int majy{0}; // Major Y i.e. the value which QPolygonF will follow
          double miny{0}; // Minimum Y: Start Point of QPainterPath
          double maxy{0}; // Minimum Y: End Point of QPainterPath
          int idy{0}; // ID to identity Value of Major Y
      
          /* This only works for data size larger than width of screen. For data size smaller than screen a much simpler approach is used but is not shown here */
          if(data.size() > width())
          {
              path.clear();
              for(int ii = 0; ii < (width() + 1); ++ii)
              {
                  py.clear();
                  x_cur = round(ii * (numOfPoints/width()));
                  if(ii != 0)
                  {
                      for(int jj = x_prv; jj < x_cur; ++jj)
                      {
                          y = round(height() * (dataMin + (dataMax - dataMin) - data[jj]) / (dataMax - dataMin));
                          if(!py.contains(y)) { py << y; }
                      }
                      if(py.size() == 1) { polyline << QPointF((ii - 1.0), py[0]); }
                      else
                      {
                          std::sort(py.begin(), py.end());
                          sy.clear();
                          sySize.clear();
                          val.clear();
                          majy = 0;
                          miny = 0.0;
                          maxy = 0.0;
                          idy = 0;
                          for(int jj = 0; jj < py.size(); ++jj)
                          {
                              if(jj == 0) { sy << py[jj]; }
                              else
                              {
                                  if(py[jj] == (py[jj - 1] + 1)) { sy << py[jj]; }
                                  else
                                  {
                                      double newVal{0.0};
                                      for(int kk = 0; kk < sy.size(); ++kk)
                                      {
                                          newVal = newVal + sy[kk];
                                      }
                                      val.append(newVal / sy.size());
                                      sySize << sy.size();
                                      sy.clear();
                                      sy << py[jj];
                                  }
                              }
                          }
                          double newVal{0.0};
                          for(int kk = 0; kk < sy.size(); ++kk)
                          {
                              newVal = newVal + sy[kk];
                          }
                          val.append(newVal / sy.size());
                          sySize << sy.size();
                          if (val.size() == 1) { polyline << QPointF((ii - 1.0), val[0]); }
                          else
                          {
                              for(int kk = 0; kk < sySize.size(); ++kk)
                              {
                                  if(sySize[kk] > majy)
                                  {
                                      majy = sySize[kk];
                                      idy = kk;
                                  }
                              }
                              auto result = std::minmax_element(val.begin(), val.end());
                              miny = *result.first;
                              maxy = *result.second;
                              polyline << QPointF((ii - 1.0), val[idy]);
                              path.moveTo(QPointF((ii - 1.0), miny));
                              path.lineTo(QPointF((ii - 1.0), maxy));
                          }
                      }
                  }
                  x_prv = x_cur;
              }
          }
          p.drawPolyline(polyline);
          p.drawPath(path);
      }
      

      At the end this is what I get:

      AliasedSolved.PNG

      1 Reply Last reply
      4
      • M Offline
        M Offline
        mrjj
        Lifetime Qt Champion
        wrote on 28 Aug 2020, 17:27 last edited by
        #2

        Hi
        I could not reproduce it as such.

        alt text

        Maybe your pixels are a bit off but you said you used doubles and i assume you then
        also used the QPointF version ?

        void MainWindow::paintEvent(QPaintEvent *event)
        {
            QPainter p(this);
            p.setPen( Qt::blue );
            p.setRenderHint( QPainter::Antialiasing );
            QPolygonF polyline;
            int h_div_2 = height() / 2;
            for( int x = 0; x < width(); x++)
            {
            float y = h_div_2 + sin( x * 6.28f / width() ) * h_div_2;
            polyline << QPointF( x, y );
            }
            p.drawPolyline( polyline );
        }
        
        C 1 Reply Last reply 30 Aug 2020, 13:36
        2
        • M mrjj
          28 Aug 2020, 17:27

          Hi
          I could not reproduce it as such.

          alt text

          Maybe your pixels are a bit off but you said you used doubles and i assume you then
          also used the QPointF version ?

          void MainWindow::paintEvent(QPaintEvent *event)
          {
              QPainter p(this);
              p.setPen( Qt::blue );
              p.setRenderHint( QPainter::Antialiasing );
              QPolygonF polyline;
              int h_div_2 = height() / 2;
              for( int x = 0; x < width(); x++)
              {
              float y = h_div_2 + sin( x * 6.28f / width() ) * h_div_2;
              polyline << QPointF( x, y );
              }
              p.drawPolyline( polyline );
          }
          
          C Offline
          C Offline
          CJha
          wrote on 30 Aug 2020, 13:36 last edited by
          #3

          @mrjj Thanks for the reply. You are constructing the data based on the size of your MainWindow i.e. you are using the width() of the MainWindow in your calculation of sine wave, this will always produce a smooth curve because it will calculate the exact y-coordinate for each x-coordinate of your MainWindow. I have to plot the recorded data with high sampling frequency i.e. the data that I am trying to plot is independent of the width() and height() of my mainWindow and so I have to map it to my mainWindow:

          void mainWindow::paintEvent(QPaintEvent *event)
          {
              /* Constructing Demo Line Plot */
              int numOfPoints{100000}; // Data Length is 100 Thousand
              QVector<double> data; // Data Vector of 100 Thousand
              for(int ii = 0; ii < numOfPoints; ++ii)
              {
                  data << (1 * sin (2 * M_PI * 5 * ii/numOfPoints)); // Sine Wave: Amplitude = 1; Frequency = 5
              }
              data[50000] = 2; // Adding a single point of amplitude 2 at 50 thousandth data point
              double dataMin{-1.0}; // Minimum amplitude of data
              double dataMax{2.0}; // Maximum amplitude of data
          
              /* Setting up Painter */
              QPainter p(this);
              p.setPen(Qt::blue);
              p.setRenderHint(QPainter::Antialiasing);
              QPolygonF polyline;
          
              /* Mapping data to rect() of mainWindow */
              for(int ii = 0; ii < numOfPoints; ++ii)
              {
                  double px = width() * ii / numOfPoints;
                  double py = height() * (dataMin + (dataMax - dataMin) - data[ii]) / (dataMax - dataMin);
                  polyline << QPointF(px, py);
              }
              p.drawPolyline(polyline);
          }
          

          This produces the following result:

          AntiAliasedNew.PNG

          The lines are not smooth, and I don't know how to fix it.

          1 Reply Last reply
          0
          • C CJha
            28 Aug 2020, 12:51

            Hi! I am trying to make a line plot in QWidget. To obtain the line plot I map the X and Y points of my data point to its correct position in QWidget and then draw a line using QPainter's drawLine() function connecting previous point to the current point. The data points are huge (in tens or hundreds of thousands). What I get is the following:

            Aliased:
            Aliased.PNG

            Anti-alised:
            AntiAliased.PNG
            In both of the above examples, the pen width is 1 and 100,000 data points are plotted in a sine wave with frequency 10 and amplitude 1. There is one data point at the center which is of value 2 which stands out, this is because I need to accommodate spikes in my plot. Since I need to accommodate spikes, I cannot use any kind of algorithm based smoothing (such as using Bezier Curves). This leaves me with options such as anti-alising and painting tricks, but as you can see above anti-alising doesn't produce a good result. I have tried the following as well:

            • Using double precision instead of integer precision for mapping the X and Y data points to it's correct position on QWidget. This significantly increases the calculation time but the final plot looks the same as if I would have done the calculation with integer precision.
            • Painting my line plot on a QImage 4 times the size of the plot area, then applying anti-alising to it and then shrink it to correct size. This also increases calculation time significantly but the final plot again looks the same as above.

            I don't think using OpenGL Paint Engine is a good idea as explained in this QOpenGLPaintDevice Class document. Is there any way I can improve the rendering of my line plots?

            C Offline
            C Offline
            CJha
            wrote on 31 Aug 2020, 18:07 last edited by
            #4

            @CJha Found a solution. This works quite fast, 100k data points plotted in around 15 - 20 ms.

            void mainWindow::paintEvent(QPaintEvent *event)
            {
                /* Constructing Line Plot */
                double numOfPoints{100000}; // Data Length is 100 Thousand
                QVector<double> data; // Data Vector of 100 Thousand
                for(int ii = 0; ii < numOfPoints; ++ii)
                {
                    // Sine Wave: Amplitude = 1; Frequency = 10
                    data << (1 * sin (2 * M_PI * 5 * ii/numOfPoints));
                }
                int midPoint = round(numOfPoints/2);
                data[midPoint] = 2; // Adding a single point of amplitude 2 at 50 thousandth data point
                double dataMin{-1}; // Minimum amplitude of data
                double dataMax{2}; // Maximum amplitude of data
            
                /* Setting up Painter */
                QPainter p(this);
                p.setPen(Qt::blue);
                p.setRenderHint(QPainter::Antialiasing);
                QPolygonF polyline; // To draw lines to connect two points with different x-coordinates
                QPainterPath path; // To draw abstract lines with same x-coordinate but different y-coordinates
            
                /* NOTE: QPainter path is used because it has a moveTo functionality i.e. path's coordinates can be changed without creating a line from previous coordinate to new one. Same is not true for QPolygonF and so it is used to draw the continuous line */
            
                /************************************************ 
                 ***** Mapping Data to rect() of mainWindow *****
                 ************************************************/
                QVector<int> py; // Point Y
                QVector<int> sy; // Serial Y
                QVector<int> sySize; // Sizes of sy
                QVector<double> val; // Y Value for different groups of data
                int x_cur{0}; // x-value for current data set
                int x_prv{0}; // x-value for previous data set
                int y{0}; 
                int majy{0}; // Major Y i.e. the value which QPolygonF will follow
                double miny{0}; // Minimum Y: Start Point of QPainterPath
                double maxy{0}; // Minimum Y: End Point of QPainterPath
                int idy{0}; // ID to identity Value of Major Y
            
                /* This only works for data size larger than width of screen. For data size smaller than screen a much simpler approach is used but is not shown here */
                if(data.size() > width())
                {
                    path.clear();
                    for(int ii = 0; ii < (width() + 1); ++ii)
                    {
                        py.clear();
                        x_cur = round(ii * (numOfPoints/width()));
                        if(ii != 0)
                        {
                            for(int jj = x_prv; jj < x_cur; ++jj)
                            {
                                y = round(height() * (dataMin + (dataMax - dataMin) - data[jj]) / (dataMax - dataMin));
                                if(!py.contains(y)) { py << y; }
                            }
                            if(py.size() == 1) { polyline << QPointF((ii - 1.0), py[0]); }
                            else
                            {
                                std::sort(py.begin(), py.end());
                                sy.clear();
                                sySize.clear();
                                val.clear();
                                majy = 0;
                                miny = 0.0;
                                maxy = 0.0;
                                idy = 0;
                                for(int jj = 0; jj < py.size(); ++jj)
                                {
                                    if(jj == 0) { sy << py[jj]; }
                                    else
                                    {
                                        if(py[jj] == (py[jj - 1] + 1)) { sy << py[jj]; }
                                        else
                                        {
                                            double newVal{0.0};
                                            for(int kk = 0; kk < sy.size(); ++kk)
                                            {
                                                newVal = newVal + sy[kk];
                                            }
                                            val.append(newVal / sy.size());
                                            sySize << sy.size();
                                            sy.clear();
                                            sy << py[jj];
                                        }
                                    }
                                }
                                double newVal{0.0};
                                for(int kk = 0; kk < sy.size(); ++kk)
                                {
                                    newVal = newVal + sy[kk];
                                }
                                val.append(newVal / sy.size());
                                sySize << sy.size();
                                if (val.size() == 1) { polyline << QPointF((ii - 1.0), val[0]); }
                                else
                                {
                                    for(int kk = 0; kk < sySize.size(); ++kk)
                                    {
                                        if(sySize[kk] > majy)
                                        {
                                            majy = sySize[kk];
                                            idy = kk;
                                        }
                                    }
                                    auto result = std::minmax_element(val.begin(), val.end());
                                    miny = *result.first;
                                    maxy = *result.second;
                                    polyline << QPointF((ii - 1.0), val[idy]);
                                    path.moveTo(QPointF((ii - 1.0), miny));
                                    path.lineTo(QPointF((ii - 1.0), maxy));
                                }
                            }
                        }
                        x_prv = x_cur;
                    }
                }
                p.drawPolyline(polyline);
                p.drawPath(path);
            }
            

            At the end this is what I get:

            AliasedSolved.PNG

            1 Reply Last reply
            4

            4/4

            31 Aug 2020, 18:07

            • Login

            • Login or register to search.
            4 out of 4
            • First post
              4/4
              Last post
            0
            • Categories
            • Recent
            • Tags
            • Popular
            • Users
            • Groups
            • Search
            • Get Qt Extensions
            • Unsolved