Zmiana bitmapy kolorowej w czarno-białą

Zmiana bitmapy kolorowej w czarno-białą
B3
  • Rejestracja:ponad 15 lat
  • Ostatnio:ponad 9 lat
0

Jak zrobić z normalnej bitmapy wczytanej do programu czarno-białą(nie w skali szarości - dwa kolory - czarny i biały). Nie mogę nic w googlach na ten temat znaleźć.

RE
Moderator
  • Rejestracja:około 18 lat
  • Ostatnio:12 miesięcy
1

Twój słownik na dzisiaj: monochromatic, monochrome, black and white, bitmap, C#, google.

B3
  • Rejestracja:ponad 15 lat
  • Ostatnio:ponad 9 lat
0

Udało się znaleźć, zastosowałem i... nic z tego. Obrazek co prawda stał się czarno-biały, ale nie zmienił swojego rozmiaru(ciągle waży 60KB), a wydaje mi się, że informacja o kolorze to sporo danych. Oto kod, który znalazłem i zastosowałem:

Klasa Monochromatic:

Kopiuj
/// <summary>
        /// Copies a bitmap into a 1bpp/8bpp bitmap of the same dimensions, fast
        /// </summary>
        /// <param name="b">original bitmap</param>
        /// <param name="bpp">1 or 8, target bpp</param>
        /// <returns>a 1bpp copy of the bitmap</returns>
        public static System.Drawing.Bitmap CopyToBpp(System.Drawing.Bitmap b, int bpp)
        {
            if (bpp != 1 && bpp != 8) throw new System.ArgumentException("1 or 8", "bpp");

            // Plan: built into Windows GDI is the ability to convert
            // bitmaps from one format to another. Most of the time, this
            // job is actually done by the graphics hardware accelerator card
            // and so is extremely fast. The rest of the time, the job is done by
            // very fast native code.
            // We will call into this GDI functionality from C#. Our plan:
            // (1) Convert our Bitmap into a GDI hbitmap (ie. copy unmanaged->managed)
            // (2) Create a GDI monochrome hbitmap
            // (3) Use GDI "BitBlt" function to copy from hbitmap into monochrome (as above)
            // (4) Convert the monochrone hbitmap into a Bitmap (ie. copy unmanaged->managed)

            int w = b.Width, h = b.Height;
            IntPtr hbm = b.GetHbitmap(); // this is step (1)
            //
            // Step (2): create the monochrome bitmap.
            // "BITMAPINFO" is an interop-struct which we define below.
            // In GDI terms, it's a BITMAPHEADERINFO followed by an array of two RGBQUADs
            BITMAPINFO bmi = new BITMAPINFO();
            bmi.biSize = 40;  // the size of the BITMAPHEADERINFO struct
            bmi.biWidth = w;
            bmi.biHeight = h;
            bmi.biPlanes = 1; // "planes" are confusing. We always use just 1. Read MSDN for more info.
            bmi.biBitCount = (short)bpp; // ie. 1bpp or 8bpp
            bmi.biCompression = BI_RGB; // ie. the pixels in our RGBQUAD table are stored as RGBs, not palette indexes
            bmi.biSizeImage = (uint)(((w + 7) & 0xFFFFFFF8) * h / 8);
            bmi.biXPelsPerMeter = 1000000; // not really important
            bmi.biYPelsPerMeter = 1000000; // not really important
            // Now for the colour table.
            uint ncols = (uint)1 << bpp; // 2 colours for 1bpp; 256 colours for 8bpp
            bmi.biClrUsed = ncols;
            bmi.biClrImportant = ncols;
            bmi.cols = new uint[256]; // The structure always has fixed size 256, even if we end up using fewer colours
            if (bpp == 1) { bmi.cols[0] = MAKERGB(0, 0, 0); bmi.cols[1] = MAKERGB(255, 255, 255); }
            else { for (int i = 0; i < ncols; i++) bmi.cols[i] = MAKERGB(i, i, i); }
            // For 8bpp we've created an palette with just greyscale colours.
            // You can set up any palette you want here. Here are some possibilities:
            // greyscale: for (int i=0; i<256; i++) bmi.cols[i]=MAKERGB(i,i,i);
            // rainbow: bmi.biClrUsed=216; bmi.biClrImportant=216; int[] colv=new int[6]{0,51,102,153,204,255};
            //          for (int i=0; i<216; i++) bmi.cols[i]=MAKERGB(colv[i/36],colv[(i/6)%6],colv[i%6]);
            // optimal: a difficult topic: http://en.wikipedia.org/wiki/Color_quantization
            // 
            // Now create the indexed bitmap "hbm0"
            IntPtr bits0; // not used for our purposes. It returns a pointer to the raw bits that make up the bitmap.
            IntPtr hbm0 = CreateDIBSection(IntPtr.Zero, ref bmi, DIB_RGB_COLORS, out bits0, IntPtr.Zero, 0);
            //
            // Step (3): use GDI's BitBlt function to copy from original hbitmap into monocrhome bitmap
            // GDI programming is kind of confusing... nb. The GDI equivalent of "Graphics" is called a "DC".
            IntPtr sdc = GetDC(IntPtr.Zero);       // First we obtain the DC for the screen
            // Next, create a DC for the original hbitmap
            IntPtr hdc = CreateCompatibleDC(sdc); SelectObject(hdc, hbm);
            // and create a DC for the monochrome hbitmap
            IntPtr hdc0 = CreateCompatibleDC(sdc); SelectObject(hdc0, hbm0);
            // Now we can do the BitBlt:
            BitBlt(hdc0, 0, 0, w, h, hdc, 0, 0, SRCCOPY);
            // Step (4): convert this monochrome hbitmap back into a Bitmap:
            System.Drawing.Bitmap b0 = System.Drawing.Bitmap.FromHbitmap(hbm0);
            //
            // Finally some cleanup.
            DeleteDC(hdc);
            DeleteDC(hdc0);
            ReleaseDC(IntPtr.Zero, sdc);
            DeleteObject(hbm);
            DeleteObject(hbm0);
            //
            return b0;
        }

        [System.Runtime.InteropServices.DllImport("gdi32.dll")]
        public static extern bool DeleteObject(IntPtr hObject);

        [System.Runtime.InteropServices.DllImport("user32.dll")]
        public static extern int InvalidateRect(IntPtr hwnd, IntPtr rect, int bErase);

        [System.Runtime.InteropServices.DllImport("user32.dll")]
        public static extern IntPtr GetDC(IntPtr hwnd);

        [System.Runtime.InteropServices.DllImport("gdi32.dll")]
        public static extern IntPtr CreateCompatibleDC(IntPtr hdc);

        [System.Runtime.InteropServices.DllImport("user32.dll")]
        public static extern int ReleaseDC(IntPtr hwnd, IntPtr hdc);

        [System.Runtime.InteropServices.DllImport("gdi32.dll")]
        public static extern int DeleteDC(IntPtr hdc);

        [System.Runtime.InteropServices.DllImport("gdi32.dll")]
        public static extern IntPtr SelectObject(IntPtr hdc, IntPtr hgdiobj);

        [System.Runtime.InteropServices.DllImport("gdi32.dll")]
        public static extern int BitBlt(IntPtr hdcDst, int xDst, int yDst, int w, int h, IntPtr hdcSrc, int xSrc, int ySrc, int rop);
        static int SRCCOPY = 0x00CC0020;

        [System.Runtime.InteropServices.DllImport("gdi32.dll")]
        static extern IntPtr CreateDIBSection(IntPtr hdc, ref BITMAPINFO bmi, uint Usage, out IntPtr bits, IntPtr hSection, uint dwOffset);
        static uint BI_RGB = 0;
        static uint DIB_RGB_COLORS = 0;
        [System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)]
        public struct BITMAPINFO
        {
            public uint biSize;
            public int biWidth, biHeight;
            public short biPlanes, biBitCount;
            public uint biCompression, biSizeImage;
            public int biXPelsPerMeter, biYPelsPerMeter;
            public uint biClrUsed, biClrImportant;
            [System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.ByValArray, SizeConst = 256)]
            public uint[] cols;
        }

        static uint MAKERGB(int r, int g, int b)
        {
            return ((uint)(b & 255)) | ((uint)((r & 255) << 8)) | ((uint)((g & 255) << 16));
        }
    } 

