PDA

مشاهده نسخه کامل : کشیدن یک عکس ردر بکگراند فرم در vb.net



vardipoor.m
12-09-21, 01:39
سلام
یه عکس هست که نصف پیکسلهاش transparent هست میخوام اونو و چندتای دیگه که با کمی تفاوت مثل هم هستن رو، روی هم توی background فرم بکشم
یه کد پیدا کردم توی نت که میکشه اما فقط نصفه فرم رو میگیره باقیش نمایش داده نمیشه
این کد

Dim gameGraphics As System.Drawing.Graphics = Me.CreateGraphics
gameGraphics.DrawImage(PictureBox3.Image, 0, 0, PictureBox3.Image.Width, PictureBox3.Image.Height)

این کد خوبیش اینه که حالت ترنسپرنت حفظ میشه اما کامل نشون نمیده عکس آزمایشی رو پایین گذاشتم
24640
من پروژم اینه
یه تصویر رو از وب کم وصل کردم به ویژوال با emgu.cv
تصویر زنده هست و یه دایره سفید هست که توی یک مستطیل مشکی در حال گشت زدن هست. و تصویرش از طریق یه تایمر هر لحظه توی picturebox نشون داده میشه. پس من هر فریم رو دارم
این هم کد نمایش تصویر وب کم توی پیکچرباکس


Imports Emgu.CV
Imports Emgu.CV.UI
Imports Emgu.CV.Structure


Public Class Form1
Dim takepic As New Capture
Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick
PictureBox1.Image = takepic.QueryFrame.ToBitmap
End Sub
End Class

اومدم پیکسلهای مشکی تصویرش رو transparent کردم و میخوام تمام فریم هایی که ایجاد میشه (بعد از ترنسپرنت شدن) رو توی form یا picturebox ذخیره کنم به صورتی قسمت ترنسپرنت چاپ نشه و در آخر مجموع دایره ها بشه یه تصویر(امیدوارم واضح گفته باشم)
هر چه سعی کردم با picturebox نتونستم چون با picturebox فقط ترنسپرنت میشه روی پس زمینه و عملا عکس پشتی قابل دیدن نیست. توی فرم همون چیزی که میخواستم شد یعنی چاپ شد و عکس بعدی هم همینطور و فقط دایره ها چاپ شدن ولی طبق عکس آزمایشی کامل چاپ نمیشه

(الته تو پرانتز بگم شاید یه جور دیگه هم بشه نوشتش مثال
هر پیکسلش که سیاه نبود رو توی فرم یا پیکچرباکس بکشه یعنی فقط دایره میمونه!
البته کدش رو نمیدونم
ولی روالش اینه
پیمایش یه تصویر برای پیدا کردن پیکسل های غیر از سیاه
کشیدن هر پیکسل پیدا شده درون یک پیکچرباکس دیگه در همون مختصات)

کسی راه حلی داره؟

SajjadKhati
12-09-21, 18:09
سلام
یه عکس هست که نصف پیکسلهاش transparent هست میخوام اونو و چندتای دیگه که با کمی تفاوت مثل هم هستن رو، روی هم توی background فرم بکشم
یه کد پیدا کردم توی نت که میکشه اما فقط نصفه فرم رو میگیره باقیش نمایش داده نمیشه
این کد

Dim gameGraphics As System.Drawing.Graphics = Me.CreateGraphics
gameGraphics.DrawImage(PictureBox3.Image, 0, 0, PictureBox3.Image.Width, PictureBox3.Image.Height)

این کد خوبیش اینه که حالت ترنسپرنت حفظ میشه اما کامل نشون نمیده عکس آزمایشی رو پایین گذاشتم
24640
من پروژم اینه
یه تصویر رو از وب کم وصل کردم به ویژوال با emgu.cv
تصویر زنده هست و یه دایره سفید هست که توی یک مستطیل مشکی در حال گشت زدن هست. و تصویرش از طریق یه تایمر هر لحظه توی picturebox نشون داده میشه. پس من هر فریم رو دارم
این هم کد نمایش تصویر وب کم توی پیکچرباکس


Imports Emgu.CV
Imports Emgu.CV.UI
Imports Emgu.CV.Structure


Public Class Form1
Dim takepic As New Capture
Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick
PictureBox1.Image = takepic.QueryFrame.ToBitmap
End Sub
End Class


سلام
دقیقا منظورتون را متوجه نشدم .
vb هم کار نکردم .

اگه منظورتون اینه که عکس تون ، در کنترل picturebox تون ، fit و کامل نمیشه ، پروپرتیِ SizeMode ئه picturebox را روی StretchImage تنظیم کنید .



اومدم پیکسلهای مشکی تصویرش رو transparent کردم و میخوام تمام فریم هایی که ایجاد میشه (بعد از ترنسپرنت شدن) رو توی form یا picturebox ذخیره کنم به صورتی قسمت ترنسپرنت چاپ نشه و در آخر مجموع دایره ها بشه یه تصویر(امیدوارم واضح گفته باشم)
هر چه سعی کردم با picturebox نتونستم چون با picturebox فقط ترنسپرنت میشه روی پس زمینه و عملا عکس پشتی قابل دیدن نیست. توی فرم همون چیزی که میخواستم شد یعنی چاپ شد و عکس بعدی هم همینطور و فقط دایره ها چاپ شدن ولی طبق عکس آزمایشی کامل چاپ نمیشه


اگه منظورتون اینه که تصویری (شامل پیکسل های transparent) ای که در یک کنترل رسم کردید (فرضا در کنترل picturebox رسم کردید) ، قسمتِ transparent ئه تصویرِ رسم شده جوری هست که کنترل های زیرین اش (کنترل های زیرینِ picturebox) قابل نمایش نیست ، این طبیعی هست .
چون کنترل های windows form ، از transparent پشتیبانی نمیکنن .

vardipoor.m
13-09-21, 00:05
سلام
دقیقا منظورتون را متوجه نشدم .
vb هم کار نکردم .

اگه منظورتون اینه که عکس تون ، در کنترل picturebox تون ، fit و کامل نمیشه ، پروپرتیِ SizeMode ئه picturebox را روی StretchImage تنظیم کنید .
سلام
تصویر داخل پیکچر باکس کامل هست و مشکلی نداره(تصویر سمت راست درون فرم). مشکل چیز دیگه ایه


اگه منظورتون اینه که تصویری (شامل پیکسل های transparent) ای که در یک کنترل رسم کردید (فرضا در کنترل picturebox رسم کردید) ، قسمتِ transparent ئه تصویرِ رسم شده جوری هست که کنترل های زیرین اش (کنترل های زیرینِ picturebox) قابل نمایش نیست ، این طبیعی هست .
چون کنترل های windows form ، از transparent پشتیبانی نمیکنن .

ببنید تصویر در پیکچر باکس هست و همونطور که گفتید اره کنترل های زیریش رو نمایش نمیده پس پیکچر باکس نمیشه استفاده کرد
اومدم توی فرم پرینتش کردم با این کد


