Προς το περιεχόμενο

C# επεξεργασία εικόνας


nikos2027

Προτεινόμενες αναρτήσεις

Δημοσ.

Παίδες χαιρετώ!

Θέλω να κάνω το εξής και δεν μπορώ.

Εμφανίζω μια εικόνα σε μια καινουργια

εικόνα. Μετά θέλω ο χρήστης να μπορεί

με το mouse να επιλέξει μια περιοχή από

την εικόνα. Αυτή η περιοχή θέλω να

μαρκαρεται (έιτε με πλαίσιο είτε με διάφανο

χρώμα - highlight).

Γνωρίζει κανεις

1) αν υπάρχει αντίστοιχη λειτουργία στο

visual studio .net

2) αν μπορεί να μου εξηγήσει. (αν γίνεται

και με κανενα μικρό παραδειγματάκι.)

Ευχαριστώ πολύ για βοηθεια προκαταβολικά!

Δημοσ.

Λοιπόν, Νίκο, καταρχήν θα πάρεις ένα βιβλίο για c#. Αν έχεις ήδη, ξαναδιάβασε το χωρίς να αφήσεις κενά. Κατόπιν θα πας στο http://www.codeproject.com/KB/cs/ όπου θα βρείς παρα πολλά tutorials και samples από εύκολα μέχρι πολύ προχωρημένα. Από το ίδιο site θα βρείς και πολλά πράγματα για το GDI+ και καλές πρακτικές για γραφικά (http://www.codeproject.com/KB/GDI-plus/)

Με το να σου δώσω έτοιμο αυτό που ζητάς, δεν σε βοηθάει σε τίποτα αυτή τη στιγμή.

Καλή συνέχεια

Δημοσ.

Αρχικα Bilco εχεις δικιο για το βιβλίο...

 

Δουλευω σε μια εταιρια και ηθελα

αμεσα λυση στο προβλημα μου.

Απο προγραμματισμο ξερω απλα ειναι

η πρώτη φορα που αχολουμαι με

visual περιβαλλον. Μεχρι τώρα

εκανα εφαρμογες σε pure C.

Thanks για το site δεν το

ηξερα. Οσο για το προβλημα

δοκιμασα να το κανω Pixel pixel

αλλα καπου χτυπά error.

Αν εχει καποιο πχ θα βοηθουσες

πολύ. Ευχαριστω ξανα για το

ενδιαφερον.

Δημοσ.

Κάποια βασικά πράγματα για το πως γίνεται.

Κρατάμε δύο bitmap

Bitmap _bitmap, _buffer;

έστω σε 24-bit rgb φορματ με ίδιο μέγεθος και τα δύο (αν η εικόνα που ανοίγουμε είναι σε index φορματ τη μετατρέπουμε)

Το πρώτο θα κρατάει την αρχική εικόνα και το δεύτερο το χρησιμοποιούμε για να μην σχεδιάζουμε κατευθείαν πάνω στο control αλλά πρώτα στο buffer και μετά render το buffer στο control. Για να υπολογίζουμε το παρ/μο επιλογής πρέπει να κρατάμε ένα σημείο (Point _lastClickPos) από το MouseDown event και στο MouseMove event (σε ότι ακολουθεί είμαστε στον handler του MouseMove)

Rectangle rct = new Rectangle((_lastClickPos.X < e.Location.X ? _lastClickPos.X : e.Location.X),

(_lastClickPos.Y < e.Location.Y ? _lastClickPos.Y : e.Location.Y),

Math.Abs(_lastClickPos.X - e.Location.X), Math.Abs(_lastClickPos.Y - e.Location.Y));

όπου e.Location είναι η τρέχουσα θέση του ποντικιού. Επίσης κρατάμε στο Rectangle _prevRect; την προηγούμενη επιλογή για λόγους ταχύτητας στο rendering. Δηλαδή όταν περνάμε το bitmap στον buffer δεν κοπιάρουμε όλο το bitmap αλλά μόνο την συγκεκριμένη περιοχή

Graphics gc = Graphics.FromImage(_buffer);

gc.SetClip(_prevRect);

gc.DrawImageUnscaled(_bitmap, 0, 0);

gc.ResetClip();

ουσιαστικά για να "διαγράψουμε" την προηγούμενη επιλογή αφού δεν έχει αλλάξει κάτι άλλο.

Κατόπιν θα γεμίσουμε την νέα επιλογή με ένα διάφανο κίτρινο χρώμα