a to już dalsze użycie:

Kopiuj
 
Bitmap saveMonochromatic = Monochromatic.CopyToBpp(screen, 1);
            

Tak jak mówiłem, kolory z obrazka znikają ale nie ma najmniejszej zmiany rozmiaru...

Pojawił się też drugi problem, chcę zmniejszyć rozdzielczość tego obrazka, znalazłem już odpowiednią metodę, ale nie działa(rozdzielczość się nie zmienia):

Kopiuj
saveMonochromatic.SetResolution(640, 480); 

Co z tym zrobić?

Szarp
  • Rejestracja:ponad 13 lat
  • Ostatnio:ponad 12 lat
  • Postów:48
1

Pewnie mapę bitową zapisujesz jako 24-bitową. Musisz zapisać bmp z encoderem i podać color depth równe 1.

Do skalowania obrazka możesz użyć klasy Graphics.


Moonless Sky
Becoming the Getsuga itself.
B3
  • Rejestracja:ponad 15 lat
  • Ostatnio:ponad 9 lat
0

Spróbowałem, problem w tym, że mam już jeden parametr, Quality. Wygląda to tak:

Kopiuj
 myEncoder = Encoder.Quality;
            myEncoderParameter = new EncoderParameter(myEncoder, 10L);
            myEncoderParameters.Param[0] = myEncoderParameter;
            jgpEncoder = GetEncoder(ImageFormat.Jpeg);
