Thông báo

Collapse
No announcement yet.

[Trợ Giúp] Giúp mình kiểm tra đoạn code xử lý ảnh.

Collapse
X
 
  • Lọc
  • Giờ
  • Show
Clear All
new posts

  • #16
    ________QT________

    QImage img("E:/IMG.bmp"); // load ảnh trên ổ E ( ảnh chụp màn hình 1280x1024 )
    QElapsedTimer tmr; // bộ đếm thời gian thực
    void MainWindow::on_pushButton_clicked()
    {
    int depth = img.depth()/8; // số byte / 1 pixel
    int stride=depth*img.width(); // số byte trên 1 line ngang
    int height = img.height(); // số line dữ liệu

    unsigned char** dataptr = new unsigned char*[height]; // tạo mảng con trỏ
    for(int i=0;i<height;i++)
    dataptr[i]=img.scanLine(i); // gán địa chỉ cho mỗi con trỏ
    qint64 time=0; // biến lưu thời gian thực thi
    tmr.restart(); // reset thời gian + bắt đầu tính thời gian
    for(int i=0;i<100;i++) // lặp 100 lần
    {
    for(int y=0;y<height;y++) // duyệt từng line trên ảnh
    {
    //duyệt từng điểm ảnh trên line
    for(int x=0;x<stride;x+=depth)
    {
    // Lúc trước dùng thế này chậm 7 lần
    //img.scanLine(y)[x]++; // tăng màu blue
    //img.scanLine(y)[x+1]++; // tăng màu green
    //img.scanLine(y)[x+2]++; // tăng màu red

    // bây giờ dùng thế này thì nhanh hơn C# khoảng 1.1 - 1.2 lần.
    dataptr[y][x]++; // tăng màu blue
    dataptr[y][x+1]++; // tăng màu green
    dataptr[y][x+2]++; // tăng màu red
    }
    }
    }
    time=tmr.elapsed(); // kết thúc, lấy thời gian thực thi.
    ui->lblDisplay->setPixmap(QPixmap::fromImage(img)); // hiển thị ảnh sau xử lý
    ui->lblTime->setText("Thời gian xử lý : " + QString::number(time) + " ms"); // hiển thị thời gian
    int fps = 100000/time; // tính fps
    ui->lblFps->setText("Frame per second : " + QString::number(fps)); // hiển thị fps
    }
    [/PHP]
    Thử với đoạn code đơn giản hơn xem thế nào bạn?
    Code:
    for ( int row = 1; row < img.height() + 1; ++row )
    {
        for ( int col = 1; col < img.width() + 1; ++col )
        {
            (img.pixel( row, col )).setBlueF(val1);
            (img.pixel( row, col )).setGreenF(val2);
            (img.pixel( row, col )).setRedF(val3);
            //Hoặc sử dụng hàm sau
            //(img.pixel( row, col )).setRgbF(val1, val2, val3, val4);
        }
    }

    ______SCharp______

    [PHP
    Bitmap img = new Bitmap("E:\\IMG.bmp"); // load ảnh trên ổ E ( ảnh chụp màn hình 1280x1024)
    Stopwatch st = new Stopwatch(); // bộ đếm thời gian thực


    private unsafe void button1_Click(object sender, EventArgs e) // hàm xử lý
    {
    Rectangle __rect = new Rectangle(0, 0, img.Width, img.Height); // hình chữ nhật bằng kích thước ảnh
    BitmapData __bmpdata = img.LockBits(__rect,ImageLockMode.ReadWrite, img.PixelFormat); // khóa dữ liệu ảnh
    int stride = __bmpdata.Stride; // lấy số byte trên 1 line ngang
    int height = __bmpdata.Height; // lấy số line của ảnh
    int depth = stride / __bmpdata.Width; // lấy số byte trên 1 pixel
    byte* ptr = (byte*)__bmpdata.Scan0.ToPointer(); // lấy con trỏ dữ liệu ảnh
    byte*[] pdata = new byte*[height]; // tạo mảng con trỏ dữ liệu ảnh
    for (int i = 0; i < height; i++)
    pdata[i] = ptr + i * stride; // gán địa chỉ cho mỗi con trỏ , mỗi con trỏ sẽ trỏ tới byte đầu tiên của 1 line
    img.UnlockBits(__bmpdata); // mở khóa ảnh để cho phép các truy cập khác
    long time = 0; // biến lưu thời gian xử lý
    st.Restart(); // reset bộ đếm + bắt đầu tính thời gian thực thi
    for(int i=0;i<100;i++) // lặp 100 lần
    {
    for(int y=0;y<height;y++) // duyệt từng line
    {
    for(int x=0;x<stride;x+=depth) // duyệt từng pixel
    {
    pdata[y][x]++; // tăng blue
    pdata[y][x + 1]++; // tăng green
    pdata[y][x + 2]++; // tăng red
    }
    }
    }
    time = st.ElapsedMilliseconds; // kết thúc xử lý, lấy thời gian thực thi
    st.Stop(); // dừng bộ đếm
    ptxDisplay.Image = img; // hiển thị ảnh
    lblTime.Text = "Thời gian xử lý : " + time.ToString() + " ms"; // hiển thị thời gian
    int fps = (int)(100000 / time); // tính fps
    lblFps.Text = "Frame per second : " + fps.ToString(); // hiển thị fps
    }
    }
    [/PHP]
    Thử với code của mình
    Code:
    public class CompvisBitmap
    {
        Bitmap source = null;
        IntPtr Iptr = IntPtr.Zero;
        BitmapData bitmapData = null;
     
        public byte[] Pixels { get; set; }
        public int Depth { get; private set; }
        public int Width { get; private set; }
        public int Height { get; private set; }
     
        public CompvisBitmap(Bitmap source)
        {
            this.source = source;
        }
    
        public void LockBits()
        {
            try
            {
                // Query kích thước ảnh
                Width = source.Width;
                Height = source.Height;
                
                //Tổng số pixel cần xử lý
                int PixelCount = Width * Height;
     
                //Rectangle cần lock
                Rectangle rect = new Rectangle(0, 0, Width, Height);
     
                //Depth của ảnh
                Depth = System.Drawing.Bitmap.GetPixelFormatSize(source.PixelFormat);
     
                // Kiểm tra bpp: 8, 24, hoặc 32
                if (Depth != 8 && Depth != 24 && Depth != 32)
                {
                    throw new ArgumentException("Chỉ có các ảnh với bpp: 8, 24 và 32 đươc sử dụng");
                }
     
                //Lấy dữ liệu bitmap
                bitmapData = source.LockBits(rect, ImageLockMode.ReadWrite,
                                             source.PixelFormat);
     
                //Sao chép pixels
                int step = Depth / 8;
                Pixels = new byte[PixelCount * step];
                Iptr = bitmapData.Scan0;
     
                // Chuyển từ con trỏ sang mảng
                Marshal.Copy(Iptr, Pixels, 0, Pixels.Length);
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }
     
        public void UnlockBits()
        {
            try
            {
                // Chuyển từ mảng sang con trỏ
                Marshal.Copy(Pixels, 0, Iptr, Pixels.Length);
     
                // khôi phục dữ liệu ảnh
                source.UnlockBits(bitmapData);
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }
     
        //Lấy về color của điểm ảnh
        public Color GetPixel(int x, int y)
        {
            Color clr = Color.Empty;
     
            // Số thành phần màu
            int cCount = Depth / 8;
     
            // Chỉ số bắt đầu của điểm ảnh tại vị trí (x,y)
            int i = ((y * Width) + x) * cCount;
     
            if (i > Pixels.Length - cCount)
                throw new IndexOutOfRangeException();
     
            if (Depth == 32) // Với ảnh có bpp=32 -->> [Red, Green, Blue và Alpha]
            {
                byte b = Pixels[i];
                byte g = Pixels[i + 1];
                byte r = Pixels[i + 2];
                byte a = Pixels[i + 3]; // a
                clr = Color.FromArgb(a, r, g, b);
            }
            if (Depth == 24) // Với ảnh có bpp=24 -->> [Red, Green và Blue]
            {
                byte b = Pixels[i];
                byte g = Pixels[i + 1];
                byte r = Pixels[i + 2];
                clr = Color.FromArgb(r, g, b);
            }
            if (Depth == 8) // Với ảnh có bpp=8 bpp -->> [Red, Green và Blue]
            {
                byte c = Pixels[i];
                clr = Color.FromArgb(c, c, c);
            }
            return clr;
        }
     
        
        //Gán giá trị màu của điểm ảnh (x,y)
        public void SetPixel(int x, int y, Color color)
        {
            // Số thành phần màu
            int cCount = Depth / 8;
     
            // Chỉ số bắt đầu của điểm ảnh tại vị trí (x,y)
            int i = ((y * Width) + x) * cCount;
     
            if (Depth == 32) // Với ảnh có bpp=32 -->> [Red, Green, Blue và Alpha]
            {
                Pixels[i] = color.B;
                Pixels[i + 1] = color.G;
                Pixels[i + 2] = color.R;
                Pixels[i + 3] = color.A;
            }
            if (Depth == 24) // Với ảnh có bpp=24 -->> [Red, Green và Blue]
            {
                Pixels[i] = color.B;
                Pixels[i + 1] = color.G;
                Pixels[i + 2] = color.R;
            }
            if (Depth == 8) // [Red, Green và Blue]
            {
                Pixels[i] = color.B;
            }
        }
    }
    
    //Dành để kiểm tra thời gian thực thi
    
    public class TimeBenchmark
    {
        private static DateTime sDate = DateTime.MinValue;
        private static DateTime eDate = DateTime.MinValue;
     
        public static TimeSpan Span { get { return eDate.Subtract(sDate); } }
     
        public static void Start() { sDate = DateTime.Now; }
     
        public static void End() { eDate = DateTime.Now; }
     
        public static double GetSeconds()
        {
            if (eDate == DateTime.MinValue) return 0.0;
            else return Span.TotalSeconds;
        }
    }
    Cách dùng:
    Code:
    public void HowToUse()
    {
        Bitmap bmp = (Bitmap)Image.FromFile("d:\\test.png");
        TimeBenchmark.Start();
        CompvisBitmap lBitmap = new CompvisBitmap(bmp);
        lockBitmap.LockBits();
     
        Color cmpClr = Color.FromArgb(255, 255, 255, 255);
        for (int y = 0; y < lBitmap.Height; y++)
        {
            for (int x = 0; x < lBitmap.Width; x++)
            {
                if (lBitmap.GetPixel(x, y) == cmpClr)
                {
                    lBitmap.SetPixel(x, y, Color.Red);
                }
            }
        }
        lBitmap.UnlockBits();
        TimeBenchmark.End();
        double seconds = TimeBenchmark.GetSeconds(); 
       bmp.Save("d:\\ketqua_day.png"); }
    Lưu ý: Trong phần code của bạn [QT và CSharp] có sử dụng các cấp phát các mảng con trỏ. Khi bạn không dùng tới nó, bạn phải hủy nó để giải phóng bộ nhớ. Điều này hết sức cơ bản và quan trọng:

    Với QT:
    Code:
    for(int i=0;i<height;i++)          
    {
        delete[] dataptr[i];
    }
    delete[] dataptr;
    Với CSHARP:
    Code:
    for(int i=0;i<height;i++)          
    {
        delete[] pdata [i];
    }
    delete[] pdata ;

    Comment


    • #17
      Nguyên văn bởi compvis Xem bài viết

      Thử với đoạn code đơn giản hơn xem thế nào bạn?
      Code:
      for ( int row = 1; row < img.height() + 1; ++row )
      {
      for ( int col = 1; col < img.width() + 1; ++col )
      {
      (img.pixel( row, col )).setBlueF(val1);
      (img.pixel( row, col )).setGreenF(val2);
      (img.pixel( row, col )).setRedF(val3);
      //Hoặc sử dụng hàm sau
      //(img.pixel( row, col )).setRgbF(val1, val2, val3, val4);
      }
      }


      Thử với code của mình
      Code:
      public class CompvisBitmap
      {
      Bitmap source = null;
      IntPtr Iptr = IntPtr.Zero;
      BitmapData bitmapData = null;
      
      public byte[] Pixels { get; set; }
      public int Depth { get; private set; }
      public int Width { get; private set; }
      public int Height { get; private set; }
      
      public CompvisBitmap(Bitmap source)
      {
      this.source = source;
      }
      
      public void LockBits()
      {
      try
      {
      // Query kích thước ảnh
      Width = source.Width;
      Height = source.Height;
      
      //Tổng số pixel cần xử lý
      int PixelCount = Width * Height;
      
      //Rectangle cần lock
      Rectangle rect = new Rectangle(0, 0, Width, Height);
      
      //Depth của ảnh
      Depth = System.Drawing.Bitmap.GetPixelFormatSize(source.PixelFormat);
      
      // Kiểm tra bpp: 8, 24, hoặc 32
      if (Depth != 8 && Depth != 24 && Depth != 32)
      {
      throw new ArgumentException("Chỉ có các ảnh với bpp: 8, 24 và 32 đươc sử dụng");
      }
      
      //Lấy dữ liệu bitmap
      bitmapData = source.LockBits(rect, ImageLockMode.ReadWrite,
      source.PixelFormat);
      
      //Sao chép pixels
      int step = Depth / 8;
      Pixels = new byte[PixelCount * step];
      Iptr = bitmapData.Scan0;
      
      // Chuyển từ con trỏ sang mảng
      Marshal.Copy(Iptr, Pixels, 0, Pixels.Length);
      }
      catch (Exception ex)
      {
      throw ex;
      }
      }
      
      public void UnlockBits()
      {
      try
      {
      // Chuyển từ mảng sang con trỏ
      Marshal.Copy(Pixels, 0, Iptr, Pixels.Length);
      
      // khôi phục dữ liệu ảnh
      source.UnlockBits(bitmapData);
      }
      catch (Exception ex)
      {
      throw ex;
      }
      }
      
      //Lấy về color của điểm ảnh
      public Color GetPixel(int x, int y)
      {
      Color clr = Color.Empty;
      
      // Số thành phần màu
      int cCount = Depth / 8;
      
      // Chỉ số bắt đầu của điểm ảnh tại vị trí (x,y)
      int i = ((y * Width) + x) * cCount;
      
      if (i > Pixels.Length - cCount)
      throw new IndexOutOfRangeException();
      
      if (Depth == 32) // Với ảnh có bpp=32 -->> [Red, Green, Blue và Alpha]
      {
      byte b = Pixels[i];
      byte g = Pixels[i + 1];
      byte r = Pixels[i + 2];
      byte a = Pixels[i + 3]; // a
      clr = Color.FromArgb(a, r, g, b);
      }
      if (Depth == 24) // Với ảnh có bpp=24 -->> [Red, Green và Blue]
      {
      byte b = Pixels[i];
      byte g = Pixels[i + 1];
      byte r = Pixels[i + 2];
      clr = Color.FromArgb(r, g, b);
      }
      if (Depth == 8) // Với ảnh có bpp=8 bpp -->> [Red, Green và Blue]
      {
      byte c = Pixels[i];
      clr = Color.FromArgb(c, c, c);
      }
      return clr;
      }
      
      
      //Gán giá trị màu của điểm ảnh (x,y)
      public void SetPixel(int x, int y, Color color)
      {
      // Số thành phần màu
      int cCount = Depth / 8;
      
      // Chỉ số bắt đầu của điểm ảnh tại vị trí (x,y)
      int i = ((y * Width) + x) * cCount;
      
      if (Depth == 32) // Với ảnh có bpp=32 -->> [Red, Green, Blue và Alpha]
      {
      Pixels[i] = color.B;
      Pixels[i + 1] = color.G;
      Pixels[i + 2] = color.R;
      Pixels[i + 3] = color.A;
      }
      if (Depth == 24) // Với ảnh có bpp=24 -->> [Red, Green và Blue]
      {
      Pixels[i] = color.B;
      Pixels[i + 1] = color.G;
      Pixels[i + 2] = color.R;
      }
      if (Depth == 8) // [Red, Green và Blue]
      {
      Pixels[i] = color.B;
      }
      }
      }
      
      //Dành để kiểm tra thời gian thực thi
      
      public class TimeBenchmark
      {
      private static DateTime sDate = DateTime.MinValue;
      private static DateTime eDate = DateTime.MinValue;
      
      public static TimeSpan Span { get { return eDate.Subtract(sDate); } }
      
      public static void Start() { sDate = DateTime.Now; }
      
      public static void End() { eDate = DateTime.Now; }
      
      public static double GetSeconds()
      {
      if (eDate == DateTime.MinValue) return 0.0;
      else return Span.TotalSeconds;
      }
      }
      Cách dùng:
      Code:
      public void HowToUse()
      {
      Bitmap bmp = (Bitmap)Image.FromFile("d:\\test.png");
      TimeBenchmark.Start();
      CompvisBitmap lBitmap = new CompvisBitmap(bmp);
      lockBitmap.LockBits();
      
      Color cmpClr = Color.FromArgb(255, 255, 255, 255);
      for (int y = 0; y < lBitmap.Height; y++)
      {
      for (int x = 0; x < lBitmap.Width; x++)
      {
      if (lBitmap.GetPixel(x, y) == cmpClr)
      {
      lBitmap.SetPixel(x, y, Color.Red);
      }
      }
      }
      lBitmap.UnlockBits();
      TimeBenchmark.End();
      double seconds = TimeBenchmark.GetSeconds();
      bmp.Save("d:\\ketqua_day.png"); }
      Lưu ý: Trong phần code của bạn [QT và CSharp] có sử dụng các cấp phát các mảng con trỏ. Khi bạn không dùng tới nó, bạn phải hủy nó để giải phóng bộ nhớ. Điều này hết sức cơ bản và quan trọng:

      Với QT:
      Code:
      for(int i=0;i<height;i++)
      {
      delete[] dataptr[i];
      }
      delete[] dataptr;
      Với CSHARP:
      Code:
      for(int i=0;i<height;i++)
      {
      delete[] pdata [i];
      }
      delete[] pdata ;
      Mình đã thử với đoạn code QT của bác. Nó rất chậm. Có thể do đã thao tác với dữ lieu qua các hàm row(),col(),width(),height()... nên nó chậm hơn với truy xuất trực tiếp đến dữ lieu ảnh bang con trỏ. Nó mất >200ms cho 1 frame (1280x1024).

      Code CSharp mình chưa test nhưng đã làm gần going cách của bác. Nghi ngờ thao tác copy dữ lieu ảnh ra mảng byte sau đó copy ngược trở lại sẽ mất tương đối thời gian. Mình sẽ thử các của bác xem thực tế thế nào.

      Về phần mảng mình có khai báo nhưng là ánh xạ đến vùng nhớ của dữ lieu ảnh chứ không khai báo mảng dữ lieu mới. Nếu mình del đi sợ nó thu hồi mất vùng dữ lieu của ảnh. Để mình test thực tế.
      ____
      Nhân tiện muốn tham khảo bác compvis luôn là bác dung IDE và Complier nào cho C++ vậy ?

      Comment


      • #18
        Mình đã thử với đoạn code QT của bác. Nó rất chậm. Có thể do đã thao tác với dữ lieu qua các hàm row(),col(),width(),height()... nên nó chậm hơn với truy xuất trực tiếp đến dữ lieu ảnh bang con trỏ. Nó mất >200ms cho 1 frame (1280x1024).
        Xin lỗi bạn là mình chưa dùng QT, chỉ là để bạn tham khảo nhé! Cả phần C#, mình code để bạn tham khảo.

        Nguyên văn bởi duong_act Xem bài viết
        Về phần mảng mình có khai báo nhưng là ánh xạ đến vùng nhớ của dữ lieu ảnh chứ không khai báo mảng dữ lieu mới. Nếu mình del đi sợ nó thu hồi mất vùng dữ lieu của ảnh. Để mình test thực tế.
        Trong C++, khi cấp phát bộ nhớ [con trỏ, từ khóa new] thì bạn nên phải giải phóng bộ nhớ sau khi sử dụng. Bạn đã biết đến từ "Memory Leak" chưa ? Khi cấp phát bộ nhớ, nếu không được giải phóng thì không gian bộ nhớ được sử dụng làm địa chỉ sẽ tăng lên. Điều này sẽ không ảnh hưởng tới chương trình không cấp phát bộ nhớ liên tục bởi nó vẫn chạy và bộ nhớ đó sẽ được giải phóng khi thoát khỏi chương trình. Tuy nhiên với các ứng dụng thời gian thực, chương trình có thời gian tồn tại lâu và cần cấp phát bộ nhờ liên tục thì bộ nhớ của bạn sẽ bị quá tải và vì thế nó càng lúc càng chậm đi. Với c#, nếu bạn không dùng các kiểu dữ liệu với unsafe thì bạn không phải giải phóng vì C# sử dụng Garbage Collection (quản lý bộ nhớ tự động). Ngược lại bạn vẫn nên giải phóng bộ nhớ sau khi sử không sử dụng tới nó.

        Nguyên văn bởi duong_act Xem bài viết
        Nhân tiện muốn tham khảo bác compvis luôn là bác dung IDE và Complier nào cho C++ vậy ?
        Visual Studio 2015 + Windows 7 x64 + Intel Core i7-4790 CPU 3.6GHz

        Comment


        • #19
          Nguyên văn bởi compvis Xem bài viết

          Xin lỗi bạn là mình chưa dùng QT, chỉ là để bạn tham khảo nhé! Cả phần C#, mình code để bạn tham khảo.



          Trong C++, khi cấp phát bộ nhớ [con trỏ, từ khóa new] thì bạn nên phải giải phóng bộ nhớ sau khi sử dụng. Bạn đã biết đến từ "Memory Leak" chưa ? Khi cấp phát bộ nhớ, nếu không được giải phóng thì không gian bộ nhớ được sử dụng làm địa chỉ sẽ tăng lên. Điều này sẽ không ảnh hưởng tới chương trình không cấp phát bộ nhớ liên tục bởi nó vẫn chạy và bộ nhớ đó sẽ được giải phóng khi thoát khỏi chương trình. Tuy nhiên với các ứng dụng thời gian thực, chương trình có thời gian tồn tại lâu và cần cấp phát bộ nhờ liên tục thì bộ nhớ của bạn sẽ bị quá tải và vì thế nó càng lúc càng chậm đi. Với c#, nếu bạn không dùng các kiểu dữ liệu với unsafe thì bạn không phải giải phóng vì C# sử dụng Garbage Collection (quản lý bộ nhớ tự động). Ngược lại bạn vẫn nên giải phóng bộ nhớ sau khi sử không sử dụng tới nó.


          Visual Studio 2015 + Windows 7 x64 + Intel Core i7-4790 CPU 3.6GHz
          C++ hay C# mình có thắc mắc chút nhờ bác chỉ giáo.
          Đúng là mình có khai báo mảng con trỏ, nhưng mảng con trỏ này sẽ trỏ tới 1 vùng nhớ đã tồn tại sẵn rồi (mảng dữ lieu của ảnh).
          PHP Code:
          unsigned char **ptr=new unsigned char*[height]; // tạo mảng con trỏ
          for(int I=0;i<height;i++)
              
          ptr[i]=imageptr I*stride;
              
          // gán địa chỉ cho mỗi con trỏ trong mảng chứ không tạo mới mảng dữ lieu
              // nếu tạo mới mảng dữ liệu : ptr[I]=new unsigned char[stride];
              // mỗi con trỏ trong mảng trỏ tới byte đầu tiên của 1 line dữ lieu ảnh.
              // imageptr là con trỏ trỏ tới byte đầu tiên line đầu tiên của mảng dữ lieu ảnh. 
          Như vậy khi khai báo mảng con trỏ thì hệ thong có cấp phát vùng nhớ không (mình nghĩ là không nhưng đó là nghĩ thôi).
          Khi giải phóng mảng con trỏ thì mảng dữ lieu được con trỏ trỏ tới có bị thu hồi không ? Vì mình cần xử lý ở những chỗ khác nữa, nếu bị thu hồi thì có thể có vấn đề.
          __
          Bác dung MFC, VC++ hay VC++.NET hay gì vậy ?

          Comment


          • #20
            unsigned char **ptr=new unsigned char*[height]; // tạo mảng con trỏ
            Bạn đã cấp phát bộ nhớ động cho con trỏ ptr. Nên giải phóng nó khi không sử dụng.

            Khi giải phóng mảng con trỏ thì mảng dữ lieu được con trỏ trỏ tới có bị thu hồi không ? Vì mình cần xử lý ở những chỗ khác nữa, nếu bị thu hồi thì có thể có vấn đề.
            Việc nhiều con trỏ cùng trỏ tới một địa chỉ thì khi hủy một con trỏ, các con trỏ khác sẽ không còn khả năng sử dụng (dữ liệu cũng sẽ bị hủy). Bạn nên xem xét sử dụng "shared_ptr"

            Theo ngu ý của mình, đoạn mã của bạn như thế này [Reference only, not tested]

            Code:
            QImage img("E:/IMG.bmp"); // load ảnh trên ổ E ( ảnh chụp màn hình 1280x1024 )  
            QElapsedTimer tmr; // bộ đếm thời gian thực        
            void MainWindow::on_pushButton_clicked()  
            {      
                int depth = img.depth()/8;  // số byte / 1 pixel      
                int stride=depth*img.width(); // số byte trên 1 line ngang      
                int height = img.height(); // số line dữ liệu      
                
                qint64 time=0; // biến lưu thời gian thực thi      
                tmr.restart(); // reset thời gian + bắt đầu tính thời gian
                
                for(int i=0;i<100;i++) // lặp 100 lần      
                {          
                    for(int y=0;y<height;y++)  // duyệt từng line trên ảnh          
                    {      
                        for(int x=0;x<stride;x+=depth)          
                        {                
                            img.scanLine(i)[y][x]++; // tăng màu blue                      
                            img.scanLine(i)[y][x+1]++; // tăng màu green                                  
                            img.scanLine(i)[y][x+2]++; // tăng màu red                  
                        }          
                    }      
                }      
                time=tmr.elapsed(); // kết thúc, lấy thời gian thực thi.
                ui->lblDisplay->setPixmap(QPixmap::fromImage(img)); // hiển thị ảnh sau xử lý
                ui->lblTime->setText("Thời gian xử lý : " + QString::number(time) + " ms"); // hiển thị thời gian    
                int fps = 100000/time; // tính fps      
                ui->lblFps->setText("Frame per second : " +     QString::number(fps)); // hiển thị fps  
            }

            Comment


            • #21
              Nguyên văn bởi compvis Xem bài viết

              Bạn đã cấp phát bộ nhớ động cho con trỏ ptr. Nên giải phóng nó khi không sử dụng.

              Việc nhiều con trỏ cùng trỏ tới một địa chỉ thì khi hủy một con trỏ, các con trỏ khác sẽ không còn khả năng sử dụng (dữ liệu cũng sẽ bị hủy). Bạn nên xem xét sử dụng "shared_ptr"
              Thanks bác !

              Theo ngu ý của mình, đoạn mã của bạn như thế này [Reference only, not tested]

              Code:
              QImage img("E:/IMG.bmp"); // load ảnh trên ổ E ( ảnh chụp màn hình 1280x1024 )
              QElapsedTimer tmr; // bộ đếm thời gian thực
              void MainWindow::on_pushButton_clicked()
              {
              int depth = img.depth()/8; // số byte / 1 pixel
              int stride=depth*img.width(); // số byte trên 1 line ngang
              int height = img.height(); // số line dữ liệu
              
              qint64 time=0; // biến lưu thời gian thực thi
              tmr.restart(); // reset thời gian + bắt đầu tính thời gian
              
              for(int i=0;i<100;i++) // lặp 100 lần
              {
              for(int y=0;y<height;y++) // duyệt từng line trên ảnh
              {
              for(int x=0;x<stride;x+=depth)
              {
              img.scanLine(i)[y][x]++; // tăng màu blue
              img.scanLine(i)[y][x+1]++; // tăng màu green
              img.scanLine(i)[y][x+2]++; // tăng màu red
              }
              }
              }
              time=tmr.elapsed(); // kết thúc, lấy thời gian thực thi.
              ui->lblDisplay->setPixmap(QPixmap::fromImage(img)); // hiển thị ảnh sau xử lý
              ui->lblTime->setText("Thời gian xử lý : " + QString::number(time) + " ms"); // hiển thị thời gian
              int fps = 100000/time; // tính fps
              ui->lblFps->setText("Frame per second : " + QString::number(fps)); // hiển thị fps
              }
              Chỗ này mình chưa hiểu ý bác lắm. Mình đọc rất kỹ đoạn code bác trích dẫn nhưng không thấy khác biệt gì cả.
              Ý bác là sẽ không khai báo con trỏ nữa mà sẽ dung bang img.scanLine(x) ?
              Nếu vậy thì nó rất chậm, chậm hơn khai báo con trỏ tới hơn 7 lần

              Comment


              • #22
                Chỗ này mình chưa hiểu ý bác lắm. Mình đọc rất kỹ đoạn code bác trích dẫn nhưng không thấy khác biệt gì cả.
                Ý bác là sẽ không khai báo con trỏ nữa mà sẽ dung bang img.scanLine(x) ?
                Nếu vậy thì nó rất chậm, chậm hơn khai báo con trỏ tới hơn 7 lần
                Xem lại code của bạn đi, mình chỉ rút gọn thôi. Trong code của bạn có sử dụng:

                Code:
                      dataptr[i] = img.scanLine(i);
                Trong này:
                Code:
                unsigned char** dataptr = new unsigned char*[height]; // tạo mảng con trỏ    
                    for(int i=0;i<height;i++)        
                         dataptr[i]=img.scanLine(i); // gán địa chỉ cho mỗi con trỏ
                    qint64 time=0; // biến lưu thời gian thực thi      
                    tmr.restart(); // reset thời gian + bắt đầu tính thời gian      
                    for(int i=0;i<100;i++) // lặp 100 lần      
                    {          
                        for(int y=0;y<height;y++)  // duyệt từng line trên ảnh          
                        {
                            //duyệt từng điểm ảnh trên line              
                            for(int x=0;x<stride;x+=depth)          
                            {                
                                // Lúc trước dùng thế này chậm 7 lần
                                //img.scanLine(y)[x]++; // tăng màu blue                
                                //img.scanLine(y)[x+1]++; // tăng màu green                  
                                //img.scanLine(y)[x+2]++; // tăng màu red    
                
                                // bây giờ dùng thế này thì nhanh hơn C# khoảng 1.1 - 1.2 lần.          
                                dataptr[y][x]++; // tăng màu blue                      
                                dataptr[y][x+1]++; // tăng màu green                                  
                                dataptr[y][x+2]++; // tăng màu red                  
                            }          
                        }      
                    }  
                Vì thế bạn khai báo thêm mảng cũng chẳng nhanh hơn và chả để làm gì? Thậm chí còn gây chầm thơn nếu ko xử lý tốt. Chẳng hiểu tại sao bạn lại nói nó nhanh hơn !!!

                Comment


                • #23
                  Nguyên văn bởi compvis Xem bài viết

                  Xem lại code của bạn đi, mình chỉ rút gọn thôi. Trong code của bạn có sử dụng:

                  Code:
                  dataptr[i] = img.scanLine(i);
                  Trong này:
                  Code:
                  unsigned char** dataptr = new unsigned char*[height]; // tạo mảng con trỏ
                  for(int i=0;i<height;i++)
                  dataptr[i]=img.scanLine(i); // gán địa chỉ cho mỗi con trỏ
                  qint64 time=0; // biến lưu thời gian thực thi
                  tmr.restart(); // reset thời gian + bắt đầu tính thời gian
                  for(int i=0;i<100;i++) // lặp 100 lần
                  {
                  for(int y=0;y<height;y++) // duyệt từng line trên ảnh
                  {
                  //duyệt từng điểm ảnh trên line
                  for(int x=0;x<stride;x+=depth)
                  {
                  // Lúc trước dùng thế này chậm 7 lần
                  //img.scanLine(y)[x]++; // tăng màu blue
                  //img.scanLine(y)[x+1]++; // tăng màu green
                  //img.scanLine(y)[x+2]++; // tăng màu red
                  
                  // bây giờ dùng thế này thì nhanh hơn C# khoảng 1.1 - 1.2 lần.
                  dataptr[y][x]++; // tăng màu blue
                  dataptr[y][x+1]++; // tăng màu green
                  dataptr[y][x+2]++; // tăng màu red
                  }
                  }
                  } 
                  Vì thế bạn khai báo thêm mảng cũng chẳng nhanh hơn và chả để làm gì? Thậm chí còn gây chầm thơn nếu ko xử lý tốt. Chẳng hiểu tại sao bạn lại nói nó nhanh hơn !!!
                  Nhanh hơn 7 lần đó bác.
                  scanLine(x) là một hàm trả về con trỏ trỏ tới byte đầu tiên của một line dữ lieu trên ảnh. Khi sử dung hàm này tốc độ nó thấp vì nó phải làm công việc của việc gọi hàm, lấy giá trị trả về rồi mới tính toán được.
                  Mình khai báo mảng dataptr rồi gán địa chỉ tới dữ lieu ảnh nên nó going như con trỏ dữ lieu của ảnh. Khi thao tác với nó có thể truy cập và thao tác trực tiếp đến dữ lieu ảnh nên nó sẽ nhanh hơn.

                  Giống như kiểu :
                  PHP Code:
                  int a=0;
                  int b=1;
                  int c=2;

                  // Tính tong thế này sẽ nhanh hơn
                  b+c;


                  // Tính thế này sẽ chậm hơn
                  a=Tong(a,b);

                  int Tong(int a,int b)
                  {
                      return (
                  a+b);

                  Comment


                  • #24
                    Nguyên văn bởi duong_act Xem bài viết

                    Nhanh hơn 7 lần đó bác.
                    scanLine(x) là một hàm trả về con trỏ trỏ tới byte đầu tiên của một line dữ lieu trên ảnh. Khi sử dung hàm này tốc độ nó thấp vì nó phải làm công việc của việc gọi hàm, lấy giá trị trả về rồi mới tính toán được.
                    Mình khai báo mảng dataptr rồi gán địa chỉ tới dữ lieu ảnh nên nó going như con trỏ dữ lieu của ảnh. Khi thao tác với nó có thể truy cập và thao tác trực tiếp đến dữ lieu ảnh nên nó sẽ nhanh hơn.

                    Giống như kiểu :
                    PHP Code:
                    int a=0;
                    int b=1;
                    int c=2;

                    // Tính tong thế này sẽ nhanh hơn
                    b+c;


                    // Tính thế này sẽ chậm hơn
                    a=Tong(a,b);

                    int Tong(int a,int b)
                    {
                    return (
                    a+b);

                    Okie, trong code của bạn có sử dụng hàm scanline nhưng trước khi bắt đầu tính thời gian, còn mình đặt ở vòng lặp cuối thì đương nhiên sẽ cost time. Thời gian xử lý phải được tính từ lúc bạn MỞ CÁI NGĂN KÉO RA cho tơi khi bạn ĐÓNG CÁI NGĂN KÉO LẠI. Tức là thời gian từ lúc bắt đầu thao tác với ảnh cho tới khi thao tác xong.

                    Và chỉ nhìn vào code của cả QT và CSharp để thao tác với ảnh (image manipulation) của bạn mình thấy cũng không chuẩn lắm. Để chọn ra một cách chuẩn nhất, bạn sử dụng một thư viện có sẵn là OpenCV (QT) và một thư việt được wrap sẵn là EmguCV (.NET) sau đó thử thao tác cùng một ảnh với một toán tử nào đó ở hai môi trường khác nhau là QT và C#. Bạn sẽ có câu trả lời rõ ràng hơn.

                    Cheers,

                    Comment


                    • #25
                      Nếu mình sử dụng scanLine trong lúc đo thời gian cũng cho kết quả nhanh hơn nếu mình khai báo mảng con trỏ. Vì khi đó scanLine chỉ được dung khi lấy địa chỉ dữ lieu thôi ( lấy số lần bang chiều cao ảnh lần ). Còn nếu để trong vòng lặp thì mỗi pixel sẽ phải lấy 3 lần (số lần lấy = chiều cao * chiều rộng * 3 * số lần lặp). Đó là nguyên nhân gây chậm trong cách sử dung thôi.

                      Mình chỉ đo thời gian tính toán với dữ lieu cơ bản (byte, ptr) với cả 2 thằng thôi, không sử dung hàm.
                      Tuy nhiên để mình thử dùng một dll của C++ test trên cả 2 thằng xem thế nào

                      Comment


                      • #26
                        Mình giải quyết được rồi.
                        Mình chuyển comlier sang MSVC thì cho tốc độ nhanh hơn 4 lần so với dùng MINGW
                        ____

                        Comment

                        Về tác giả

                        Collapse

                        duong_act Tìm hiểu thêm về duong_act

                        Bài viết mới nhất

                        Collapse

                        Đang tải...
                        X