C# Problem Z Zapisaniem Edytowanego W PictureBoxie Obrazka

0

Witam.
Jestem nowy w programowaniu, i na tym forum również, mam za sobą 20+ tutoriali, odnośnie tego jak zrobić painta i jak zapisywać pliki graficzne z pictureboxa ale żaden z powyższych nie pomógł mi rozwiązać problemu.

Najpierw co muszę osiągnąć:

  • wczytuję do pictureBoxa grafikę ( np za pomocą opendialog ).
  • trzymając przycisk myszki mam narysować znak wodny ( ma być niewidoczny, ale możliwy do zdekodowania ) na tym obrazku.
  • po zakończeniu edytowania, mam zapisać plik z niewidocznym znakiem wodnym.
  • potem za pomocą innego programu ( lub tego samego ) porównać oryginał ze znakiem wodnym i w efekcie uzyskać różnicę z porównania czyli znak wodny.

Mam prawie wszystko... ale tylko prawie bo brak mi zapisu pliku z pictureboxa, a może raczej to co się zapisuje nie zawiera zmian wprowadzonych podczas edytowania... Plik owszem się zapisuje, ale bez znaku wodnego.

Mam nadzieję że wystarczająco nakreśliłem mój problem.
Teraz trochę kodów, zaznaczam że nie jest to pełny kod, gdyż programik zawiera trochę więcej funkcji a nie ma sensu zaśmiecać postu niepotrzebnymi procedurami. Chyba że okaże się że jest sens, wtedy owszem wkleję:

private void pictureBox1_MouseMove(object sender, MouseEventArgs e)
{
  if (paint)
  {
    int green = 0;
    Bitmap img = newBitmap(pictureBox1.Image);
    Color originalColor = img.GetPixel(e.X, e.Y);
       
       if ((originalColor.G % 2) == 0)
          { green = originalColor.G + 1; } 
       else
          { green = originalColor.G - 1; }
color =newSolidBrush(Color.FromArgb(originalColor.R, green, originalColor.B));
img.SetPixel(e.X, e.Y,Color.FromArgb(originalColor.R, green, originalColor.B));
Graphics g = pictureBox1.CreateGraphics();
g.FillEllipse(color, e.X, e.Y, 3, 3);
g.Dispose();
}
} 

Program zmienia wyglad obrazka w PictureBoxie... z powodu opóźnień związanych z ruchem myszki widać przesunięcia w grafice... ale wygląda to jakby malował w powietrzu bo obrazek i tak jest nie tknięty... czy ktoś może pomóc??

Z góry dziękuję.

1

Bitmap img = newBitmap(pictureBox1.Image);
Tutaj tworzysz nowy obrazek o treści tej samej co w pictureboksie. Ale to już jest inny obrazek...

Graphics g = pictureBox1.CreateGraphics();
Tutaj tworzysz obiekt Graphics, który rysuje po pictureboksie. Ale nie w bitmapie przypisanej do kontrolki, tylko „po wierzchu”, po kontrolce...
Żeby rysować po bitmapie,

Graphics g = Graphics.FromImage(pictureBox1.Image);

czy jakoś tak.

0

Witam i dziękuję za odpowiedź.
Coś się zmieniło jednak nie załatwia to problemu.
Otóż kiedy dodaje znak wodny na sztywno ( tzn namalowałem sobie w paincie znak wodny ) i dodaje go za pomocą algorytmu:

 private void button11_Click(object sender, EventArgs e)
        {
           int green = 0;
           Bitmap img = new Bitmap(pictureBox1.Image);
           nazwa = "PrzedZnak";
           pictureBox1.Image.Save((nazwa) + ".jpg"); 
           Bitmap img2 = new Bitmap(Directory.GetCurrentDirectory() + "\\Znak.bmp");
            for (int i = 0; i < img2.Width; i++)
            {
                for (int j = 0; j < img2.Height; j++)
                {
                    Color originalColor = img.GetPixel(i + ((img.Width - img2.Width) / 2), j + ((img.Height - img2.Height) / 2));
                    Color originalColor2 = img2.GetPixel(i, j);
                    if ((originalColor2.R!=255) && (originalColor2.G!=255) && (originalColor2.B!=255))
                    {
                        if ((originalColor.G % 2) == 0)
                        {green = originalColor.G + 1; }
                        else
                        {green = originalColor.G - 1;}
                    }
                    else
                    {green = originalColor.G;}
                    Color newColor = Color.FromArgb(originalColor.R, green, originalColor.B);
                    img.SetPixel(i + ((img.Width - img2.Width) / 2), j + ((img.Height - img2.Height) / 2), newColor);
                }
            }
            pictureBox1.Image = img;
            nazwa = "PoZnak";
            pictureBox1.Image.Save((nazwa) + ".jpg");
        }