(...)
private ImageCodecInfo GetEncoder(ImageFormat format)
        {

            ImageCodecInfo[] codecs = ImageCodecInfo.GetImageDecoders();

            foreach (ImageCodecInfo codec in codecs)
            {
                if (codec.FormatID == format.Guid)
                {
                    return codec;
                }
            }
            return null;
        }
 

Dopisałem parę linijek i wyszło coś takiego:

Kopiuj
  myEncoder = Encoder.Quality;
            myEncoderParameter = new EncoderParameter(myEncoder, 10L);
            encColorDepth = Encoder.ColorDepth;
            myEncoderParameter2 = new EncoderParameter(encColorDepth, 1L);
            myEncoderParameters.Param[0] = myEncoderParameter;
            myEncoderParameters.Param[1] = myEncoderParameter2;
            jgpEncoder = GetEncoder(ImageFormat.Jpeg);

a potem użycie:

Kopiuj
 saveMonochromatic.Save(path, jgpEncoder, myEncoderParameters);

A głębia ciągle 24bit...

Szarp
  • Rejestracja:ponad 13 lat
  • Ostatnio:ponad 12 lat
  • Postów:48
0
Kopiuj
public static Bitmap ConvertTo1bpp(Image srcImage)
{
    Bitmap tmpBmp = new Bitmap(srcImage);

    return tmpBmp.Clone(new RectangleF(0, 0, srcImage.Width, srcImage.Height), PixelFormat.Format1bppIndexed);
}

Niby w szczegółach pokazuje głębie w bitach: 24, ale rozmiar pliku jest dobry.

Kopiuj
var encParams = new EncoderParameters(2);

encParams.Param[0] = new EncoderParameter(System.Drawing.Imaging.Encoder.Quality, 100L);
encParams.Param[1] = new EncoderParameter(System.Drawing.Imaging.Encoder.ColorDepth, 1L);

Moonless Sky
Becoming the Getsuga itself.
edytowany 3x, ostatnio: Szarp
B3
  • Rejestracja:ponad 15 lat
  • Ostatnio:ponad 9 lat
0
Kopiuj
 screen = screen.Clone(new RectangleF(0, 0, screen.Width, screen.Height), PixelFormat.Format1bppIndexed);

OutOfMemoryException.

Szarp
Ciekawe. Przed chwilą zrobiłem zrzut ekranu (1366x768) i proces łyknął przez chwilę 10 000 K RAMu przy konwersji. Żadne Out of memory nie wyskoczyło...
B3
Nie wiem, na pewno nie zabrakło pamięci. W dodatku obrazek ma 640x480.
B3
  • Rejestracja:ponad 15 lat
  • Ostatnio:ponad 9 lat
