public static void SaveJpeg(string aFilename, int aQuality, Bitmap aImage) { if (aImage.PixelFormat != System.Drawing.Imaging.PixelFormat.Format24bppRgb) { throw new ArgumentException("Only three channel color images are supported."); } if (aImage.Width % 16 != 0 || aImage.Height % 16 != 0) { throw new ArgumentException("The provided bitmap must have a height and width of a multiple of 16."); } JPEGCompression compression = new JPEGCompression(); NPPImage_8uC3 src = new NPPImage_8uC3(aImage.Width, aImage.Height); NPPImage_8uC1 srcY = new NPPImage_8uC1(aImage.Width, aImage.Height); NPPImage_8uC1 srcCb = new NPPImage_8uC1(aImage.Width / 2, aImage.Height / 2); NPPImage_8uC1 srcCr = new NPPImage_8uC1(aImage.Width / 2, aImage.Height / 2); src.CopyToDevice(aImage); //System.Drawing.Bitmap is ordered BGR not RGB //The NPP routine BGR to YCbCR outputs the values in clamped range, following the YCbCr standard. //But JPEG uses unclamped values ranging all from [0..255], thus use our own color matrix: float[,] BgrToYCbCr = new float[3, 4] {{0.114f, 0.587f, 0.299f, 0}, {0.5f, -0.33126f, -0.16874f, 128}, {-0.08131f, -0.41869f, 0.5f, 128}}; src.ColorTwist(BgrToYCbCr); //Reduce size of of Cb and Cr channel src.Copy(srcY, 2); srcY.Resize(srcCr, 0.5, 0.5, InterpolationMode.SuperSampling); src.Copy(srcY, 1); srcY.Resize(srcCb, 0.5, 0.5, InterpolationMode.SuperSampling); src.Copy(srcY, 0); FrameHeader oFrameHeader = new FrameHeader(); oFrameHeader.nComponents = 3; oFrameHeader.nHeight = (ushort)aImage.Height; oFrameHeader.nSamplePrecision = 8; oFrameHeader.nWidth = (ushort)aImage.Width; oFrameHeader.aComponentIdentifier = new byte[] { 1, 2, 3 }; oFrameHeader.aSamplingFactors = new byte[] { 34, 17, 17 }; //Y channel is twice the sice of Cb/Cr channel oFrameHeader.aQuantizationTableSelector = new byte[] { 0, 1, 1 }; //Get quantization tables from JPEG standard with quality scaling QuantizationTable[] aQuantizationTables = new QuantizationTable[2]; aQuantizationTables[0] = new QuantizationTable(QuantizationTable.QuantizationType.Luminance, aQuality); aQuantizationTables[1] = new QuantizationTable(QuantizationTable.QuantizationType.Chroma, aQuality); CudaDeviceVariable<byte>[] pdQuantizationTables = new CudaDeviceVariable<byte>[2]; pdQuantizationTables[0] = aQuantizationTables[0].aTable; pdQuantizationTables[1] = aQuantizationTables[1].aTable; //Get Huffman tables from JPEG standard HuffmanTable[] aHuffmanTables = new HuffmanTable[4]; aHuffmanTables[0] = new HuffmanTable(HuffmanTable.HuffmanType.LuminanceDC); aHuffmanTables[1] = new HuffmanTable(HuffmanTable.HuffmanType.ChromaDC); aHuffmanTables[2] = new HuffmanTable(HuffmanTable.HuffmanType.LuminanceAC); aHuffmanTables[3] = new HuffmanTable(HuffmanTable.HuffmanType.ChromaAC); //Set header ScanHeader oScanHeader = new ScanHeader(); oScanHeader.nA = 0; oScanHeader.nComponents = 3; oScanHeader.nSe = 63; oScanHeader.nSs = 0; oScanHeader.aComponentSelector = new byte[] { 1, 2, 3 }; oScanHeader.aHuffmanTablesSelector = new byte[] { 0, 17, 17 }; NPPImage_16sC1[] apdDCT = new NPPImage_16sC1[3]; NPPImage_8uC1[] apDstImage = new NPPImage_8uC1[3]; NppiSize[] aDstSize = new NppiSize[3]; aDstSize[0] = new NppiSize(srcY.Width, srcY.Height); aDstSize[1] = new NppiSize(srcCb.Width, srcCb.Height); aDstSize[2] = new NppiSize(srcCr.Width, srcCr.Height); // Compute channel sizes as stored in the output JPEG (8x8 blocks & MCU block layout) NppiSize oDstImageSize = new NppiSize(); float frameWidth = (float)Math.Floor((float)oFrameHeader.nWidth); float frameHeight = (float)Math.Floor((float)oFrameHeader.nHeight); oDstImageSize.width = (int)Math.Max(1.0f, frameWidth); oDstImageSize.height = (int)Math.Max(1.0f, frameHeight); //Console.WriteLine("Output Size: " + oDstImageSize.width + "x" + oDstImageSize.height + "x" + (int)(oFrameHeader.nComponents)); apDstImage[0] = srcY; apDstImage[1] = srcCb; apDstImage[2] = srcCr; int nMCUBlocksH = 0; int nMCUBlocksV = 0; // Compute channel sizes as stored in the JPEG (8x8 blocks & MCU block layout) for (int i = 0; i < oFrameHeader.nComponents; ++i) { nMCUBlocksV = Math.Max(nMCUBlocksV, oFrameHeader.aSamplingFactors[i] >> 4); nMCUBlocksH = Math.Max(nMCUBlocksH, oFrameHeader.aSamplingFactors[i] & 0x0f); } for (int i = 0; i < oFrameHeader.nComponents; ++i) { NppiSize oBlocks = new NppiSize(); NppiSize oBlocksPerMCU = new NppiSize(oFrameHeader.aSamplingFactors[i] & 0x0f, oFrameHeader.aSamplingFactors[i] >> 4); oBlocks.width = (int)Math.Ceiling((oFrameHeader.nWidth + 7) / 8 * (float)(oBlocksPerMCU.width) / nMCUBlocksH); oBlocks.width = DivUp(oBlocks.width, oBlocksPerMCU.width) * oBlocksPerMCU.width; oBlocks.height = (int)Math.Ceiling((oFrameHeader.nHeight + 7) / 8 * (float)(oBlocksPerMCU.height) / nMCUBlocksV); oBlocks.height = DivUp(oBlocks.height, oBlocksPerMCU.height) * oBlocksPerMCU.height; // Allocate Memory apdDCT[i] = new NPPImage_16sC1(oBlocks.width * 64, oBlocks.height); } /*************************** * * Output * ***************************/ // Forward DCT for (int i = 0; i < 3; ++i) { compression.DCTQuantFwd8x8LS(apDstImage[i], apdDCT[i], aDstSize[i], pdQuantizationTables[oFrameHeader.aQuantizationTableSelector[i]]); } // Huffman Encoding CudaDeviceVariable<byte> pdScan = new CudaDeviceVariable<byte>(BUFFER_SIZE); int nScanLength = 0; int nTempSize = JPEGCompression.EncodeHuffmanGetSize(aDstSize[0], 3); CudaDeviceVariable<byte> pJpegEncoderTemp = new CudaDeviceVariable<byte>(nTempSize); NppiEncodeHuffmanSpec[] apHuffmanDCTableEnc = new NppiEncodeHuffmanSpec[3]; NppiEncodeHuffmanSpec[] apHuffmanACTableEnc = new NppiEncodeHuffmanSpec[3]; for (int i = 0; i < 3; ++i) { apHuffmanDCTableEnc[i] = JPEGCompression.EncodeHuffmanSpecInitAlloc(aHuffmanTables[(oScanHeader.aHuffmanTablesSelector[i] >> 4)].aCodes, NppiHuffmanTableType.nppiDCTable); apHuffmanACTableEnc[i] = JPEGCompression.EncodeHuffmanSpecInitAlloc(aHuffmanTables[(oScanHeader.aHuffmanTablesSelector[i] & 0x0f) + 2].aCodes, NppiHuffmanTableType.nppiACTable); } JPEGCompression.EncodeHuffmanScan(apdDCT, 0, oScanHeader.nSs, oScanHeader.nSe, oScanHeader.nA >> 4, oScanHeader.nA & 0x0f, pdScan, ref nScanLength, apHuffmanDCTableEnc, apHuffmanACTableEnc, aDstSize, pJpegEncoderTemp); for (int i = 0; i < 3; ++i) { JPEGCompression.EncodeHuffmanSpecFree(apHuffmanDCTableEnc[i]); JPEGCompression.EncodeHuffmanSpecFree(apHuffmanACTableEnc[i]); } // Write JPEG to byte array, as in original sample code byte[] pDstOutput = new byte[BUFFER_SIZE]; int pos = 0; oFrameHeader.nWidth = (ushort)oDstImageSize.width; oFrameHeader.nHeight = (ushort)oDstImageSize.height; writeMarker(0x0D8, pDstOutput, ref pos); writeJFIFTag(pDstOutput, ref pos); writeQuantizationTable(aQuantizationTables[0], pDstOutput, ref pos); writeQuantizationTable(aQuantizationTables[1], pDstOutput, ref pos); writeFrameHeader(oFrameHeader, pDstOutput, ref pos); writeHuffmanTable(aHuffmanTables[0], pDstOutput, ref pos); writeHuffmanTable(aHuffmanTables[1], pDstOutput, ref pos); writeHuffmanTable(aHuffmanTables[2], pDstOutput, ref pos); writeHuffmanTable(aHuffmanTables[3], pDstOutput, ref pos); writeScanHeader(oScanHeader, pDstOutput, ref pos); pdScan.CopyToHost(pDstOutput, 0, pos, nScanLength); pos += nScanLength; writeMarker(0x0D9, pDstOutput, ref pos); FileStream fs = new FileStream(aFilename, FileMode.Create, FileAccess.Write); fs.Write(pDstOutput, 0, pos); fs.Close(); //cleanup: fs.Dispose(); pJpegEncoderTemp.Dispose(); pdScan.Dispose(); apdDCT[2].Dispose(); apdDCT[1].Dispose(); apdDCT[0].Dispose(); pdQuantizationTables[1].Dispose(); pdQuantizationTables[0].Dispose(); srcCr.Dispose(); srcCb.Dispose(); srcY.Dispose(); src.Dispose(); compression.Dispose(); }
private void btn_Resize_Click(object sender, EventArgs e) { if ((Bitmap)pic_Image.Image == null) return; Bitmap bmp = (Bitmap)pic_Image.Image; int w = bmp.Width; int h = bmp.Height; if ((w <= 16 || h <= 16) && trk_Size.Value < 100) { MessageBox.Show("Image is too small for resizing."); return; } int newW = (int)(trk_Size.Value / 100.0f * w); int newH = (int)(trk_Size.Value / 100.0f * h); if (newW % 16 != 0) { newW = newW - (newW % 16); } if (newW < 16) newW = 16; if (newH % 16 != 0) { newH = newH - (newH % 16); } if (newH < 16) newH = 16; double ratioW = newW / (double)w; double ratioH = newH / (double)h; if (ratioW == 1 && ratioH == 1) return; if (bmp.PixelFormat != System.Drawing.Imaging.PixelFormat.Format24bppRgb) { MessageBox.Show("Only three channel color images are supported!"); return; } NPPImage_8uC3 imgIn = new NPPImage_8uC3(w, h); NPPImage_8uC3 imgOut = new NPPImage_8uC3(newW, newH); InterpolationMode interpol = InterpolationMode.SuperSampling; if (ratioH >= 1 || ratioW >= 1) interpol = InterpolationMode.Lanczos; imgIn.CopyToDevice(bmp); imgIn.ResizeSqrPixel(imgOut, ratioW, ratioH, 0, 0, interpol); Bitmap bmpRes = new Bitmap(newW, newH, System.Drawing.Imaging.PixelFormat.Format24bppRgb); imgOut.CopyToHost(bmpRes); pic_Image.Image = bmpRes; imgIn.Dispose(); imgOut.Dispose(); }
private void btn_open_Click(object sender, EventArgs e) { if (!_nppOK) return; CleanUp(); OpenFileDialog ofd = new OpenFileDialog(); ofd.Filter = "Images|*.jpg;*.bmp;*.png;*.tif"; if (ofd.ShowDialog() != System.Windows.Forms.DialogResult.OK) return; Bitmap src = new Bitmap(ofd.FileName); switch (src.PixelFormat) { case PixelFormat.Format24bppRgb: _colorChannels = 3; break; case PixelFormat.Format32bppArgb: _colorChannels = 4; break; case PixelFormat.Format32bppRgb: _colorChannels = 4; break; case PixelFormat.Format8bppIndexed: _colorChannels = 1; break; default: _colorChannels = 0; txt_info.AppendText(ofd.FileName + " has an unsupported pixel format.\n"); break; } try { switch (_colorChannels) { case 1: //Allocate memory on device for one channel images... src_c1 = new NPPImage_8uC1(src.Width, src.Height); dest_c1 = new NPPImage_8uC1(src.Width, src.Height); src_c1.CopyToDevice(src); txt_info.AppendText("Info: Loaded image '" + ofd.FileName + "' succesfully (Size: " + src.Width.ToString() + " x " + src.Height.ToString() + ", color channels: " + _colorChannels.ToString() + ")\n"); break; case 3: //As of version 5, NPP has new histogram and LUT functions for three channel images, no more need to convert first to 4 channels. //Allocate memory on device for four channel images... src_c3 = new NPPImage_8uC3(src.Width, src.Height); dest_c3 = new NPPImage_8uC3(src.Width, src.Height); //Fill 3 channel image in device memory src_c3.CopyToDevice(src); txt_info.AppendText("Info: Loaded image '" + ofd.FileName + "' succesfully (Size: " + src.Width.ToString() + " x " + src.Height.ToString() + ", color channels: " + _colorChannels.ToString() + ")\n"); break; case 4: //Allocate memory on device for four channel images... src_c4 = new NPPImage_8uC4(src.Width, src.Height); dest_c4 = new NPPImage_8uC4(src.Width, src.Height); src_c4.CopyToDevice(src); txt_info.AppendText("Info: Loaded image '" + ofd.FileName + "' succesfully (Size: " + src.Width.ToString() + " x " + src.Height.ToString() + ", color channels: " + _colorChannels.ToString() + ")\n"); break; } } catch (Exception ex) { if (ex is NPPException) { txt_info.AppendText("NPPException: " + ex.Message + "\n"); CleanUp(); } else if (ex is CudaException) { txt_info.AppendText("CudaException: " + ex.Message + "\n"); CleanUp(); } else throw; } //Show original image pictureBox_src.Image = src; }