/// Applies a Gaussian blur to the rows of an image. /// @param channelToBlur A gray-scale channel to be blurred. private void ExecuteHorizontalBlur(ref MonochromeBitmap channelToBlur) { // We will blur each row in parallel. int numberOfThreads = channelToBlur.height; OnionLogger.globalLog.PushInfoLayer(string.Format("Setting up {0} GrayscaleGaussianBlur threads", numberOfThreads)); // We will keep an active thread count that is atomically decremented when each thread finishes. // When this count reaches 0, then we know that all threads have finished and can set "allThreadsDone". activeThreadCount = numberOfThreads; allThreadsDone = new ManualResetEvent(false); // Initialize thread objects GrayscaleGaussianBlurThread[] threads = new GrayscaleGaussianBlurThread[numberOfThreads]; for (int i = 0; i < numberOfThreads; ++i) { threads[i] = new GrayscaleGaussianBlurThread(this, i); } OnionLogger.globalLog.PopInfoLayer(); OnionLogger.globalLog.PushInfoLayer("Running Gaussian blur"); for (int i = 0; i < numberOfThreads; ++i) { ThreadPool.QueueUserWorkItem(new WaitCallback(threads[i].BlurRow), channelToBlur); } if (!allThreadsDone.WaitOne(500)) { string logStr = string.Format("Timed out after 500ms - {0} threads unfinished.", activeThreadCount); OnionLogger.globalLog.LogError(logStr); } OnionLogger.globalLog.PopInfoLayer(); /* Done with parallel section. */ }
private void CreateMonochromeBitmap() { if (m_originalBitmap == null || m_doNotUpdateMonochrome) { return; } using (var scaledImage = m_originalBitmap.Resize(new Size(m_width, m_height))) { if (m_monochromeBitmap != null) { m_monochromeBitmap.Dispose(); m_monochromeBitmap = null; } var mode = (NamedItemContainer <MonochromeConversionMode>)ConversionTypeComboBox.SelectedItem; switch (mode.Data) { case MonochromeConversionMode.ThresholdBased: { m_monochromeBitmap = MonochromeBitmap.ConvertTo1Bit(scaledImage, MonochromeConversionMode.ThresholdBased, (int)ThresholdUpDown.Value); break; } case MonochromeConversionMode.FloydSteinbergDithering: { m_monochromeBitmap = MonochromeBitmap.ConvertTo1Bit(scaledImage); break; } default: throw new ArgumentOutOfRangeException(); } } }
/// <summary> /// Sets the bounds of the control to the specified location and size. /// </summary> /// <param name="x">The new <see cref="P:LogiFrame.LCDControl.Left" /> property value of the control.</param> /// <param name="y">The new <see cref="P:LogiFrame.LCDControl.Top" /> property value of the control.</param> /// <param name="width">The new <see cref="P:LogiFrame.LCDControl.Width" /> property value of the control.</param> /// <param name="height">The new <see cref="P:LogiFrame.LCDControl.Height" /> property value of the control.</param> /// <param name="preventInvalidation">if set to <c>true</c> invalidation is prevented.</param> protected virtual void SetBounds(int x, int y, int width, int height, bool preventInvalidation) { ThrowIfDisposed(); if (width < 1) { width = 1; } if (height < 1) { height = 1; } if (_left == x && _top == y && _width == width && _height == height) { return; } _left = x; _top = y; _width = width; _height = height; Bitmap = new MonochromeBitmap(Width, Height); if (!preventInvalidation) { Invalidate(); } }
/** * Transposes the data in a bitmap. * @param bitmap The bitmap that will be transposed. */ public static void Transpose(ref MonochromeBitmap bitmap) { OnionLogger.globalLog.PushInfoLayer("Transposing bitmap of length " + (bitmap.height * bitmap.width)); float[] transposedData = new float[bitmap.channel.Length]; int insertionIndex = 0; for (int x = 0; x < bitmap.width; ++x) { for (int y = 0; y < bitmap.height; ++y) { int pixelIndex = y * bitmap.width + x; //OnionLogger.globalLog.LogTrace(string.Format("Moving pixel {0} to {1}", pixelIndex, insertionIndex)); transposedData[insertionIndex++] = bitmap.channel[pixelIndex]; } } // set the new data bitmap.channel = transposedData; // swap width and height int temp = bitmap.height; bitmap.height = bitmap.width; bitmap.width = temp; OnionLogger.globalLog.PopInfoLayer(); }
/// <summary> /// Initializes a new instance of the <see cref="LCDPaintEventArgs" /> class. /// </summary> /// <param name="bitmap">The bitmap.</param> /// <exception cref="System.ArgumentNullException">Thrown if bitmap is null.</exception> public LCDPaintEventArgs(MonochromeBitmap bitmap) { if (bitmap == null) { throw new ArgumentNullException(nameof(bitmap)); } Bitmap = bitmap; }
/** * Extract a MonochromeBitmap from the blue channel of an RGBBitmap. * @param rgbBitmap The RGBBitmap to extract B from. * @return A MonochromeBitmap containing the blue channel. */ public static MonochromeBitmap blueBitmapFromRGBBitmap(ref RGBBitmap rgbBitmap) { MonochromeBitmap blue = new MonochromeBitmap(); blue.channel = rgbBitmap.rgb.b; blue.height = rgbBitmap.height; blue.width = rgbBitmap.width; return(blue); }
/** * Extract a MonochromeBitmap from the green channel of an RGBBitmap. * @param rgbBitmap The RGBBitmap to extract G from. * @return A MonochromeBitmap containing the green channel. */ public static MonochromeBitmap greenBitmapFromRGBBitmap(ref RGBBitmap rgbBitmap) { MonochromeBitmap green = new MonochromeBitmap(); green.channel = rgbBitmap.rgb.g; green.height = rgbBitmap.height; green.width = rgbBitmap.width; return(green); }
/** * Extract a MonochromeBitmap from the red channel of an RGBBitmap. * @param rgbBitmap The RGBBitmap to extract R from. * @return A MonochromeBitmap containing the red channel. */ public static MonochromeBitmap redBitmapFromRGBBitmap(ref RGBBitmap rgbBitmap) { MonochromeBitmap red = new MonochromeBitmap(); red.channel = rgbBitmap.rgb.r; red.height = rgbBitmap.height; red.width = rgbBitmap.width; return(red); }
/** * Inverts the values in a single channel, represented as an array of floats * @param state An object containing a MonochromeBitmap. * Must be 'object' because of how .NET handles threads. */ public void ThreadedProcessChannel(object state) { MonochromeBitmap monochromeBitmap = (MonochromeBitmap)state; for (int i = 0; i < monochromeBitmap.channel.Length; ++i) { monochromeBitmap.channel[i] = 1f - monochromeBitmap.channel[i]; } done.Set(); }
/** * Create a deep copy of this monochrome bitmap. * @param original The bitmap to copy. * @return A deep copy of the original. */ public static MonochromeBitmap Copy(ref MonochromeBitmap original) { MonochromeBitmap copy = new MonochromeBitmap(); copy.height = original.height; copy.width = original.width; copy.channel = (float[])original.channel.Clone(); return(copy); }
/// <summary> /// Pushes the specified bitmap to the LCD. /// </summary> /// <param name="bitmap">The bitmap.</param> private void Push(MonochromeBitmap bitmap) { if (bitmap == null) { throw new ArgumentNullException(nameof(bitmap)); } var render = new MonochromeBitmap(bitmap, (int)LgLcd.BitmapWidth, (int)LgLcd.BitmapHeight); var lgBitmap = new LgLcd.Bitmap160X43X1 { Header = { Format = LgLcd.BitmapFormat160X43X1 }, Pixels = render.Pixels }; LgLcd.UpdateBitmap(_device, ref lgBitmap, (uint)UpdatePriority); OnRendered(new RenderedEventArgs(render)); }
/** * Blurs a single row of a bitmap. * @param state An object containing a MonochromeBitmap. * Must be 'object' because of how .NET handles threads. */ public void BlurRow(object state) { MonochromeBitmap blurred = (MonochromeBitmap)state; this.original = ColorUtils.Copy(ref blurred); // Zero the row we are blurring. int startingPixel = rowNumber * blurred.width; int finishPixel = startingPixel + blurred.width; for (int i = startingPixel; i < finishPixel; ++i) { blurred.channel[i] = 0f; } int numberOfCoefficients = this.coefficients.Length - 1; for (int j = 0; j < blurred.width; ++j) { int targetPixel = rowNumber * blurred.width + j; for (int k = 0; k <= numberOfCoefficients; ++k) { if (j + k < 0 || j + k >= blurred.width) { continue; } int sourcePoint = targetPixel + k; float factor = coefficients[Mathf.Abs(k)]; float weightedValue = factor * original.channel[sourcePoint]; blurred.channel[targetPixel] += weightedValue; } } // This thread is considered finished. We atomically decrement, and if the active thread count hits 0 then // this thread is the last to finish. if (Interlocked.Decrement(ref threadManager.activeThreadCount) <= 0) { threadManager.allThreadsDone.Set(); } }
/// <summary> /// Called after the control has been added to another container. /// </summary> protected virtual void InitLayout() { if (_isLayoutInit) { Invalidate(); return; } _isLayoutInit = true; if (_width == 0 || _height == 0) { SetBounds(0, 0, 1, 1); } else { Bitmap = new MonochromeBitmap(Width, Height); } Invalidate(); }
/** * Applies gaussian blur effect to a grayscale bitmap. * @param colorBitmap the image to be processed. */ public void ProcessBitmap(ref ColorBitmap colorBitmap) { OnionLogger.globalLog.PushInfoLayer("Performing GrayscaleGaussianBlur on bitmap."); RGBBitmap rgbBitmap = new RGBBitmap(); ColorUtils.colorBitmapToRGBBitmap(ref colorBitmap, ref rgbBitmap); /* Since we are assuming the image is grayscale, we can apply the blur to just one * channel. We arbitrarily choose the red channel. */ MonochromeBitmap blurR = ColorUtils.redBitmapFromRGBBitmap(ref rgbBitmap); // temporarily hard-coded int numberOfCoefficients = 5; this.coefficients = ApproximateGaussianCoefficients(numberOfCoefficients); OnionLogger.globalLog.PushInfoLayer("Blurring rows"); ExecuteHorizontalBlur(ref blurR); ColorUtils.Transpose(ref blurR); /* transpose to set up for blurring columns */ OnionLogger.globalLog.PopInfoLayer(); // Since we transposed the image, we can re-use the horizontal blur operation to blur the columns. OnionLogger.globalLog.PushInfoLayer("Blurring columns"); ExecuteHorizontalBlur(ref blurR); ColorUtils.Transpose(ref blurR); /* reset the image to original orientation */ OnionLogger.globalLog.PopInfoLayer(); // Since we assumed that we are using a grayscale image, the Blue and Green channels can be copied from // the red channel. rgbBitmap.rgb.r = blurR.channel; rgbBitmap.rgb.b = blurR.channel; rgbBitmap.rgb.g = blurR.channel; ColorUtils.RGBBitmapToColorBitmap(ref rgbBitmap, ref colorBitmap); OnionLogger.globalLog.PopInfoLayer(); }
/** * Invert the colors of a bitmap. * * @param colorBitmap The ColorBitmap to invert. */ public void ProcessBitmap(ref ColorBitmap colorBitmap) { OnionLogger.globalLog.PushInfoLayer("Inverting bitmap"); RGBBitmap rgbBitmap = new RGBBitmap(); ColorUtils.colorBitmapToRGBBitmap(ref colorBitmap, ref rgbBitmap); MonochromeBitmap invertedr = ColorUtils.redBitmapFromRGBBitmap(ref rgbBitmap); MonochromeBitmap invertedg = ColorUtils.greenBitmapFromRGBBitmap(ref rgbBitmap); MonochromeBitmap invertedb = ColorUtils.blueBitmapFromRGBBitmap(ref rgbBitmap); // We'll run a thread for each color channel. int numberOfThreads = 3; ManualResetEvent[] threadsDone = new ManualResetEvent[numberOfThreads]; ColorInvertThread[] threads = new ColorInvertThread[numberOfThreads]; for (int i = 0; i < numberOfThreads; ++i) { threadsDone[i] = new ManualResetEvent(false); threads[i] = new ColorInvertThread(threadsDone[i]); } ThreadPool.QueueUserWorkItem(new WaitCallback(threads[0].ThreadedProcessChannel), invertedr); ThreadPool.QueueUserWorkItem(new WaitCallback(threads[1].ThreadedProcessChannel), invertedg); ThreadPool.QueueUserWorkItem(new WaitCallback(threads[2].ThreadedProcessChannel), invertedb); WaitHandle.WaitAll(threadsDone); // Done with parallel section. rgbBitmap.rgb.r = invertedr.channel; rgbBitmap.rgb.g = invertedg.channel; rgbBitmap.rgb.b = invertedb.channel; ColorUtils.RGBBitmapToColorBitmap(ref rgbBitmap, ref colorBitmap); OnionLogger.globalLog.PopInfoLayer(); }
private void ExportBitmapMenuItem_Click(object sender, EventArgs e) { if (SelectedImageMetadata.Count == 0) { return; } string directoryPath; using (var fb = new FolderBrowserDialog()) { if (fb.ShowDialog() != DialogResult.OK) { return; } directoryPath = fb.SelectedPath; } var exportData = SelectedImageMetadata.Select(x => new { Metadata = x, ImageData = m_firmware.ReadImage(x) }); foreach (var data in exportData) { try { using (var image = MonochromeBitmap.Create1BitBitmapFromRaw(data.ImageData)) { var fileName = Path.Combine(directoryPath, "0x" + data.Metadata.Index.ToString("X2") + Consts.BitmapFileExtensionWoAsterisk); image.Save(fileName, ImageFormat.Bmp); } } catch { // Ignore } } }
/// <summary> /// Initializes a new instance of the <see cref="RenderedEventArgs" /> class. /// </summary> /// <param name="bitmap">The bitmap.</param> public RenderedEventArgs(MonochromeBitmap bitmap) { Bitmap = bitmap; }