0

Ok, nie wiem o co chodziło, ale znalazłem taką klasę na necie, która daje specjalną metodę do takiej konwersji i działa, szczegóły ciągle 24bit, ale rozmiar adekwatny do jakości. Dzięki wszystkim.

Wrrróć za wcześnie się ucieszyłem nie działa. Już tracę na to nerwy 3 godziny dziś nad tym siedzę i nic.

Kopiuj
screen = ImageUtilities.ResizeImage(screen, 1024, 768);
            screen = Monochromatic.CopyToBpp(screen, 1);
            screen.Save(path, jpgEncoder, encParams); 

Problem musi być w zapisie, te cholerne 24bit ciągle robią problem. Tu encParams i jpgEncoder:

Kopiuj
        Encoder encQuality, encColorDepth;
        EncoderParameter encQualityParam, encColorDepthParam;
        EncoderParameters encParams = new EncoderParameters(2);
        ImageCodecInfo jpgEncoder;

(...)

encQuality = Encoder.Quality;
            encQualityParam = new EncoderParameter(encQuality, 10L);
            encParams.Param[0] = encQualityParam;
            encColorDepth = Encoder.ColorDepth;
            encColorDepthParam = new EncoderParameter(encColorDepth, 1L);
            encParams.Param[1] = encColorDepthParam;
            jpgEncoder = GetEncoderInfo("image/jpeg");

(...)

private static ImageCodecInfo GetEncoderInfo(String mimeType)
        {
            int j;
            ImageCodecInfo[] encoders;
            encoders = ImageCodecInfo.GetImageEncoders();
            for (j = 0; j < encoders.Length; ++j)
            {
                if (encoders[j].MimeType == mimeType)
                    return encoders[j];
            }
            return null;
        }
   
edytowany 1x, ostatnio: blazej30
Szarp
  • Rejestracja:ponad 13 lat
  • Ostatnio:ponad 12 lat
  • Postów:48
0
Kopiuj
    public static class BitmapExtensions
    {
        public static void SaveAs(this Bitmap bmp, string FileName, ImageFormat Format, long ColorDepth)
        {
            ImageCodecInfo imgCodecInfo;
            System.Drawing.Imaging.Encoder myEncoder;
            EncoderParameter epColorDepth;
            EncoderParameters eParams;

            imgCodecInfo = GetEncoder(Format);
            myEncoder = System.Drawing.Imaging.Encoder.ColorDepth;
            eParams = new EncoderParameters(1);

            epColorDepth = new EncoderParameter(myEncoder, ColorDepth);
            eParams.Param[0] = epColorDepth;
            bmp.Save(FileName, imgCodecInfo, eParams);
        }

        public static Bitmap ConvertTo1bpp(Image srcImage)
        {
            Bitmap srcBmp = new Bitmap(srcImage);

            return srcBmp.Clone(new RectangleF(0, 0, srcImage.Width, srcImage.Height), PixelFormat.Format1bppIndexed);
        }

        public static ImageCodecInfo GetEncoder(ImageFormat Format)
        {
            ImageCodecInfo[] codecs = ImageCodecInfo.GetImageDecoders();

            foreach (ImageCodecInfo codec in codecs)
            {
                if (codec.FormatID == Format.Guid)
                {
                    return codec;
                }
            }

            return null;
        }
    }

Użycie:

Kopiuj
BitmapExtensions.SaveAs(BitmapExtensions.ConvertTo1bpp(Image.FromFile(@"Temp\Original.png")),
                @"Temp\test.jpg", ImageFormat.Jpeg, 1L);

Metoda SaveAs zawiera w sobie kod pochodzący z: MSDN. Działa doskonale u mnie.

PS Tylko jeden EncoderParameter jest brany pod uwagę, ten w Param[0].


Moonless Sky
Becoming the Getsuga itself.
edytowany 2x, ostatnio: Szarp
B3
  • Rejestracja:ponad 15 lat
  • Ostatnio:ponad 9 lat