Dim gameGraphics As System.Drawing.Graphics = Me.CreateGraphics
gameGraphics.DrawImage(PictureBox3.Image, 0, 0, PictureBox3.Image.Width, PictureBox3.Image.Height)


دقت کنید این کد توی تایمر هست و چون تصویر قسمت رنگ سیاهش ترنسپرنت میشه (تصویر سمت چپ درون عکس) هر دفعه که تایمر استارت میزنه یه تصویر ثبت میشه توی بکگراند فرم و چون دایره در حال گشت زدن هست پس یه دایره چندین جای فرم ثبت میشه

الان مشکل اینه توی فرم تصویر رو کامل نشون نمیده فقط یه تیکست توی تصویر هم مشخصه (تصویر سمت چپ درون فرم که چندین عکس ترنسپرنت شده از تصویر سمت راست هست و به دفعات توی فرم، روی هم چاپ شده ) در اصل باید اندازه ی هر دو عکس یکی میبود

SajjadKhati
13-09-21, 12:28
سلام
تصویر داخل پیکچر باکس کامل هست و مشکلی نداره(تصویر سمت راست درون فرم). مشکل چیز دیگه ایه



ببنید تصویر در پیکچر باکس هست و همونطور که گفتید اره کنترل های زیریش رو نمایش نمیده پس پیکچر باکس نمیشه استفاده کرد
اومدم توی فرم پرینتش کردم با این کد


Dim gameGraphics As System.Drawing.Graphics = Me.CreateGraphics
gameGraphics.DrawImage(PictureBox3.Image, 0, 0, PictureBox3.Image.Width, PictureBox3.Image.Height)


دقت کنید این کد توی تایمر هست و چون تصویر قسمت رنگ سیاهش ترنسپرنت میشه (تصویر سمت چپ درون عکس) هر دفعه که تایمر استارت میزنه یه تصویر ثبت میشه توی بکگراند فرم و چون دایره در حال گشت زدن هست پس یه دایره چندین جای فرم ثبت میشه

الان مشکل اینه توی فرم تصویر رو کامل نشون نمیده فقط یه تیکست توی تصویر هم مشخصه (تصویر سمت چپ درون فرم که چندین عکس ترنسپرنت شده از تصویر سمت راست هست و به دفعات توی فرم، روی هم چاپ شده ) در اصل باید اندازه ی هر دو عکس یکی میبود


سلام
کد کامل تون باید مشخص بشه چیه .
من vb کار نکردم . منتظر آقا آرمین باشید .

ولی اندازه ی کنترل ، یا اندازه ی گرافیکی که توش رسم میکنید را چک کنید .
ضمنا ، از متد CreateGraphics استفاده نکنید . گرافیک را از رویداد Paint ئه کنترلی که میخواید توش رسم کنید (از e.Graphics) ، بگیرید .
آموزش Graphic (در سی شارپ) ، قبلا داده شد . اگه مایل بودید میتونید از آموزشی که توی امضام هست ، استفاده کنید (از قسمت 77 به بعد) .

vardipoor.m
13-09-21, 13:01
Imports Emgu.CV 'emgu.cv for show webcam
Imports Emgu.CV.UI 'emgu.cv for show webcam
Imports Emgu.CV.Structure 'emgu.cv for show webcam


Public Class Form1
Dim takepic As New Capture


Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick
PictureBox1.Image = takepic.QueryFrame.ToBitmap 'add frame from my webcam to picturebox
Dim bm_label1 As New Bitmap(PictureBox1.Image)


bm_label1.MakeTransparent(Color.FromArgb(24, 53, 73)) 'transparent the background of rectangle
PictureBox2.Image = bm_label1

End Sub


Private Sub Form1_Paint(ByVal Sender As System.Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles MyBase.Paint


End Sub
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Timer1.Start()


End Sub


Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
Timer1.Stop()

End Sub


Private Sub Button4_Click(sender As Object, e As EventArgs) Handles Button4.Click
Application.Exit()

End Sub

Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load

End Sub
End Class

این بازی 5 ثانیه اجرا میشه پس با یه حساب 5*60 فریم در ثانیه میشه 300 فریم
پس من در نهایت 300 فریم دارم که باید ترنسپرنت بشن(البته شدن) و روی هم پرینت بشن

vardipoor.m
13-09-21, 13:20
آقا سجاد اگه c# هم باشه مشکلی نیست با C# مینویسم چون emgu.cv برای c# هم هست
مشکل اینه که از اینترنت تونستم کد های form_paint پیدا کنم و استفاده کردم دیدم چون فقط یک بار فرم paint میشه و هر بار باید refresh کنیم و تصویر قبلی حذف میشه. حالا یا من نتونستم درست استفاده کنم یا کد اینجور بوده
احتمالا باید مثلا 300 تا تصویر قبلا با هم ترکیب بشن بعد به یکباره چاپ بشن یا ...

در مورد اموزش هم منظورتون از 77 چی هست

SajjadKhati
13-09-21, 15:07
من زبان vb بلد نیستم .
اون آموزش هم توی امضام هست :

دانلود C# Programming Tutorial - آموزش برنامه نویسی با سی شارپ به زبان (Only the registered members can see the link)

قسمت 77 به بعد ، آموزش گرافیک (در زبان سی شارپ) هست .

vardipoor.m
13-09-21, 18:25
آقا من الان کد c# رو تموم کردم و برای اولین بار درست کارمیکنه
24643
فقط مشکلی که هست اینه که تو سه ثانیه رم پر میشه رم من 8 هست


using System;using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using Emgu.CV;
using Emgu.CV.Structure;
using Emgu.CV.UI;


namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
private Graphics gfx;
public Form1()
{
InitializeComponent();
gfx = this.CreateGraphics();

}

private void button1_Click(object sender, EventArgs e)
{
timer1.Start();


}

private void timer1_Tick(object sender, EventArgs e)
{
var capture = new Emgu.CV.Capture();
pictureBox1.Visible = false;
using (var nextFrame = capture.QueryFrame())
{
if (nextFrame != null)
{
pictureBox1.Image = nextFrame.ToBitmap();
}

}
Bitmap myBitmap3 = new Bitmap(pictureBox1.Image);
myBitmap3.MakeTransparent(Color.FromArgb(31, 58, 79));
Graphics g = Graphics.FromImage(myBitmap3);
pictureBox2.Image = myBitmap3;
gfx.DrawImage(pictureBox2.Image, 0, 0);
}

}

}

}

SajjadKhati
14-09-21, 00:54
آقا من الان کد c# رو تموم کردم و برای اولین بار درست کارمیکنه
24643
فقط مشکلی که هست اینه که تو سه ثانیه رم پر میشه رم من 8 هست


using System;using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using Emgu.CV;
using Emgu.CV.Structure;
using Emgu.CV.UI;


namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
private Graphics gfx;
public Form1()
{
InitializeComponent();
gfx = this.CreateGraphics();

}

private void button1_Click(object sender, EventArgs e)
{
timer1.Start();


}