Zapisuje mi on dwa pliki JPG "PrzedZnak" i "PoZnak" na pierwszy rzut oka się niczym nie różnią, ale jak dodaje kolejną metode, by sprawdzić czy faktycznie się nie różnią:

private void button13_Click(object sender, EventArgs e)
        {
            int green,red,blue = 0;
            
           Bitmap img = new Bitmap(Directory.GetCurrentDirectory() +"\\PrzedZnak.jpg");
           Bitmap img2 = new Bitmap(Directory.GetCurrentDirectory() +"\\PoZnak.jpg");
            for (int i = 0; i < img.Width; i++)
            {
                for (int j = 0; j < img.Height; j++)
                {
                    Color originalColor = img.GetPixel(i, j);
                    Color originalColor2 = img2.GetPixel(i, j);
                    if ((originalColor2.G - originalColor.G)==0)
                    {
                        green=red=blue=0;
                    }
                    else
                    {
                        green = red = blue =255;
                    }
                    Color newColor = Color.FromArgb(red, green, blue);
                    img.SetPixel(i, j, newColor);
                }
            }
            pictureBox1.Image = img;
        } 

to pojawia mi się w efekcie coś takiego:
Image Hosted by ImageShack.us

Problem w tym że kiedy namaluje kursorem Znak wodny... otrzymuje coś takiego:
Image Hosted by ImageShack.us

Widać ze z jakiś przyczyn ... albo mój "dekoder" jest zły ( nie sądzę bo działa w przypadku na sztywno jak należy ), albo urzywając clasy Graphics.FromImage -> plik graficzny z jakiś przyczyn zmienia więcej pixeli niż tylko te gdzie rysowałem w picture boxie.... Mozna powiedzieć że przez to że dekoder tworze grafikę 2 kolorową ten obrazek jest tak nieczytelny... gdyby kolorów było więcej ... byłby bardziej zbliżony do oryginału.

obrazki zapisuję za pomocą:

 pictureBox1.Image.Save((nazwa) + ".jpg"); 

gdzie nazwa to zmienna typu string.

0

Format JPEG jest stratny, więc jest to całkiem zrozumiałe, że ponownie zapisany obrazek wykazuje różnice w wielu pikselach.

Porównaj np. z PNG.

0

Sorry że tak późno ale:

Zapisałem przetestowałem to na jpg .. oryginalnym z neta, nowym zapisanym przed i nowym po bazgraniu... efekty takie same.
Przetestowalem to na png tak jak zalecałeś... rezultat identyczny.
Przetestowałem na bmp... również bez zmian.

Czy są może jeszcze jakieś inne pomysły co do przyczyny?

0

Odkrylem jednak niechcacy ciekawostke ...
Ponieważ mam przycisk który dodaje znak wodny ( przygotowany wcześniej w paintcie ), którego kod wygląda tak:

private void button11_Click(object sender, EventArgs e)
        {
           int green = 0;
           Bitmap img = new Bitmap(pictureBox1.Image);
           nazwa = "PrzedZnak";
           pictureBox1.Image.Save((nazwa) + ".bmp"); 
           Bitmap img2 = new Bitmap(Directory.GetCurrentDirectory() + "\\Znak.bmp");
            for (int i = 0; i < img2.Width; i++)
            {
                for (int j = 0; j < img2.Height; j++)
                {
                    Color originalColor = img.GetPixel(i + ((img.Width - img2.Width) / 2), j + ((img.Height - img2.Height) / 2));
                    Color originalColor2 = img2.GetPixel(i, j);
                    if ((originalColor2.R!=255) && (originalColor2.G!=255) && (originalColor2.B!=255))
                    {
                        if ((originalColor.G % 2) == 0)
                        {green = originalColor.G + 1; }
                        else
                        {green = originalColor.G - 1;}
                    }
                    else
                    {green = originalColor.G;}
                    Color newColor = Color.FromArgb(originalColor.R, green, originalColor.B);
                    img.SetPixel(i + ((img.Width - img2.Width) / 2), j + ((img.Height - img2.Height) / 2), newColor);
                }
            }
            pictureBox1.Image = img;
            nazwa = "PoZnak";
            pictureBox1.Image.Save((nazwa) + ".bmp");
        } 

po jego naciśnięciu ... i rysowaniu na obrazku "Znaku Wodnego", zapisaniu obrazka .. a później porównaniu go z oryginałem za pomocą algorytmu:

 private void button13_Click(object sender, EventArgs e)
        {
            int green,red,blue = 0;
            
           Bitmap img = new Bitmap(Directory.GetCurrentDirectory() +"\\PrzedZnak.bmp");
           Bitmap img2 = new Bitmap(Directory.GetCurrentDirectory() +"\\PoZnak.bmp");
            for (int i = 0; i < img.Width; i++)
            {
                for (int j = 0; j < img.Height; j++)
                {
                    Color originalColor = img.GetPixel(i, j);
                    Color originalColor2 = img2.GetPixel(i, j);
                    if ((originalColor2.G - originalColor.G)==0)
                    {
                        green=red=blue=0;
                    }
                    else
                    {
                        green = red = blue =255;
                    }
                    Color newColor = Color.FromArgb(red, green, blue);
                    img.SetPixel(i, j, newColor);
                }
            }
            pictureBox1.Image = img;
        }

W efekcie dostaję coś takiego, gdzie te kropeczki to jest to co namalowałem:

Image Hosted by ImageShack.us

Natomiast jeżeli tego przycisku ( do tworzenia znaku wodnego ) nie wcisnę ... pojawia się grafika jak poprzednio... czyli SYF bieli i czerni.

0

Sprawę załatwiłem w następujący sposób. ( dodałem dwa przyciski ):
pierwszy przycisk ( "Zakończ rysowanie znaku") tworzy bitmape img, wrzuca ja w pictureboxa... a pozniej go zapisuje na dysku jako PoZnaku

        private void button14_Click(object sender, EventArgs e)
        {
            Bitmap img = new Bitmap(pictureBox1.Image);
            pictureBox1.Image = img;
            nazwa = "PoZnak";
            pictureBox1.Image.Save((nazwa) + ".bmp");
            nazwa = "lll";
        } 

Drugi ( Rozpocznij Malowanie )zapisuje obrazek z pictureboxa jako PrzedZnakiem:

 
private void button15_Click(object sender, EventArgs e)
        {
            nazwa = "PrzedZnak";
            pictureBox1.Image.Save((nazwa) + ".bmp");
            MessageBox.Show("Możesz teraz malować");
            nazwa = "lll";
        } 

Działa to w następujący sposób:

1.Klikam przycisk Rozpocznij Malowanie -> zapisuje grafikę z PictureBoxa jako PrzedZnakiem
2.Maluje na PictureBoxie swój znak wodny kursorem.
3.Klikam Zakończ rysowanie znaku w momencie kiedy juz skończyłem. I tym samym zapisuje grafikę jako PoZnak.
4.Klikam Sprawdz znak wody ( porównuje PrzedZnakiem i PoZnaku ) i wszystkie różnice w kolorach pixeli maluje na biało, resztę na czarno.

otrzymuję to co wyżej, tylko bez tej mordki "kota" tzn znak który namalowałem myszką. :)

Całą sprawę załatwiło stworzenie bitmapy i wrzucenie jej w pictureboxa... no i oczywiscie wczesniej wspomniana przez Ciebie metoda Graphics.FromImage, co do foratu zapisu, to nie sprawdzałem ale prawie na pewno będzie to działać również na JPG.

1 użytkowników online, w tym zalogowanych: 0, gości: 1