gc.CompositingMode = System.Drawing.Drawing2D.CompositingMode.SourceOver;

gc.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighSpeed;

Brush brush = new SolidBrush(Color.FromArgb(100, 255, 255, 0))

gc.FillRectangle(brush, rct);΄

Έτσι το buffer είναι έτοιμο να το περάσουμε στην οθόνη

gc = Graphics.FromHwnd(pbPic.Handle);

όπου το pbPic είναι το control πάνω στο οποίο θα κάνουμε render

Πάλι κλιπάρουμε και αυτή τη φορά εκτός από την προηγούμενη επιλογή και την τρέχουσα

gc.SetClip(_prevRect);

gc.SetClip(rct, System.Drawing.Drawing2D.CombineMode.Union);

και render

gc.DrawImageUnscaled(_buffer, 0, 0);

gc.Dispose();

Τέλος κρατάμε την τρέχουσα επιλογή για την επόμενη φορά

_prevRect = rct;

αφού την "φαρδύνουμε" λίγο

_prevRect.Inflate(8, 8);

γιατί αλλιώς μπορεί να έχουμε "σκουπιδάκια" στην εικόνα μας.

Σε γενικές γραμμές that's all

 

Υ.Γ. Να με συγχωρείς αν κάνω λάθος, αλλά κάτι δεν μου πάει καλά με αυτά που λες

Δημοσ.
1000 Ευχαριστώ φιλε!

Παρακαλώ.

Θα μου κάνεις μια χάρη και εμένα; Θα ήθελα να ποστάρεις εδώ το μέρος του κώδικα που χρησιμοποιείς ή πρόκειται να χρησιμοποιήσεις τα παραπάνω, για να μου φύγει η περιέργεια.

Δημοσ.

Λοιπον ο ακολουθος κωδικας ειναι για να παιρνει καποιες

συντεταγμενες απο ενα κειμενο και να τις κανει highlight.

Στο παραδειγμα παρακατω δεν το κανει δυναμικα αλλα εχει

για συγκεριμενες συντεταγμενες.

public Bitmap MakeGrayscale(Bitmap original)//, int[] pos_up1, int[] pos_down1, int[] pos_left1, int[] pos_right1)

{

int pos_up3 = pos_up2[2];

int pos_down3 = pos_down2[2];

int pos_left3 = pos_left2[2];

int pos_right3 = pos_right2[2];

//make an empty bitmap the same size as original

 

Bitmap newBitmap = new Bitmap(original.Width, original.Height);

 

for (int i = 0; i < original.Width; i++)//original.Width

{

for (int j = 0; j < original.Height; j++)//original.Height

{

//get the pixel from the original image

Color originalColor = original.GetPixel(i, j);//<----

originalColor = original.GetPixel(i, j);

newBitmap.SetPixel(i, j, originalColor);

}

}

 

//for (int ii = 0; ii < fores; ii++)

//{

for (int i = 671 - 5; i < 5 + 684; i++)//κανει highlight τις συγκεκριμενες συντεταγμενες σε ενα .jpg

{

for (int j = 36 - 5; j < 51 + 5; j++)

{

Color originalColor1 = original.GetPixel(i, j);

int grayScale = (int)((originalColor1.R * .34) + (originalColor1.G * .159) + (originalColor1.B * .211));

Color newColor = Color.FromArgb(grayScale, grayScale, grayScale);

 

 

newBitmap.SetPixel(i, j, newColor);

}

}

//}

 

return newBitmap;

}

 

 

 

Εχοντας τον κωδικα αυτο σαν οδηγο θελησα να παιρνω τις

συντεταγμενες του mouse on move με click left και να κανω

highlight μια περιοχη. Δεν δουλευει βεβαια αλλα δεν εχω χρονο

για να το ψαξω.

 

 

 

public Bitmap MakeGrayscale2(Bitmap original)