private void timer1_Tick(object sender, EventArgs e)
{
var capture = new Emgu.CV.Capture();
pictureBox1.Visible = false;
using (var nextFrame = capture.QueryFrame())
{
if (nextFrame != null)
{
pictureBox1.Image = nextFrame.ToBitmap();
}

}
Bitmap myBitmap3 = new Bitmap(pictureBox1.Image);
myBitmap3.MakeTransparent(Color.FromArgb(31, 58, 79));
Graphics g = Graphics.FromImage(myBitmap3);
pictureBox2.Image = myBitmap3;
gfx.DrawImage(pictureBox2.Image, 0, 0);
}

}

}

}




نمیدونم کد پروژه تون چیه و میخواید چی کار کنید .
با کتابخونه ی Emgu.CV هم کار نکردم .

اما دقت کنید اشیاء هایی که متد dispose دارند و همچنین اینکه نیاز ندارید را بعد از استفاده ، dispose کنید . تا منابعی که unmanaged ئه مربوطه را از حافظه پاک بشه .
یعنی بعد از اتمام استفاده ، متدِ Dispose شون را فراخونی کنید یا از using (مثل کدی که خودتون دادید) ، برای اون اشیاء استفاده کنید .

شی های capture ، myBitmap3 ، و همچنین شیِ g و همچنین gfx ، از این دسته اشیاء هستند که باید بعد از اتمام استفاده ، dispose بشن که در کدتون ، dispose شدنِ این اشیاء ، مشخص نیست .


ضمنا ، در تایمر ، مقدار pictureBox1.Image و pictureBox2.Image را مشخص کردید . شیِ Bitmap هم جزء اشیاء dispose شدنی هست (که بسته به اندازه و کیفیت بیت مپ ، معمولا حافظه ی قابل توجهی را اشغال میکنن) .
وقتی که در تایمر مقدار بیت مپ ها را هر بار یه مقدار جدید میدید ، مقدارِ بیت مپ ای که در تایمر قبلی ، مقدار دهی شده بود را dispose نکردید . این میتونه یه دلیل مهم برای اشغال حافظه باشه .
ضمن اینکه شی و متغییرِ g که در کدتون گرفتید را اصلا استفاده نکردید! پس چه نیازی دارید؟


بجز حافظه ی Unmanaged ، هر چند مدیریت حافظه ی managed را خود gc ، بصورت اتوماتیک انجام میده (ممکنه فرضا رم تون پر بشه اما gc بعد از اینکه پر شد ، بسته به مقدار حافظه ای که سیستم عامل میخواد ، صرفا همون قدر را خالی کنه اما در صورتی که مقدار حافظه ی بیشتری از حافظه ی managed اش را لازم نداره) ، اما اگه خودتون بخواین بصورت فوری تر ، حافظه های managed ای که استفاده نشده هست (و اشاره گری بهش اشاره نمیکنه) ، از حافظه پاک بشه ، از متد GC.Collect استفاده کنید :

GC.Collect Method (System) | Microsoft Docs (Only the registered members can see the link_GC_Collect)

vardipoor.m
14-09-21, 02:10
پروژه رو که تو پست اول توضیح دادم
یه بازی آنلاین هست که باید توش تصویر حدس زد و به این صورته که یه مستطیل تقریبا سیاه هست (کد رنگش تو کدهام هست) که دایره توش هست از داخل دایره قسمتی از یه تصویر پیداست و این دایره مرتب توی مستطیل دور میزنه
حالا من تصویر این مستطیل رو از طریق برنامه ی manycam که وب کم مجازی هست دارم و این تصویر رو از طریق کتابخونه ی emgu.cv که کدهاش توی نت بود به C#.net اوردم
تا اینجا که مشکلی نیست ok
حالا این تصویر به خاطر اینکه همیشه بکگراندش یک رنگ هست میتونم بکگراندش رو transparent کنم و نهایت دایره میمونه که قسمتی از تصویر که باید حدس زده بشه توش مشخص هست (توی عکسی که فرستادم یه خرمالو هست)
برای هر فریم باید این کار انجام بشه و کدهای نمایش وب کم توی پیکچر باکس باید توی تایمر باشه چون emgu.cv در هر لحظه فقط یه فریم کپچر میکنه. خوب در نهایت مجموع تمام فریمها میشه تصویر مورد نظر. به همین سادگی

راستش من برنامه نویسی رو در حد دانشگاه بلدم رشتم نرم افزار بوده :1. (28):

****************************

کد های بدون استفاده رو پاک کردم
اومدم کدهای ترنسپرنت رو هم پاک کردم فقط این کد رو گذاشتم