0

Za chwilę postaram się przetestować, problem musi istnieć właśnie w tym, że pod uwagę brany jest Param[0]. Więc dlaczego jest to tablica, skoro "działa" tylko pierwsza pozycja? Wydaje mi się, że będę musiał po prostu dwa razy zapisywać plik - zapisać z obniżoną jakością -> zdjąć kolory -> zapisać z obniżoną głębią.

Szarp
W ten sposób, który Ci tu przedstawiłem, otrzymujesz taki sam rozmiar dla bmp, jakbyś zapisywał ten obraz przy pomocy Painta jako monochromatyczny bmp. Może jest sposób, by wszystkie argumenty z Param były brane pod uwagę, ale ja go nie znam. Raczej możesz pominąć obniżanie jakości.
B3
  • Rejestracja:ponad 15 lat
  • Ostatnio:ponad 9 lat
0

Twój sposób nie działa. OutOfMemoryException i basta a pamięci wolnej opór. Ale tak jak mówiłem mam już do tego klasę i starczy. Chcę tylko to normalnie zapisać, potrzebuję obniżenia jakości i głębi kolorów, dwóch parametrów, skoro EncoderParameters to tablica to musi być na to sposób.

Kliknij, aby dodać treść...

Pomoc 1.18.8

Typografia

Edytor obsługuje składnie Markdown, w której pojedynczy akcent *kursywa* oraz _kursywa_ to pochylenie. Z kolei podwójny akcent **pogrubienie** oraz __pogrubienie__ to pogrubienie. Dodanie znaczników ~~strike~~ to przekreślenie.

Możesz dodać formatowanie komendami , , oraz .

Ponieważ dekoracja podkreślenia jest przeznaczona na linki, markdown nie zawiera specjalnej składni dla podkreślenia. Dlatego by dodać podkreślenie, użyj <u>underline</u>.

Komendy formatujące reagują na skróty klawiszowe: Ctrl+B, Ctrl+I, Ctrl+U oraz Ctrl+S.

Linki

By dodać link w edytorze użyj komendy lub użyj składni [title](link). URL umieszczony w linku lub nawet URL umieszczony bezpośrednio w tekście będzie aktywny i klikalny.

Jeżeli chcesz, możesz samodzielnie dodać link: <a href="link">title</a>.

Wewnętrzne odnośniki

Możesz umieścić odnośnik do wewnętrznej podstrony, używając następującej składni: [[Delphi/Kompendium]] lub [[Delphi/Kompendium|kliknij, aby przejść do kompendium]]. Odnośniki mogą prowadzić do Forum 4programmers.net lub np. do Kompendium.

Wspomnienia użytkowników

By wspomnieć użytkownika forum, wpisz w formularzu znak @. Zobaczysz okienko samouzupełniające nazwy użytkowników. Samouzupełnienie dobierze odpowiedni format wspomnienia, zależnie od tego czy w nazwie użytkownika znajduje się spacja.

Znaczniki HTML

Dozwolone jest używanie niektórych znaczników HTML: <a>, <b>, <i>, <kbd>, <del>, <strong>, <dfn>, <pre>, <blockquote>, <hr/>, <sub>, <sup> oraz <img/>.

Skróty klawiszowe

Dodaj kombinację klawiszy komendą notacji klawiszy lub skrótem klawiszowym Alt+K.

Reprezentuj kombinacje klawiszowe używając taga <kbd>. Oddziel od siebie klawisze znakiem plus, np <kbd>Alt+Tab</kbd>.

Indeks górny oraz dolny

Przykład: wpisując H<sub>2</sub>O i m<sup>2</sup> otrzymasz: H2O i m2.

Składnia Tex

By precyzyjnie wyrazić działanie matematyczne, użyj składni Tex.

<tex>arcctg(x) = argtan(\frac{1}{x}) = arcsin(\frac{1}{\sqrt{1+x^2}})</tex>

Kod źródłowy

Krótkie fragmenty kodu

Wszelkie jednolinijkowe instrukcje języka programowania powinny być zawarte pomiędzy obróconymi apostrofami: `kod instrukcji` lub ``console.log(`string`);``.