{

newBitmap = new Bitmap(original.Width, original.Height);

 

for (int i = 0; i < original.Width; i++)//original.Width

{

for (int j = 0; j < original.Height; j++)//original.Height

{

originalColor = original.GetPixel(i, j);

newBitmap.SetPixel(i, j, originalColor);

}

}

 

for (int i = old_x; i < new_x; i++)//old_x, new_y einai oi syntetagmenes me to pou ginetai to click

//new_x, new_y einai oi syntetagmenes me to pou teleionei to click

{

 

for (int j = old_y; j < new_y; j++)//original.Height

{

 

int grayScale = (int)((originalColor1.R * .34) + (originalColor1.G * .159) + (originalColor1.B * .111));

newColor = Color.FromArgb(grayScale, grayScale, grayScale);

originalColor1 = original.GetPixel(i, j);

 

newBitmap.SetPixel(i, j, newColor);

 

}

}

MessageBox.Show(Convert.ToString(old_x) + ", " + Convert.ToString(old_y) + "\n" + Convert.ToString(new_x) + ", " + Convert.ToString(new_y));

 

 

return newBitmap;

}

Thanks again. θα κοιταξω το κωδικα που μου

εστειλες και θα σου πω πως δουλευει!

Δημοσ.

Οι GetPixel και SetPixel είναι πολύ αργές και δεν κάνουν γιαυτή τη δουλειά.

Ένας άλλος τρόπος για να το κάνεις πιο οικονομικά (να μην χρησιμοποιήσεις δύο bitmap) αλλά λιγότερο "θεαματικά" είναι με αντιστροφή των bits στα pixels (η επιλογή θα είναι το αρνητικό της εικόνας). Η συνάρτηση χειρισμού του MouseMove είναι:

>
void picMouseMove(object sender, MouseEventArgs e)
{
   // αν δεν έχουμε bitmap ή δεν κρατάμε το αριστερό
   // κουμπί πατημένο δεν κάνουμε τίποτα
   if (_bitmap == null || e.Button != MouseButtons.Left) return;
   // υπολογίζουμε την νέα επιλογή
   Rectangle rct = new Rectangle((_prevClickPos.X < e.Location.X ? _prevClickPos.X : e.Location.X),
           (_prevClickPos.Y < e.Location.Y ? _prevClickPos.Y : e.Location.Y),
           Math.Abs(_prevClickPos.X - e.Location.X), Math.Abs(_prevClickPos.Y - e.Location.Y));

   // έλεγχος αν το rct είναι μέσα στα όρια του
   // bitmap (εδώ χρειάζεται οπωσδήποτε αφού θα γράψουμε
   // κατευθείαν στη μνήμη του bitmap και όχι με το gc που μας 
   // προστατεύει)
   norm_rect(ref rct, _bitmap.Size);
   // γράφουμε στο Bitmap
   rev_pixels(_bitmap, rct, _prevSelection);
   // render
   Graphics gc = Graphics.FromHwnd(_picbox.Handle);
   gc.SetClip(_prevSelection);
   gc.SetClip(rct, System.Drawing.Drawing2D.CombineMode.Union);
   gc.DrawImageUnscaled(_bitmap, 0, 0);
   gc.ResetClip();
   gc.Dispose();
   // κρατάμε την επιλογή
   _prevSelection = rct;
}

όπου η μέθοδος που γράφουμε στο bitmap είναι

>
unsafe void rev_pixels(Bitmap, bitmap, Rectangle curRect, Rectangle prevRect)
{
  // και πάλι το pixel format πρέπει να μην είναι indexed αλλιώς δεν θα
  // δουλέψει
   BitmapData bd = bitmap.LockBits(new Rectangle(Point.Empty, bitmap.Size),
       ImageLockMode.ReadWrite, bitmap.PixelFormat);
   // o unmanaged δείκτης στην data του Bitmap
   byte* ptr = (byte*)bd.Scan0;
   // υπολογίζουμε τα bytes ανα pixel
   int bpp = bd.Stride / bd.Width;
   // αντιστρέφουμε τα pixel και για τα δύο παρλ/μα
   rev_rct(ptr, prevRect, bpp, bd.Stride);
   rev_rct(ptr, curRect, bpp, bd.Stride);            

   bitmap.UnlockBits(bd);
}

Για να χρησιμοποιήσουμε δείκτες στη c# πρέπει να μαρκάρουμε το scope με το keyword unsafe. Ακόμα πρέπει και στο compiler να μπει ο διακόπτης unsafe (για το visual studio: project->properties->build και τσεκάρουμε το allow unsafe code). Το stride κρατάει πόσα bytes υπάρχουν σε μια γραμμή του bitmap, γιαυτό και το διαιρώ με το πλάτος για να βρώ τα Bytes ανα pixel.