Capture capture = new Capture();
private void timer1_Tick(object sender, EventArgs e)
{


pictureBox2.Visible = false;
using (var nextFrame = capture.QueryFrame())
{
if (nextFrame != null)
{
pictureBox1.Image = nextFrame.ToBitmap();
}


}

بازم حافظه سریع پر شد یعنی خود نمایش تصویر توی پیکچر باکس هم حافظه رو پر میکنه
میشه یه مثال توی کد خودم در مورد dispose بزنید؟

SajjadKhati
14-09-21, 11:39
پروژه رو که تو پست اول توضیح دادم
یه بازی آنلاین هست که باید توش تصویر حدس زد و به این صورته که یه مستطیل تقریبا سیاه هست (کد رنگش تو کدهام هست) که دایره توش هست از داخل دایره قسمتی از یه تصویر پیداست و این دایره مرتب توی مستطیل دور میزنه
حالا من تصویر این مستطیل رو از طریق برنامه ی manycam که وب کم مجازی هست دارم و این تصویر رو از طریق کتابخونه ی emgu.cv که کدهاش توی نت بود به C#.net اوردم
تا اینجا که مشکلی نیست ok


یعنی بخاطر رسم یه مستطیل ، از یه کتابخونه ای که به وب کم ربط داره ، استفاده کردید؟!!
نمیدونم منظورتون همینه و یا من بد متوجه شدم.
اگه همینه ، از شیِ Graphic میتونید همین کار را کنید . نیازی به استفاده از کتابخونه ی وبکم نیست!!
اگه گرافیک یا برنامه نویسی را کار نکردید و میخواید یه پروژه ی گرافیکی درست کنید ، قطعا یا شدنی نیست یا با مشکلات بسیار زیاد مواجه میشید . اگه این طوره ، حتما اول برید یاد بگیرید .

منظورم ، توصیف پروژه نبود . کد پروژه بود که خودتون باید تمام زوایاش را بررسی کنید .



حالا این تصویر به خاطر اینکه همیشه بکگراندش یک رنگ هست میتونم بکگراندش رو transparent کنم و نهایت دایره میمونه که قسمتی از تصویر که باید حدس زده بشه توش مشخص هست (توی عکسی که فرستادم یه خرمالو هست)
برای هر فریم باید این کار انجام بشه و کدهای نمایش وب کم توی پیکچر باکس باید توی تایمر باشه چون emgu.cv در هر لحظه فقط یه فریم کپچر میکنه. خوب در نهایت مجموع تمام فریمها میشه تصویر مورد نظر. به همین سادگی

راستش من برنامه نویسی رو در حد دانشگاه بلدم رشتم نرم افزار بوده :1. (28):

****************************

کد های بدون استفاده رو پاک کردم
اومدم کدهای ترنسپرنت رو هم پاک کردم فقط این کد رو گذاشتم



Capture capture = new Capture();
private void timer1_Tick(object sender, EventArgs e)
{


pictureBox2.Visible = false;
using (var nextFrame = capture.QueryFrame())
{
if (nextFrame != null)
{
pictureBox1.Image = nextFrame.ToBitmap();
}


}

بازم حافظه سریع پر شد یعنی خود نمایش تصویر توی پیکچر باکس هم حافظه رو پر میکنه
میشه یه مثال توی کد خودم در مورد dispose بزنید؟


مثال ، که در کد خودتون هم هست .
وقتی نوشتید :

using (var nextFrame = capture.QueryFrame())

، وقتی که کدِ بلاکِ using ، بسته شه ، هر منبعِ unmanaged ای که برای شیِ nextFrame در نظر گرفته شده بود ، از حافظه ، پاک میشه .
فرقی نداره که بجای استفاده از using در کد بالا ، شما متدِ dispose ئه شیِ nextFrame را فراخونی کنید . یعنی فرقی نداره که بجای کد بالا ، از nextFrame.Dispose() استفاده کنید (البته یه فرق خیلی کوچولو داره) .

اما فراخونیِ متدِ Dispose (یا استفاده از using) ، بسته به کار خودتون داره که چه زمانی کارتون با اون شی ، به اتمام رسید . اگه قبل از اتمام ، Dispose کنید ، امکان زیادی داره که زمان استفاده از شیِ مورد نظر ، با مشکل (و ارور زمان اجرا یا درست عمل نکردنِ شی و هر مشکل دیگه ای) مواجه بشید .
اون شیِ capture ، شی ای Dispose شدنی هست که زمانی که کارتون باهاش تمام شد ، باید Dispose اش کنید (که خودتون میدونید ، چه زمانی کارتون باهاش تمام میشه) .


اینکه رم تون هنوز پر میشه ، قبلا اشاره کردم .
Image ها هم Dispose شدنی هستند .
در pictureBox1.Image ، یه Image ئه جدیدی را در هر بار اجرای تایمر ایجاد میکنید که در اجرای بعدی ، Image های قبلی ای را که ایجاد کرده بودید ،در حافظه میذارید باقی بمونه و Dispose شون نمیکنید .
ضمن اینکه برای پاک کردنِ سریعترِ حافظه های managed هم (که قبلا گفته بودم) ، از متد GC.Collect() استفاده نکردید .

vardipoor.m
14-09-21, 12:58
یعنی بخاطر رسم یه مستطیل ، از یه کتابخونه ای که به وب کم ربط داره ، استفاده کردید؟!!
خیر، درسته این کتابخونه برای پردازش تصویر هست اما من فقط تصویر وب کم خودم رو توی یه پیکچر باکس نمایش میدم همین
تصویر من، تصویر یه بازی آنلاین هست که یه کادر مستتطیل شکل هست. فرض کنید دارید از مانیتور کامپیوتر فیلم میگیرید همین

الان یه چیزی متوجه شدم اینکه حافظه اشغال میشه اما به یه حدی که رسید دوباره خالی میشه
24644
حالت عادی روی 64 هست و موقع اجرای برنامه تا 79 میرشه و دوباره برمیگرده 64، این عادی هست؟
و اون موقع ارور داده بود چون رم من حدود 85 درصد پر بود و برنامه رو که اجرا کردم پر شد.

من از dispose استفاده کردم اما هیچ تاثیری نداشت
این دو خط رو به تایمر اضافه کردم



myBitmap3.Dispose();
pictureBox1.Dispose();

SajjadKhati
14-09-21, 14:12
خیر، درسته این کتابخونه برای پردازش تصویر هست اما من فقط تصویر وب کم خودم رو توی یه پیکچر باکس نمایش میدم همین
تصویر من، تصویر یه بازی آنلاین هست که یه کادر مستتطیل شکل هست. فرض کنید دارید از مانیتور کامپیوتر فیلم میگیرید همین

الان یه چیزی متوجه شدم اینکه حافظه اشغال میشه اما به یه حدی که رسید دوباره خالی میشه
24644
حالت عادی روی 64 هست و موقع اجرای برنامه تا 79 میرشه و دوباره برمیگرده 64، این عادی هست؟
و اون موقع ارور داده بود چون رم من حدود 85 درصد پر بود و برنامه رو که اجرا کردم پر شد.


منظورتون اینه که بصورت پیش فرض ، برنامه تون از 64 مگابایت از رم استفاده میکنه؟
این ربطی به اصولی بودن کدتون نداره .
برنامه تون ممکنه 40 مگ (یا کمتر) از رم را اشغال کنه اما کدی نوشته باشین که یه متغییر و شی ای را تعریف کرده باشه اما زمانی که ازش استفاده نمیکنین و یا حتی بعد از بستنِ برنامه تون ، حافظه ای که براش گرفته شده را به سیستم عامل ، پس نده و کماکان اشغال کنه .
که جریانش (جریان آزادسازی حافظه های unmanaged) را توضیح دادم .



من از dispose استفاده کردم اما هیچ تاثیری نداشت
این دو خط رو به تایمر اضافه کردم



myBitmap3.Dispose();
pictureBox1.Dispose();



نگفتم که استفاده از Dispose ، اون میزان از آزادسازی ای از رم که مد نظرتون هست ، تاثیر داره یا تاثیر نداره .
Dispose ، باعث میشه منابع حافظه که برای حافظه های نوع unmanaged (نه همه ی نوع حافظه که شامل managed هم میشه) برای اون شی استفاده میشه ، از حافظه ، آزاد بشه .

مقدار این حافظه ی unmanaged برای اون شی ، میتونه در حد چند بایت باشه ، یا در حد کیلو یا شاید مگا یا گیگابایت . مقدار حافظه ی unmanaged برای اون شی را فقط کسی که کتابخونه اش را ساخت ، میدونه .
وظیفه ی شما به عنوان برنامه نویس فقط اینه که اگه شیِ جدیدِ Dispose شدنی ای را به متغییری اختصاص دادین ، بعد از اتمام کارتون با اون متغییر ، این متد را فراخونی کنین تا منابع حافظه ی unmanaged اش از حافظه ، پاک شه . اما اینکه چه حجمی از حافظه ی unmanaged ، به این شی اختصاص داده شده بود ، فقط به کسی که کتابخونه اش را نوشت ، مربوطه .


درباره ی آزادسازیِ سریعترِ منابع managed (ای که اشاره گری بهش اشاره نمیکنه) هم که گفتم .
با این حال ، اینها ، این مقادیر را از حافظه پاک میکنن . نه صرفا از حافظه ی فیزیکی (رم) . بلکه برنامه ها از حافظه ی مجازی استفاده میکنن (شامل هم رم و هم pagefile ئه حافظه ی جانبی) .
اگه فرضا تغییرات حافظه را از قسمت نمودار memory در task manager پیگیری میکنید ، اون نمودار فقط تغییرات حافظه ی فیزیکی (رم) را نشون میده .

-----------------

دقت کنید که pictureBox1 را (مثل همه ی اشیاء دیگه) ، زمانی Dispose کنید که :
- اولا ، خودتون یه شیِ جدیدی از pictureBox ساخته باشید . یعنی زمانی که خودتون (new PictureBox را نوشته باشید) .
یعنی زمانی که pictureBox را زمانی که فرم را طراحی میکردید (زمانِ design در form) ، اضافه کردید ، برای این نوع pictureBox ، نیازی به Dispose کردن ، نیست .
- و دوما به کنترلِ pictureBox1 ، نیازی ندارید . یعنی فرضا میخواید pictureBox1 را از روی form تون حذف کنید . یا اینکه میخواید از برنامه بیرون برید .

بنابراین اگه احتمالا زمان طراحی فرم ، pictureBox1 را اضافه کردید (که احتمالا همینطوره) ، نیازی به اجرای کدِ pictureBox1.Dispose() نیست .

و همچنین ، زمانی شیِ myBitmap3 را هم Dispose کنید که دیگه در جای دیگه ای ازش استفاده نمیکنید و لازم تون نمیشه .
یعنی مثلا زمانی شیِ myBitmap3 را هم Dispose کنید که در pictureBox1.Image (یا هر جای دیگه) ، دیگه از شیِ myBitmap3 استفاده نمیکنید .

vardipoor.m
14-09-21, 14:19
اون تصویر که مقدار رم رو نشون میده تصویر برنامه hwinfo است که مقدار اشغال شده رو به درصد نشون میده یعنی 64 درصد که چون رم من 8 گیگ هست میشه حدود 5 گیگ

آقا من چون مثل شما زیاد تخصصی نیستم اودم با آزمون خطا پیدا کردم
یه کد رفرش فرم زدم

this.refresh()
دیم که اصلا از 60 بالا نمیره!
به نظزتون چطور میتونم هندلش کنم

SajjadKhati
14-09-21, 16:22
اون تصویر که مقدار رم رو نشون میده تصویر برنامه hwinfo است که مقدار اشغال شده رو به درصد نشون میده یعنی 64 درصد که چون رم من 8 گیگ هست میشه حدود 5 گیگ


برنامه ی hwinfo چرا؟!!
شما باید برنامه ی خودتون را دقیق رصد کنید که چه نوعی ، فعلا داره چه مقدار حافظه را مصرف میکنه .
در ویژال استودیو ، از قسمت Memory Usage در Diagnostic Tools ، باید snapshot بگیرید تا متوجه بشید .
snapshot را هم حداقل 2 بار باید بگیرید . یک بار break point را قبل از اینکه کد مورد نظرتون (که از رم زیادی استفاده میکنه) ، بذارید و snapshot بگیرید و بار دوم ، f10 را بزنید تا اجرای کد ، به کدهای مورد نظرتون (که رم بالایی را استفاده میکنن) ،برسه و دومین snapshot را بگیرید و مقدار حافظه ی تمام اشیاءِ اون نوع را مقایسه کنید :

Measure memory usage in your apps - Visual Studio (Windows) | Microsoft Docs (Only the registered members can see the link)

و

Analyze memory usage in the Performance Profiler - Visual Studio (Windows) | Microsoft Docs (Only the registered members can see the link)

دقت کنید که در snapshot ، صرفا اطلاعات مقدار حافظه ای که برای تمامِ اشیاهایی از برنامه تون ، از یک نوعِ خاصی هست (مثلا تمام اشیایی که از نوع Bitmap هست) را بهتون میده .

برای جستجوی نوع داده (گرفتنِ مقدار حافظه ی اشغالی برای تمام اشیایی که از اون نوع هست) هم وقتی روی لینکِ snapshot ای کلیک کردید و به تصویرِ زیر رسیدید (که در لینک اول در بالا که گذاشتم ، توضیح میده) ، یه کادرِ جستجو ، در قسمت بالا ، سمت راست صفحه داره که میتونید نامِ نوعِ تمام اشیایی از اون نوع که در کل برنامه تون وجود داره را جستجو کنید که چقدر از حافظه مصرف میکنن (فرضا نوعِ Bitmap یا نوعِ Image را در این کادر میتونید بنویسید) (کادر در تصویر زیر مشخص هست) :


Only the registered members can see the link ?view=vs-2019





آقا من چون مثل شما زیاد تخصصی نیستم اودم با آزمون خطا پیدا کردم
یه کد رفرش فرم زدم

this.refresh()
دیم که اصلا از 60 بالا نمیره!
به نظزتون چطور میتونم هندلش کنم


ربطی به حافظه ی اشغالی برنامه تون نداره .
اگه this ، شیِ فرم تون هست ، فرم تون را فقط ریفرش میکنه (نه کار دیگه ای) .
ممکنه در این لحظه ، گربگ کالکتور هم بخواد داده های managed را هم collect کنه (که قبلا بهتون گفتم که چجوری با کد این کار را کنید) .
به هر حال ، این کد ربطی به حافظه نداره .

-------------

لطفا اول کد نویسی سی شارپ را طبق منبعی که دادم (فیلم آموزش سی شارپ یا منابع انگلیسی) و همچنین کتابخونه های مورد نظرتون را (مثل گرافیک ، یا اون کتابخونه ای که برای وبکم بود و ...) که باز هم منابع یادگیری گرافیک (فیلم آموزش) را دادم ، اول کامل یاد بگیرید . بعد از یادگیری ، مشکلاتی که براتون بوجود میاد را بپرسید .

این طور که نمیشه بدون یادگیری دقیق بخواید برید سروقت پروژه . بسیاری سئوالات (از مبتدی گرفته تا بالاتر) براتون پیش میاد که با یادگیری زبان سی شارپ و کتابخونه ها ، این سئوالات از همون ابتدا براتون توجیه و قابل حل میشه .

پست قبلی (پست 13) ام را هم ویرایش کردم (بعد از خط چین) ، بخونید .


تاپیکی که اسمش را "شارپ کردن تصویر در c#" گذاشتید ، این انجمن ، با کاراکترهایی مثل # و اینها ، مشکل داره . واسه ی همین تاپیک تون نمایش داده نمیشه . بجای c# ، صرفا سی شارپ بنویسید (کلا کاراکتر # نداشته باشه) .

vardipoor.m
15-09-21, 00:58
برنامه hwinfo از قبل نصب بود و کلا همیشه دارمش
همونطور که بهتون گفتم حتی کد نمایش وب کم تو پیکچر باکس که همون چند خط اول هست رو به تنهایی هم بزنم حافظه زیاد میبره. حافظه رو با روش شما چک کردم و هر بار 867 مگ حافظه میگیره و دوباره ازاد میشه! اگه چند خط کد بیت مپ رو اضافه کنم که ترنسپرنت کنم و چاپ کنم رو فرم هر بار 1.9 گیگ حافظه میگیره دوباره آزاد میکنه! که فکر میکنم زیاد هست چون من حساب کردم چون دوربینم 60 فریم داره و تایمر هم 17 میلی ست شده یعنی هر ثانیه حدود 60 فریم ثبت میشه توی پیکچر باکس و چون 25 ثانیه طول برد تا حافظه آزاد شد پس 25*60= 1500 فریم توی پیکچر باکس ثبت شده که شده 867 مگ.
24654
این عکس انالایز حافظه برای این کد هست

private void timer1_Tick(object sender, EventArgs e)
{

using (var nextFrame = capture.QueryFrame())
{
if (nextFrame != null)
{
pictureBox1.Image = nextFrame.ToBitmap();

}

}
}

و میدونم الان میگید باید اسنپ شات میگرفتید اما گرفتم هیچ اشاره ای به هیچ چیز نکرده که شما گفتید مثلا بیت مپ یا پیکچر باکس من هرچه گشتم ندیدم، موجوده اگه گفتید اپلود میکنم
مگه شما نمیگید با استفاده از using بعد از اینکه کارش تمام شد حافظه رو آزاد میکنه الان فقط using تو کد هست ولی انگار فریم ها رو رو هم توی پیکچر باکس ذخیره میکنه و فریم قبلی رو پاک نمیکنه

من منظورم این نیست که این کد (()this.refresh)مدیریت حافظه میکنه اما دفدش کردن باعث میشه حافظه خالی بشه با همین کد بالا تست کردم به این صورت


private void timer1_Tick(object sender, EventArgs e)
{

using (var nextFrame = capture.QueryFrame())
{
if (nextFrame != null)
{
pictureBox1.Image = nextFrame.ToBitmap();
this.Refresh();
}

}
}

نتیجه شد این
24655
فقط 200 مگ حافظه نهایت

gc collector هم دارم سرچ میکنم
فقط الان راهی هست سریع بشه توی تایمر بعد از هر بار که پیکچرباکس یه عکس توش قرار گرفت خالی بشه؟
یه همچین چیزی
picturebox1.image=null;

vardipoor.m
15-09-21, 01:18
آقا gc.collect() نوشتم عجب چیزیه
نمیزاره از 46 مگ میشتر بشه! :1. (31):


Capture capture = new Capture();


private void timer1_Tick(object sender, EventArgs e)
{
using (var nextFrame = capture.QueryFrame())
{
if (nextFrame != null)
{
pictureBox1.Image = nextFrame.ToBitmap();
}
}
Bitmap myBitmap3 = new Bitmap(pictureBox1.Image);
Color pixle1 = myBitmap3.GetPixel(100, 100);
Color pixle2 = myBitmap3.GetPixel(400, 250);

if (pixle1 == Color.FromArgb(24, 53, 73) || pixle2 == Color.FromArgb(24, 53,73))
{
myBitmap3.MakeTransparent(Color.FromArgb(24, 53, 73));
gfx.DrawImage(myBitmap3, 1, 1);
GC.Collect();
}
else
{
GC.Collect();
}


}


فقط یه سوال البته میدونم زیاد سوال پرسیدم ببخشد شما رو هم اذیت کردم این gc.collect() چطور فریم هایی که توی فرم چاپ شدن رو پاک نمیکنه یعنی برنامه من هیچ ایرادی توش نیفتاد ولی حافظه خالی شد! ظاهرا اشغال حافظه مال همین عکس های چاپ شده توی فرم و توی پیکچرباکس هستن یا من اشتباه میکنم؟

SajjadKhati
15-09-21, 16:33
برنامه hwinfo از قبل نصب بود و کلا همیشه دارمش
همونطور که بهتون گفتم حتی کد نمایش وب کم تو پیکچر باکس که همون چند خط اول هست رو به تنهایی هم بزنم حافظه زیاد میبره.



سلام
من هم قبلا بارها گفتم .
کدِ زیرتون :



nextFrame.ToBitmap()


یه شیِ بیت مپ جدید ایجاد میکنه (که این شی را به pictureBox1.Image اختصاص دادید) .
هر بار هم در تایمر ، ازش استفاده میکنید .
اما در تایمرِ بعدی که از این شی استفاده کردید ، این شیِ بیت مپِ قبلی را گفتید فورا از حافظه پاک کنه؟
نه .
بنابراین این شی های بیت مپ ، تجمع میشه و رم تون پر میشه .






حافظه رو با روش شما چک کردم و هر بار 867 مگ حافظه میگیره و دوباره ازاد میشه!



روش من چیه؟!!
من از خودم روشی ایجاد نکردم .
روال برنامه نویسی .net هست .

ببینید ، از هر شی ای بسازید و استفاده کنید (چه شیِ بیت مپ مثل کد بالا و هر شی ای) ، 2 حالت داره :
- حافظه ی managed ، برای اون شی اختصاص داده میشه .
- ممکنه هم علاوه بر این ، از حافظه ی Unmanaged هم استفاده بشه (این نوع اشیاء ، متد Dispose دارن) .


حالا برای پاک سازیِ هر نوع از این حافظه ها برای اون شی ، روشی متفاوت وجود داره :
- پاکسازی حافظه ی Unmanaged ، اغلب ، با متد Dispose انجام میشه .
- پاکسازیِ حافظه ی managed ، اول باید هیچ اشاره گری به اون شی ، اشاره نکنه و بعد اینکه با متد GC.Collect ، میتونین روند حذف حافظه های managed (ای که اشاره گری بهش وجود نداره) را درخواست بدین که در اولین فرصت این کار انجام بشه .
اگه از متد GC.Collect استفاده نکنین ، گربگ کالکتور ، این روند که پاکسازی حافظه های managed (ای که اشاره گری بهش وجود نداره) را میذاره برای وقتی که خودش به صلاح بدونه (مثلا موقعی که برنامه تون را میبندید یا زمانی که حافظه ی سیستم ، پر شده باشه و ...) . یعنی اگه از اون متد استفاده نکنین ، گربگ کالتور ، عجله ای در بررسی و آزادسازیِ حافظه های managed (ای که اشاره گری بهش وجود نداره) ، نمیکنه .







اگه چند خط کد بیت مپ رو اضافه کنم که ترنسپرنت کنم و چاپ کنم رو فرم هر بار 1.9 گیگ حافظه میگیره دوباره آزاد میکنه! که فکر میکنم زیاد هست چون من حساب کردم چون دوربینم 60 فریم داره و تایمر هم 17 میلی ست شده یعنی هر ثانیه حدود 60 فریم ثبت میشه توی پیکچر باکس و چون 25 ثانیه طول برد تا حافظه آزاد شد پس 25*60= 1500 فریم توی پیکچر باکس ثبت شده که شده 867 مگ.



فاصله ی زمانی تایمر (که 17 میلی ثانیه گذاشتین) ، اولا که فکر نکنید دقیقا هر 17 میلی ثانیه اجرا میشه .
بلکه این فاصله ی زمانی ، حتی اگه پردازنده مشغول نباشه ، دقیق نیست . یعنی اجراش ،اندکی ممکنه جلو و عقب بشه .
اگه عواملی مثل اِشغالِ پردازنده هم وجود داشته باشه که احتمال داره تاخیرش بیشتر بشه .

اما در کل تاخیرش خیلی زیاد نیست .
ولی اگه برای زمان های طولانی بخواین روی فاصله ی زمانیِ تایمر ، حساب باز کنید ، این تاخیر خودش را کاملا واضح نشون میده . مثلا برای یکسان کردنِ رقص نور با آهنگی و یا برای هماهنگ کردن با فریم های فیلم ، هر چی فیلم و آهنگ طولانی تر باشن ، تاخیرش بیشتر خودش را نشون میده .




24654
این عکس انالایز حافظه برای این کد هست

private void timer1_Tick(object sender, EventArgs e)
{

using (var nextFrame = capture.QueryFrame())
{
if (nextFrame != null)
{
pictureBox1.Image = nextFrame.ToBitmap();

}

}
}

و میدونم الان میگید باید اسنپ شات میگرفتید اما گرفتم هیچ اشاره ای به هیچ چیز نکرده که شما گفتید مثلا بیت مپ یا پیکچر باکس من هرچه گشتم ندیدم، موجوده اگه گفتید اپلود میکنم



اون لینکی که دادم ، گفته بود .
نمیدونم از نسخه ی چند از ویژال استودیو استفاده میکنید .
توی نسخه ی 2019 اش ، وقتی برنامه را اجرا کنید ، سمت راستِ ویژال استودیو ، پنجره ی Diagnostic Tools باز میشه . (اگه این پنجره نشون داده نشد ، از قسمت Debug > Windows > Show Diagnostic Tools انتخاب کنید) .
تقریبا قسمت زیرِ این پنجره ، سربرگِ Memory Usage را انتخاب کنید . روی آیکون دوربین که کنارش نوشته Take Snapshot کلیک کنید .
در ستون Heap Size ، روی عددی که در این ستون داده شد ، کلیک کنید .
در پنجره ی باز شده (شبیه تصویر در لینک 15) میاد که نوعِ مورد نظرتون را میتونید جستجو کنید . منتها قبل از جستجوی نوع مورد نظرتون ، کدِ اشیاء و متغییرهایی که از اون نوع هستن ، باید اجرا شده باشن . وگرنه در لیست نمیان (که بدیهی هست) .






مگه شما نمیگید با استفاده از using بعد از اینکه کارش تمام شد حافظه رو آزاد میکنه



نه .
گفتم فقط حافظه های Unmanaged از اون شی را پاک میکنه . (اون هم خودش پاک نمیکنه ، درخواست پاک کردن را میده) .




الان فقط using تو کد هست ولی انگار فریم ها رو رو هم توی پیکچر باکس ذخیره میکنه و فریم قبلی رو پاک نمیکنه



اولا که در کد بالای شما ، فقط برای شیِ nextFrame ، درخواستِ حذفِ حافظه های Unmanaged (ای که برای متغییرهاش اختصاص داده شد) را دادین .
حافظه ی managed را نگفتین که فوری حذف کنه . بنابراین پاک کردن حافظه ی managed ، میمونه تا وقتی که گربگ کالکتور صلاح بدونه که معمولا مواقع لزوم یا مواقع اضطراری هست .

دوما (همونطور که در بالا و هم در پست های قبل گفتم) ، شما هیچ کاری با شیِ خروجیِ متدِ زیر در کدتون نکردین :



nextFrame.ToBitmap()



نگفتین که حافظه ی Bitmap ای که در تایمر قبلی تولید شده بود را اولا نه حافظه ی Unmanaged اش را پاک کردین (چون متد Dispose از این شی را اجرا نکردین) و نه حافظه ی managed اشس را گفتین که فوری پاک کنه (زمانی که بهش نیاز ندارین) .

بنابراین اولا علاوه بر اینکه این شی را به پروپرتیِ pictureBox1.Image اختصاص میدین ، در متغییرِ دیگه ای از این نوع هم اختصاص بدین تا در تایمر بعدی که بهش نیاز ندارین ، Dispose اش کنید :



private Image previouseImageForDisposing;
private void timer1_Tick(object sender, EventArgs e)
{
if (this.previouseImageForDisposing != null)
{
this.previouseImageForDisposing.Dispose();
this.previouseImageForDisposing = null;
}



using (var nextFrame = capture.QueryFrame())
{
if (nextFrame != null)
{
pictureBox1.Image = nextFrame.ToBitmap();
this.previouseImageForDisposing = pictureBox1.Image;
}


}
}


اون متغییر previouseImageForDisposing ، حتما باید null بشه تا اشاره گرش را از دست بده وگرنه با فراخونیِ متد GC.Collect ، شی اش (یا هر شی ای که اشاره گر داره) ، از حافظه پاک نمیشه .


دوما ، اون متد GC.Collect را باید اجرا کنید تا بگید حافظه ی managed از تمام اشیایی که اشاره گر ندارن ، در اولین فرصت ، پاک بشن (که این کار را هم نکردید) .
دقت کنید که حتما فراخونیِ متدِ GC.Collect را بعد از کدی که متغییرِ previouseImageForDisposing در بالا را null کردیم ، و همچنین بعد از کد زیر در بالا ، انجام بشه :



pictureBox1.Image = nextFrame.ToBitmap();


چون اشاره گرهای به شیِ Bitmap ئه قبلی (که پروپرتیِ pictureBox1.Image و متغییر previouseImageForDisposing که بهش اشاره میکردن) ، باید قبلش از دست بره .

لازم هم نیست حتما در هر تایمر ، متد GC.Collect را فراخونی کنید . البته مشکلی نیست و میتونین انجام بدین . اما بهتره که هر وقت حافظه ی کاربر نهایی پر شد یا از اشغال حافظه ، از مقدار خاصی بیشتر شد انجام بدین . چون برای پردازنده ، سربار داره (ولو سربارش ، اندک یا زیاد باشه) .




من منظورم این نیست که این کد (()this.refresh)مدیریت حافظه میکنه اما دفدش کردن باعث میشه حافظه خالی بشه با همین کد بالا تست کردم به این صورت


private void timer1_Tick(object sender, EventArgs e)
{

using (var nextFrame = capture.QueryFrame())
{
if (nextFrame != null)
{
pictureBox1.Image = nextFrame.ToBitmap();
this.Refresh();
}

}
}

نتیجه شد این
24655
فقط 200 مگ حافظه نهایت

gc collector هم دارم سرچ میکنم



خالی نمیکنه .
شاید با ریفرش کردن ، فرصتی برای گربک کالکتور باشه یا گربگ کالکتور پیامی دریافت کنه که حافظه را اون موقع جمع کنه . مدیریت حافظه ی mmanaged ، فقط توسط گربگ کالکتور انجام میشه .
بالاخره روش اصولی نیست.




فقط الان راهی هست سریع بشه توی تایمر بعد از هر بار که پیکچرباکس یه عکس توش قرار گرفت خالی بشه؟
یه همچین چیزی
picturebox1.image=null;


توضیح دادم .

SajjadKhati
15-09-21, 16:50
آقا gc.collect() نوشتم عجب چیزیه
نمیزاره از 46 مگ میشتر بشه! :1. (31):


Capture capture = new Capture();


private void timer1_Tick(object sender, EventArgs e)
{
using (var nextFrame = capture.QueryFrame())
{
if (nextFrame != null)
{
pictureBox1.Image = nextFrame.ToBitmap();
}
}
Bitmap myBitmap3 = new Bitmap(pictureBox1.Image);
Color pixle1 = myBitmap3.GetPixel(100, 100);
Color pixle2 = myBitmap3.GetPixel(400, 250);

if (pixle1 == Color.FromArgb(24, 53, 73) || pixle2 == Color.FromArgb(24, 53,73))
{
myBitmap3.MakeTransparent(Color.FromArgb(24, 53, 73));
gfx.DrawImage(myBitmap3, 1, 1);
GC.Collect();
}
else
{
GC.Collect();
}


}





روش Dispose کردنِ شیِ خروجیِ متدِ زیر که در تایمرهای قبلی ایجاد شده بودن :



nextFrame.ToBitmap()


را در پست قبلی (پست 18) ، گفتم .

علاوه بر این ، چندین بار دارم میگم که اشیایی که متد Dispose دارن را بعد از استفاده ، Dispose کنید . شیِ myBitmap3 تون هم از این نوع هست و Dispose نکردید .
ضمنا ، نیازی به دو بار اجرای متد GC.Collect نیست . نهایتا میتونید ، یکبار ، اون هم در خط آخر بنویسید . دیگه if و else گذاشتن برای این متد ، معنا نداره .
ضمن اینکه بهتره که این متد را زمانی اجرا کنید که فرضا مقدار اشغالی حافظه ، از یه حدی بیشتر بشه . هر چند مشکلی نداره اما قضیه ی سربار را که گفتم .




فقط یه سوال البته میدونم زیاد سوال پرسیدم ببخشد شما رو هم اذیت کردم این gc.collect() چطور فریم هایی که توی فرم چاپ شدن رو پاک نمیکنه یعنی برنامه من هیچ ایرادی توش نیفتاد ولی حافظه خالی شد! ظاهرا اشغال حافظه مال همین عکس های چاپ شده توی فرم و توی پیکچرباکس هستن یا من اشتباه میکنم؟


چون گربک کالکتور ، فقط حافظه های managed ای را پاک میکنه که اشاره گری بهش وجود نداشته باشه .
وقتی مینویسید :



pictureBox1.Image = nextFrame.ToBitmap();


یعنی شیِ Bitmap ای که از خروجیِ متدِ nextFrame.ToBitmap ایجاد میشه ، اشاره گر داره .
اشاره گرش هم پروپرتیِ pictureBox1.Image هست .
بنابراین ، گربگ کالکتور ، حافظه ی managed ئه مربوط به این شی را پاک نمیکنه .

vardipoor.m
17-09-21, 21:53
جا داره از آقا سجاد تشکر کنم به خاطر این همه راهنمایی ممنونم

ravegoat
24-10-21, 17:33
با سلام،

بنده چند تا نکته به این بحث اضافه کنم که شاید کمک بیش تری به دوستانی بکنه که به مشکل مشابه بر می خورن:

۱− تابع Dispose توسط برنامه نویس نوشته میشه (و نه این اینکه توسط JIT Compiler به شکل خودکار ایجاد بشه) و هدفش اینه که منابع Unmanaged رو پاک کنه (سجاد هم بار ها بهش اشاره کرد). پس برنامه نویس موظفه که اگه کلاسی می نویسه که منابع بومی داره، از اینترفیس IDisposable پیروی کنه و تابع Dispose رو به شکلی پیدا سازی کنه که منابع بومی رو پاک کنه. مضافا بر اون برخلاف Finalizer که به شکل خودکار در آخر عمر اشیا صدا زده میشه، تابع Dispose باید به صراحت توسط کد صدا زده بشه وگرنه کسی دیگه این کار رو نمی کنه.

۲− کنترل PictureBox عکس های رو که بهش اختصاص داده میشن ذخیره نمی کنه. تو تمام مثال های مطرح شده در این تاپیک وقتی شما عکس رو به PictureBox تخصیص میدید، عکس قبلی دیگه به پایان عمر خودش میرسه و دیر یا زود توسط GC جمع آوری میشه. نکته اینه که اگه نگاهی به سورس دات نت (Only the registered members can see the link.Drawing/commonui/System/Drawing/Image.cs,5f4dd2af374d1f3c) بیاندازید، GC پس از مدتی منابع بومی و هم منابع غیر بومی شی Bitmap رو آزاد می کنه (حالا ممکنه این روند در حالت شما تا زمانی طول بکشه که میزان حافظه به حدود ۹۰۰ مگابایت برسه).

۳− این که چرا Refresh کردن فرم به آزادسازی حافظه کمک می کنه بنده نمی تونم دلیلی براش بیارم. این متد دو دستور لایه پایین رو اجرا می کنه که یکیش مستقیما به کرنل هستش. یه احتمال می تونه این باشه چون تمام بچه های فرم بی اعتبار میشن و مجاورت فضای Heap مربوط به PictureBox (به عنوان یکی از بچه های فرم) هم نامعتبره، کل عکس های قبلی که در اون مجاورت تخصیص پیدا کرده بودن (امیدوارم حداقل ویندوز عقلش رسیده باشه که این کار رو بکنه) به یک باره آزاد میشن. میشه این فرضیه رو تست کرد.

۴− صدا زدن صریح GC.Collect عموما کار درستی نیست. نکته اینه که شما با این کار اشیای نسل صفرم تا نسل دوم رو پاک می کنید. این در حالیه که عناصر گرافیکی معمولا برای مدت طولانی تری در نسل دوم می مونن تا در فرخوانی های بعدی دات نت مجبور نباشه اونا رو ایجاد کنه ولی شما با این کار هر چی دات نت رشته کرده بود رو پنبه می کنید. جدای از اون، نشت حافظه تنها به معنای افزایش افسار گسیخته ی حافظه نیست بلکه شامل مفهمومی به نام GC Pressure هم میشه که شما با فرخوانی GC.Collect در هر ۱۷ میلی ثانیه می تونید به اون دامن بزنید.

۵− مخزن کد EmguCV همین مثال (Only the registered members can see the link) رو به شکل Zero Copy پیدا کرده (که روش درستش هم هست چون لایه ی پایین OpenCV دقیقا این طوری کار می کنه). متاسفانه بنده در حال حاضر ابزاری ندارم که این کد رو تست کنم ولی احتمالا اصلا هیچ مشکل حافظه ای به وجود نیاره.