Kod wielolinijkowy

Dodaj fragment kodu komendą . Fragmenty kodu zajmujące całą lub więcej linijek powinny być umieszczone w wielolinijkowym fragmencie kodu. Znaczniki ``` lub ~~~ umożliwiają kolorowanie różnych języków programowania. Możemy nadać nazwę języka programowania używając auto-uzupełnienia, kod został pokolorowany używając konkretnych ustawień kolorowania składni:

```javascript
document.write('Hello World');
```

Możesz zaznaczyć również już wklejony kod w edytorze, i użyć komendy  by zamienić go w kod. Użyj kombinacji Ctrl+`, by dodać fragment kodu bez oznaczników języka.

Tabelki

Dodaj przykładową tabelkę używając komendy . Przykładowa tabelka składa się z dwóch kolumn, nagłówka i jednego wiersza.

Wygeneruj tabelkę na podstawie szablonu. Oddziel komórki separatorem ; lub |, a następnie zaznacz szablonu.

nazwisko;dziedzina;odkrycie
Pitagoras;mathematics;Pythagorean Theorem
Albert Einstein;physics;General Relativity
Marie Curie, Pierre Curie;chemistry;Radium, Polonium

Użyj komendy by zamienić zaznaczony szablon na tabelkę Markdown.

Lista uporządkowana i nieuporządkowana

Możliwe jest tworzenie listy numerowanych oraz wypunktowanych. Wystarczy, że pierwszym znakiem linii będzie * lub - dla listy nieuporządkowanej oraz 1. dla listy uporządkowanej.

Użyj komendy by dodać listę uporządkowaną.

1. Lista numerowana
2. Lista numerowana

Użyj komendy by dodać listę nieuporządkowaną.

* Lista wypunktowana
* Lista wypunktowana
** Lista wypunktowana (drugi poziom)

Składnia Markdown

Edytor obsługuje składnię Markdown, która składa się ze znaków specjalnych. Dostępne komendy, jak formatowanie , dodanie tabelki lub fragmentu kodu są w pewnym sensie świadome otaczającej jej składni, i postarają się unikać uszkodzenia jej.

Dla przykładu, używając tylko dostępnych komend, nie możemy dodać formatowania pogrubienia do kodu wielolinijkowego, albo dodać listy do tabelki - mogłoby to doprowadzić do uszkodzenia składni.

W pewnych odosobnionych przypadkach brak nowej linii przed elementami markdown również mógłby uszkodzić składnie, dlatego edytor dodaje brakujące nowe linie. Dla przykładu, dodanie formatowania pochylenia zaraz po tabelce, mogłoby zostać błędne zinterpretowane, więc edytor doda oddzielającą nową linię pomiędzy tabelką, a pochyleniem.

Skróty klawiszowe

Skróty formatujące, kiedy w edytorze znajduje się pojedynczy kursor, wstawiają sformatowany tekst przykładowy. Jeśli w edytorze znajduje się zaznaczenie (słowo, linijka, paragraf), wtedy zaznaczenie zostaje sformatowane.

  • Ctrl+B - dodaj pogrubienie lub pogrub zaznaczenie
  • Ctrl+I - dodaj pochylenie lub pochyl zaznaczenie
  • Ctrl+U - dodaj podkreślenie lub podkreśl zaznaczenie
  • Ctrl+S - dodaj przekreślenie lub przekreśl zaznaczenie

Notacja Klawiszy

  • Alt+K - dodaj notację klawiszy

Fragment kodu bez oznacznika

  • Alt+C - dodaj pusty fragment kodu

Skróty operujące na kodzie i linijkach:

  • Alt+L - zaznaczenie całej linii
  • Alt+, Alt+ - przeniesienie linijki w której znajduje się kursor w górę/dół.
  • Tab/⌘+] - dodaj wcięcie (wcięcie w prawo)
  • Shit+Tab/⌘+[ - usunięcie wcięcia (wycięcie w lewo)

Dodawanie postów:

  • Ctrl+Enter - dodaj post
  • ⌘+Enter - dodaj post (MacOS)