>
unsafe void rev_rct(byte* ptr, Rectangle rct, int bpp, int stride)
{
  // για κάθε γραμμή του παραλ/μου
   for (int h = rct.Top; h < rct.Bottom; h++)
    // για κάθε pixel στη γραμμή
       for (int w = rct.Left * bpp; w < rct.Right * bpp; w += bpp)
         // για κάθε byte του pixel
           for (int k = 0; k < bpp; k++)
             // αντιστρέφω τα bits
               ptr[h * stride + w + k] ^= 0xff;
}

Δημοσ.

Ο πρώτος τρόπος που μου εδειξες

δουλευει? Δεν εχω προλαβει να το δω

γιατι πνίγομαι με κατι αλλο αυτή την

περιοδο...

Δημοσ.

Δουλεύει...

Για να μην τραβήξουμε άλλο το thread (δεν νομίζω να ενδιαφέρει κανέναν άλλον), δοκίμασε όποιον από τους δύο θέλεις και όπου έχεις πρόβλημα pm τον κώδικά σου.

Καλή επιτυχία...

Δημοσ.

Δεν λειτουργησε...

πανω στον κωδικα φαινονται

2 απορίες που εχω.

Επισης τα Bitmap _bitmap, _buffer

τα αρχικοποιώ?

ακολουθει αυτο που εγραψα και δεν δουλευει

 

private void pictureBox1_MouseMove(object sender, MouseEventArgs e)

{

if (e.Button.ToString() == "Left")

{

//label1.Text = Convert.ToString(e.Location);

//MakeGrayscale1(theImage, e.X, e.Y);

 

Rectangle rct = new Rectangle((_lastClickPosX < e.Location.X ? _lastClickPosX : e.Location.X),

(_lastClickPosY < e.Location.Y ? _lastClickPosY : e.Location.Y),

Math.Abs(_lastClickPosX - e.Location.X), Math.Abs(_lastClickPosY - e.Location.Y));

 

Rectangle _prevRect;//<------------------------edo me ti einai iso?

 

Graphics gc = Graphics.FromImage(_buffer);

gc.SetClip(_prevRect);

gc.DrawImageUnscaled(_bitmap, 0, 0);

gc.ResetClip();

 

gc.CompositingMode = System.Drawing.Drawing2D.CompositingMode.SourceOver;

gc.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighSpeed;

Brush brush = new SolidBrush(Color.FromArgb(100, 255, 255, 0));

gc.FillRectangle(brush, rct);

 

//gc = Graphics.FromHwnd(pbPic.Handle);

gc = Graphics.FromHwnd(Handle);//<---------edo ti bazo?

 

gc.SetClip(_prevRect);

gc.SetClip(rct, System.Drawing.Drawing2D.CombineMode.Union);

 

gc.DrawImageUnscaled(_buffer, 0, 0);

gc.Dispose();

_prevRect = rct;

_prevRect.Inflate(8, 8);

}

}

το error λεει:

value cannot be null.

Parameter name: image.

Δημοσ.

Φιλε κανε μου μια τελεθταια χαρη...

Επιδη δεν δουλευει και Δευτερα εχω deadline

σου δινω ενα απλο κωδικα που απλα εμφανιζει

μια εικονα και σε παρακαλω συμπλήρωσε τον

με το κωδικα που κανει τη δουλεια που θελω.

Ευχαριστώ προκαταβολικα.

 

 

using System;

using System.Collections.Generic;

using System.ComponentModel;

using System.Data;

using System.Drawing;

using System.Text;

using System.Windows.Forms;

 

 

namespace test_image2

{

public partial class Form2 : Form

{

public Form2()

{

InitializeComponent();

}

 

Bitmap theImage = new Bitmap("C:\\Documents and Settings\\Nikos\\Desktop\\small.jpg");

 

private void Load_Form2(object sender, EventArgs e)

{

pictureBox1.Image = bitmap;

}

}

}

Δημοσ.

Σου έχω στείλει εδώ και μέρες pm (αριστερά στις επιλογές χρήστη/νέα μηνύματα) με ένα παράδειγμα.

Σε παρακαλώ, μη μου λες για deadlines και εταιρείες και να συμπληρώσω έναν κώδικα που ουσιαστικά δεν περιέχει τίποτα. Καλύτερα να μου πεις ότι "ξύπνησα σήμερα το πρωί και ήθελα να μάθω πως γίνεται αυτό..."

Αρχειοθετημένο

Αυτό το θέμα έχει αρχειοθετηθεί και είναι κλειστό για περαιτέρω απαντήσεις.

  • Δημιουργία νέου...