C# Forms Print Image As Big As Possible [duplicate] - c#

This question already has answers here:
c# Image resizing to different size while preserving aspect ratio
(14 answers)
Closed 4 years ago.
I want to print an image on Windows Forms, but I want to print the image as big as possible on the page without breaking its aspect ratio. Like when I want to print a simple square to A4 paper it becomes rectangle with this script:
private void Page(object sender, PrintPageEventArgs e)
{
Bitmap i = image;
if(e.PageBounds.Height > e.PageBounds.Width)
{
if (i.Width > i.Height)
i.RotateFlip(RotateFlipType.Rotate90FlipNone);
}
else if(e.PageBounds.Width > e.PageBounds.Height)
{
if (i.Height > i.Width)
i.RotateFlip(RotateFlipType.Rotate90FlipNone);
}
e.Graphics.DrawImage(i, new Rectangle(0, 0, e.PageBounds.Width, e.PageBounds.Height));
i.Dispose();
}
Original:
500x500 square
What I got (as pdf):
A rectangle on the A4 paper
Another example:
Original:Original image(313x234)
Print:Print image
I don't want to break the aspect ratio of the image

Try this:
private void Page(object sender, PrintPageEventArgs e)
{
using (Bitmap i = image) //are you *really sure* you want to dispose this after printing?
{
var pageRatio = e.PageBounds.Width / (double)e.PageBounds.Height;
var imageRatio = i.Width / (double)i.Height;
//do we need to rotate?
if ( (pageRatio < 1 && imageRatio > 1) || (pageRatio < 1 && imageRatio > 1))
{
i.RotateFlip(RotateFlipType.Rotate90FlipNone);
imageRatio = i.Width / (double)i.Height; //ratio will have changed after rotation
}
var scale = 1.0D;
//do we need to scale?
if (pageRatio > imageRatio)
{ //the page is wider than the image, so scale to the height
scale = e.PageBounds.Height / (double)i.Height;
}
else if (pageRatio < imageRatio)
{ //the page is narrower than the image, so scale to width
scale = e.PageBounds.Width / (double)i.Width;
}
var W = (int)(scale * i.Width);
var H = (int)(scale * i.Height);
e.Graphics.DrawImage(i, new Rectangle(0, 0, W, H));
}
}
This will always draw from the top left corner. If you want it centered, you need to make additional adjustments:
private void Page(object sender, PrintPageEventArgs e)
{
using (Bitmap i = image) //are you *really sure* you want to dispose this after printing?
{
var pageRatio = e.PageBounds.Width / (double)e.PageBounds.Height;
var imageRatio = i.Width / (double)i.Height;
//do we need to rotate?
if ( (pageRatio < 1 && imageRatio > 1) || (pageRatio < 1 && imageRatio > 1))
{
i.RotateFlip(RotateFlipType.Rotate90FlipNone);
imageRatio = i.Width / (double)i.Height; //ratio will have changed after rotation
}
int T = 0, L = 0; //top, left
var scale = 1.0D;
int W = i.Width, H = i.Height;
//do we need to scale?
if (pageRatio > imageRatio)
{ //the page is wider than the image, so scale to the height
scale = e.PageBounds.Height / (double)i.Height;
W = (int)(scale * i.Width);
H = (int)(scale * i.Height);
L = (e.PageBounds.Width - W)/2;
}
else if (pageRatio < imageRatio)
{ //the page is narrower than the image, so scale to width
scale = e.PageBounds.Width / (double)i.Width;
W = (int)(scale * i.Width);
H = (int)(scale * i.Height);
T = (e.PageBounds.Height - H)/2;
}
e.Graphics.DrawImage(i, new Rectangle(L, T, W+L, H+T));
}
}
The important thing is both the width and height are adjusted to the same scale... that is, multiplied by the same number.

Related

Crop correct part of image while the PictureBox is in 'zoom' mode [duplicate]

This question already has an answer here:
Draw Rectangle inside picture box SizeMode Zoom
(1 answer)
Closed 3 years ago.
I have a PictureBox1 with it's sizemode set to Stretch and PictureBox1. The PictureBox1 contains an image and let's me select part of it and then crop it and store the cropped part inside PictureBox2. It works great when the sizemode is set to Stretch and the picture is not zoomed, but not when I zoom it or set the sizemode to zoom.
working example - sizemode set to 'stretch'
The code I use to crop part of the picture (original source)
try
{
float stretch1X = 1f * pictureBox1.Image.Width / pictureBox1.ClientSize.Width;
float stretch1Y = 1f * pictureBox1.Image.Height / pictureBox1.ClientSize.Height;
Point pt = new Point((int)(_mDown.X * stretch1X), (int)(_mDown.Y * stretch1Y));
Size sz = new Size((int)((_mCurr.X - _mDown.X) * stretch1X),
(int)((_mCurr.Y - _mDown.Y) * stretch1Y));
if (sz.Width > 0 && sz.Height > 0)
{
Rectangle rSrc = new Rectangle(pt, sz);
Rectangle rDest = new Rectangle(Point.Empty, sz);
Bitmap bmp = new Bitmap(sz.Width, sz.Height);
using (Graphics G = Graphics.FromImage(bmp))
G.DrawImage(pictureBox1.Image, rDest, rSrc, GraphicsUnit.Pixel);
return bmp;
}
return null;
}
catch (Exception ex)
{
throw ex;
}
How do I calculate it properly? How can I make the crop function work in a way so it lets the user zoom in/out and still crop the correct part of the picture?
You need to calculate the points using the stretch factor and maybe also the offset.
For Zoom there is only one factor as aspect ratio is always the same for Image and PictureBox, but there usually is an offset; for Stretch you need no offset but two factors.
Here is an example that goes all the way using two PictureBoxes two show a zoomed version and the cropped bitmap. It makes use of an all-purpose function ImageArea that determines size and offset.
Two class level variables:
Point pDown = Point.Empty;
Rectangle rect = Rectangle.Empty;
Three mouse events:
private void PictureBox1_MouseDown(object sender, MouseEventArgs e)
{
pDown = e.Location;
pictureBox1.Refresh();
}
private void PictureBox1_MouseMove(object sender, MouseEventArgs e)
{
if (!e.Button.HasFlag(MouseButtons.Left)) return;
rect = new Rectangle(pDown, new Size(e.X - pDown.X, e.Y - pDown.Y));
using (Graphics g = pictureBox1.CreateGraphics())
{
pictureBox1.Refresh();
g.DrawRectangle(Pens.Orange, rect);
}
}
private void PictureBox1_MouseUp(object sender, MouseEventArgs e)
{
Rectangle iR = ImageArea(pictureBox2);
rect = new Rectangle(pDown.X - iR.X, pDown.Y - iR.Y,
e.X - pDown.X, e.Y - pDown.Y);
Rectangle rectSrc = Scaled(rect, pictureBox2, true);
Rectangle rectDest = new Rectangle(Point.Empty, rectSrc.Size);
Bitmap bmp = new Bitmap(rectDest.Width, rectDest.Height);
using (Graphics g = Graphics.FromImage(bmp))
{
g.DrawImage(pictureBox2.Image, rectDest, rectSrc, GraphicsUnit.Pixel);
}
pictureBox2.Image = bmp;
}
Here is a useful function that returns the area of the actual image inside a picturebox for any sizemode..:
Rectangle ImageArea(PictureBox pbox)
{
Size si = pbox.Image.Size;
Size sp = pbox.ClientSize;
if (pbox.SizeMode == PictureBoxSizeMode.StretchImage)
return pbox.ClientRectangle;
if (pbox.SizeMode == PictureBoxSizeMode.Normal ||
pbox.SizeMode == PictureBoxSizeMode.AutoSize)
return new Rectangle(Point.Empty, si);
if (pbox.SizeMode == PictureBoxSizeMode.CenterImage)
return new Rectangle(new Point((sp.Width - si.Width) / 2,
(sp.Height - si.Height) / 2), si);
// PictureBoxSizeMode.Zoom
float ri = 1f * si.Width / si.Height;
float rp = 1f * sp.Width / sp.Height;
if (rp > ri)
{
int width = si.Width * sp.Height / si.Height;
int left = (sp.Width - width) / 2;
return new Rectangle(left, 0, width, sp.Height);
}
else
{
int height = si.Height * sp.Width / si.Width;
int top = (sp.Height - height) / 2;
return new Rectangle(0, top, sp.Width, height);
}
}
We only need the offset to determine the rectangle unscaled. We also need to scale it:
Rectangle Scaled(Rectangle rect, PictureBox pbox, bool scale)
{
float factor = GetFactor(pbox);
if (!scale) factor = 1f / factor;
return Rectangle.Round(new RectangleF(rect.X * factor, rect.Y * factor,
rect.Width * factor, rect.Height * factor));
}
For this need to know the scaling factor, which depends on the aspect ratio:
float GetFactor(PictureBox pBox)
{
if (pBox.Image == null) return 0;
Size si = pBox.Image.Size;
Size sp = pBox.ClientSize;
float ri = 1f * si.Width / si.Height;
float rp = 1f * sp.Width / sp.Height;
float factor = 1f * pBox.Image.Width / pBox.ClientSize.Width;
if (rp > ri) factor = 1f * pBox.Image.Height / pBox.ClientSize.Height;
return factor;
}
This solution will also work if the PictureBox is zoomed in or out by placing it inside a AutoScrolling Panel and changing the Pbox.Size.

Set Specified region for Motion Detection

I need to specify a region on where motion detection will occur. What I am trying to do is count the number of vehicles that passes a certain region. Below is my code:
private static void ProcessFrame(Mat backgroundFrame, int threshold, int erodeIterations, int dilateIterations)
{
// Find difference between background (first) frame and current frame
CvInvoke.AbsDiff(backgroundFrame, rawFrame, diffFrame);
// Apply binary threshold to grayscale image (white pixel will mark difference)
CvInvoke.CvtColor(diffFrame, grayscaleDiffFrame, ColorConversion.Bgr2Gray);
CvInvoke.Threshold(grayscaleDiffFrame, binaryDiffFrame, threshold, 255, ThresholdType.Binary);
// Remove noise with opening operation (erosion followed by dilation)
CvInvoke.Erode(binaryDiffFrame, denoisedDiffFrame, null, new Point(-1, -1), erodeIterations, BorderType.Default, new MCvScalar(1));
CvInvoke.Dilate(denoisedDiffFrame, denoisedDiffFrame, null, new Point(-1, -1), dilateIterations, BorderType.Default, new MCvScalar(1));
rawFrame.CopyTo(finalFrame);
//Rectangle rec = new Rectangle(100, 100, 100, 100);
//finalFrame = crop_color_frame(rawFrame, rec);
var img = crop_color_frame(denoisedDiffFrame, rec);
DetectObject(denoisedDiffFrame, finalFrame);
}
static int vnum = 0;
private static void DetectObject(Mat detectionFrame, Mat displayFrame)
{
using (VectorOfVectorOfPoint contours = new VectorOfVectorOfPoint())
{
// Build list of contours
CvInvoke.FindContours(detectionFrame, contours, null, RetrType.List, ChainApproxMethod.ChainApproxSimple);
// Selecting largest contour
if (contours.Size > 0)
{
double maxArea = 0;
int chosen = 0;
for (int i = 0; i < contours.Size; i++)
{
VectorOfPoint contour = contours[i];
double area = CvInvoke.ContourArea(contour);
if (area > maxArea)
{
maxArea = area;
chosen = i;
}
}
// Draw on a frame
MarkDetectedObject(displayFrame, contours[chosen], maxArea, contours.Size, maxArea);
}
}
}
private static void MarkDetectedObject(Mat frame, VectorOfPoint contour, double area, double contourSize, double maxArea)
{
// Getting minimal rectangle which contains the contour
Rectangle box = CvInvoke.BoundingRectangle(contour);
// Drawing contour and box around it
CvInvoke.Polylines(frame, contour, true, drawingColor);
CvInvoke.Rectangle(frame, box, drawingColor);
// Write information next to marked object
Point center = new Point(box.X + box.Width / 2, box.Y + box.Height / 2);
Point center2 = new Point(box.Width, box.Height);
var info = new string[] {
$"Area: {area}",
$"Position: {center.X}, {center.Y}"
};
Console.WriteLine($"X: {center.X} | Y: {center.Y} | Area: {area} | Count: {vnum} | Status: {vehicleState} | contour: {contour.Size}");
switch (vehicleState)
{
case VehicleState.Entering:
if(_startCount)
{
//if(((maxArea > 15000 && maxArea <= 20000) && center.Y <= 120) || ((maxArea >= 5000 && maxArea < 10000) && center.Y >= 150))
if(center.Y >= 100 && maxArea > 20000)
{
CountVehicle();
vehicleState = VehicleState.Exiting;
_startCount = false;
}
}
break;
case VehicleState.Exiting:
if (!_startCount)
{
//if(maxArea < 12000 && center.Y <= 120)
if(center.Y <= 130 && center.X <= 100 && maxArea <= 15000)
{
vehicleState = VehicleState.Entering;
_startCount = true;
}
}
break;
}
WriteMultilineText(frame, info, new Point(box.Right + 5, center.Y));
}
As of the moment, this code works on detecting vehicles but I am just using the
if(center.Y >= 100 && maxArea > 20000) condition to start counting the vehicles
the problem with that approach is, all movements in the frame are being monitored. That is why I need to set an specific region only.
Could you please show me how to do this?
You can set ROI for the input image
public static Mat crop_roi(Mat input_img)
{
Image<Gray, byte> img = input_img.ToImage<Gray, byte>();
double w = input_img.Width;
double h = input_img.Height;
Rectangle r = new Rectangle((int)(w * 0.2), (int)(h * 0.4), (int)(w * 0.6), (int)(h * 0.6));
Image<Gray, byte> output = img.Copy(r);
return output.Mat;
}
//USE
private static void DetectObject(Mat detectionFrame, Mat displayFrame)
{
using (VectorOfVectorOfPoint contours = new VectorOfVectorOfPoint())
{
//set roi to the frame
Mat roi = new Mat()
roi = set_roi(detectionFrame);
// Build list of contours
CvInvoke.FindContours(roi , contours, null, RetrType.List, ChainApproxMethod.ChainApproxSimple);
// Selecting largest contour
...
MarkDetectedObject(roi , contours[chosen], maxArea, contours.Size, maxArea);
}
Below is the image I draw the ROI in an image, you can adjust the ROI by changing the parameter in this line Rectangle r = new Rectangle((int)(w * 0.2), (int)(h * 0.4), (int)(w * 0.6), (int)(h * 0.6));

Circular Fisheye Image dewarp to flat image

UPDATE as on 12 Nov 2015
I used PanoTools plugin with Photoshop and Hugin and played with all those parameters. End up i found the parameters for projection, HFOV and image output size that fulfill my lowest requirement.
Parameteres:
Processed Output:
My question is then how can i convert all these parameters and values into C# algorithm coding so that when I provide the original image, i will get the corrected output image?
Thanks a lot.
I have a square image captured from a circular fisheye camera. The size is 2650 * 2650 pixels.
Now, i will need to programmatically dewarp the image to a flat panorama image using C# language.
I had look around from internet with different algorithm example from Link for code below , Link1 and Link2 but just can't make it success. My maths sincerely sucks and can't help me with that. Hopefully someone able to guide me through this.
Thanks a lot.
Example of image output from the camera:
--Image grabbed from Wikipedia Fisheye Lens & size modified to fit my sample pixel.
The code i tried to dewarp it but no luck:
Bitmap sourceImage = (Bitmap)Bitmap.FromFile("circularfisheye.jpg");
double factor = 0.5;
Boolean autoCrop = false;
Color backgroundColor = Color.White;
Bitmap StartImage = null;
BitmapData srcBitmapData = null;
Byte[] srcPixels = null;
Byte[] dstPixels = null;
Bitmap NewImage = null;
BitmapData dstBitmapData = null;
try
{
// Checks whether bpp ​​( Bits Per Pixel ) is 8 , 24, or 32
int Depth = System.Drawing.Bitmap.GetPixelFormatSize(sourceImage.PixelFormat);
if (Depth != 8 && Depth != 24 && Depth != 32)
{
throw new ArgumentException("Only 8, 24 and 32 bpp images are supported.");
}
// Retrieves the count of the color components
int cCount = Depth / 8;
Size baseSize = new Size(sourceImage.Width, sourceImage.Height);
// check if a low image resize and need to improve the quality
// and not generate image aliasing
Int32 maxSize = Math.Max(sourceImage.Width, sourceImage.Height);
if (maxSize < 3000)
{
float percent = 3000F / (float)maxSize;
baseSize = new Size((Int32)((float)sourceImage.Width * percent), (Int32)((float)sourceImage.Height * percent));
}
StartImage = new Bitmap(baseSize.Width, baseSize.Height, sourceImage.PixelFormat);
StartImage.SetResolution(sourceImage.HorizontalResolution, sourceImage.VerticalResolution);
// Create the drawing object and white background
Graphics g = Graphics.FromImage(StartImage);
g.SmoothingMode = SmoothingMode.AntiAlias;
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
g.PixelOffsetMode = PixelOffsetMode.HighQuality;
g.DrawImage(sourceImage, new Rectangle(-1, -1, baseSize.Width + 1, baseSize.Height + 1), 0, 0, sourceImage.Width, sourceImage.Height, GraphicsUnit.Pixel);
g.Dispose();
// Locks the source image and copies it to the byte array and releases the source image
srcBitmapData = StartImage.LockBits(new Rectangle(0, 0, StartImage.Width, StartImage.Height), ImageLockMode.ReadOnly, StartImage.PixelFormat);
srcPixels = new byte[StartImage.Width * StartImage.Height * (Depth / 8)];
Marshal.Copy(srcBitmapData.Scan0, srcPixels, 0, srcPixels.Length);
StartImage.UnlockBits(srcBitmapData);
srcBitmapData = null;
// Create the target image byte array
dstPixels = new Byte[srcPixels.Length];
// Fill the entire frame with the selected background color
Int32 index = ((1 * StartImage.Width) + 1) * cCount; //index = ((Y * Width) + X) * cCount
do
{
if (Depth == 32) //For 32 bpp defines Red , Green, Blue and Alpha
{
dstPixels[index++] = backgroundColor.B;
dstPixels[index++] = backgroundColor.G;
dstPixels[index++] = backgroundColor.R;
dstPixels[index++] = backgroundColor.A; // a
}
if (Depth == 24) //For 24 bpp defines Red , Green and Blue
{
dstPixels[index++] = backgroundColor.B;
dstPixels[index++] = backgroundColor.G;
dstPixels[index++] = backgroundColor.R;
}
if (Depth == 8)
// For 8 bpp defines the value of color ( Red , Green and Blue to be the same thing)
{
dstPixels[index++] = backgroundColor.B;
}
} while (index < srcPixels.Length);
// Calculate the maximum possible extent for the image and multiply by the desired factor
double amp = 0;
double ang = Math.PI * 0.5;
for (Int32 a = 0; a < StartImage.Height; a++)
{
int y = (int)((StartImage.Height / 2) - amp * Math.Sin(ang));
if ((y < 0) || (y > StartImage.Height))
break;
amp = a;
}
amp = (amp - 2) * (factor < -1 ? -1 : (factor > 1 ? 1 : factor));
// Define variables that calculates the cutoff points (if any)
Int32 x1, y1, x2, y2;
x1 = StartImage.Width;
y1 = StartImage.Height;
x2 = 0;
y2 = 0;
// Copy pixel by pixel for the new positions
index = ((1 * StartImage.Width) + 1) * cCount;
do
{
Int32 y = (Int32)((index / cCount) / StartImage.Width);
Int32 x = (index / cCount) - (y * StartImage.Width);
Point pt = NewPoint(new Point(x, y), StartImage.Width, StartImage.Height, amp, factor < 0);
//Values ​​for crop
if (factor >= 0)
{
if (x == StartImage.Width / 2)
{
if (pt.Y < y1)
y1 = pt.Y;
if (pt.Y > y2)
y2 = pt.Y;
}
if (y == StartImage.Height / 2)
{
if (pt.X < x1)
x1 = pt.X;
if (pt.X > x2)
x2 = pt.X;
}
}
else
{
if ((x == 1) && (y == 1))
{
y1 = pt.Y;
x1 = pt.X;
}
if ((x == StartImage.Width - 1) && (y == StartImage.Height - 1))
{
y2 = pt.Y;
x2 = pt.X;
}
}
//Bytes Index which will apply the pixel
Int32 dstIndex = ((pt.Y * StartImage.Width) + pt.X) * cCount;
if (Depth == 32)
{
dstPixels[dstIndex] = srcPixels[index++];
dstPixels[dstIndex + 1] = srcPixels[index++];
dstPixels[dstIndex + 2] = srcPixels[index++];
dstPixels[dstIndex + 3] = srcPixels[index++]; // a
}
if (Depth == 24)
{
dstPixels[dstIndex] = srcPixels[index++];
dstPixels[dstIndex + 1] = srcPixels[index++];
dstPixels[dstIndex + 2] = srcPixels[index++];
}
if (Depth == 8)
{
dstPixels[dstIndex] = srcPixels[index++];
}
} while (index < srcPixels.Length);
//Creates a new image based on the byte array previously created
NewImage = new Bitmap(StartImage.Width, StartImage.Height, StartImage.PixelFormat);
NewImage.SetResolution(StartImage.HorizontalResolution, StartImage.VerticalResolution);
dstBitmapData = NewImage.LockBits(new Rectangle(0, 0, StartImage.Width, StartImage.Height), ImageLockMode.WriteOnly, StartImage.PixelFormat);
Marshal.Copy(dstPixels, 0, dstBitmapData.Scan0, dstPixels.Length);
NewImage.UnlockBits(dstBitmapData);
//Generates the final image to crop or resize the real coo
Bitmap FinalImage = new Bitmap(sourceImage.Width + 1, sourceImage.Height, StartImage.PixelFormat);
NewImage.SetResolution(StartImage.HorizontalResolution, StartImage.VerticalResolution);
Graphics g1 = Graphics.FromImage(FinalImage);
g1.SmoothingMode = SmoothingMode.AntiAlias;
g1.InterpolationMode = InterpolationMode.HighQualityBicubic;
g1.PixelOffsetMode = PixelOffsetMode.HighQuality;
//Performs the cut if enabled automatic cutting and there is need to cut
if ((autoCrop) && ((x1 > 0) || (y1 > 0) || (x2 < NewImage.Height) || (y2 < NewImage.Height)))
{
Rectangle cropRect = new Rectangle(x1, y1, x2 - x1, y2 - y1);
g1.DrawImage(NewImage, new Rectangle(-1, -1, FinalImage.Width + 1, FinalImage.Height + 1), cropRect.X, cropRect.Y, cropRect.Width, cropRect.Height, GraphicsUnit.Pixel);
}
else
{
g1.DrawImage(NewImage, new Rectangle(-1, -1, FinalImage.Width + 1, FinalImage.Height + 1), 0, 0, NewImage.Width, NewImage.Height, GraphicsUnit.Pixel);
}
g1.Dispose();
g1 = null;
NewImage = null;
FinalImage.Save("output.jpg");
FinalImage.Dispose();
}
finally
{
srcBitmapData = null;
srcPixels = null;
dstPixels = null;
dstBitmapData = null;
}
Such a distortion as a symmetry of revolution.
In polar coordinates, with the pole at the center of the image, it is expressed as
r' = f(r)
Θ' = Θ
where the quote indicates the distorted coordinates. The function f is unknown and should be measured empirically, by calibration (looking at a regular target).
To correct the image, you need to invert the function f and apply the reverse transform to the image. In fact, it is easier to measure g directly by calibration. As a starting approximation, a simple model like
r = r' + a.r'³
can do.
Most probably you don't have a picture of a grid taken with the same lens. Your last resort is to implement the undistortion function with adjustable parameters, and optimize these by trial and error.
It should also be possible to derive the calibration curve by looking at the deformation of straight lines, but this is more "technical".
In Cartesian coordinates, you can express the correction transform as
x = g(r').x'/r'
y = g(r').y'/r'
where r' = √x'²+y'².
Use the algorithm from here:
http://www.helviojunior.com.br/fotografia/barrel-and-pincushion-distortion/
It worked for me
I've made some revamp to the HelvioJunior's library (that was linked by #Tarek.Mh), I think this may suit your need:
Below, the code:
using System.Drawing;
using System.Drawing.Imaging;
using System.Linq;
using System.Runtime.InteropServices;
using static System.Math;
namespace HelvioJunior
{
//https://www.helviojunior.com.br/fotografia/barrel-and-pincushion-distortion/
public class Program
{
private static void Main(string[] args)
{
Bitmap source = (Bitmap)Image.FromFile(#"JpwX0.png");
Bitmap bmp = BarrelDistortion(source, 4/10f, true);
bmp.Save(#"test.png");
bmp.Dispose();
source.Dispose();
}
static public Bitmap BarrelDistortion(Bitmap sourceImage, double factor = 0, bool autoCrop = true, uint previewRectangleWidth = 0, Color? fillerColor = null)
{
int sourceRight = sourceImage.Width - 1, sourceBottom = sourceImage.Height - 1;
// Vertical amplitude is half the height times factor
// Horizontal amplitude is missing ; vertical amplitude's applied to both directions
double amp = sourceBottom / 2f * factor;
// Inner shrinking area points
RePoint[] lPts;
bool inverse = factor < 0;
// Shrinking area coordinates (center point is considered always available)
double x1 = sourceRight / 2f,
y1 = sourceBottom / 2f,
x2 = sourceRight / 2f,
y2 = sourceBottom / 2f;
if (inverse)
{
lPts = new RePoint[]
{
new RePoint(0, 0),
new RePoint(0, sourceBottom),
new RePoint(sourceRight, sourceBottom),
new RePoint(sourceRight, 0)
};
}
else
{
lPts = new RePoint[]
{
new RePoint(sourceRight * 1 / 2f, 0),
new RePoint(0, sourceBottom * 1 / 2f),
new RePoint(sourceRight, sourceBottom * 1 / 2f),
new RePoint(sourceRight * 1 / 2f, sourceBottom)
};
}
foreach (var pN in lPts.Select(pt => NewPoint(pt, sourceImage.Width, sourceImage.Height, amp, inverse)))
{
if (pN.Y < y1) y1 = pN.Y;
if (pN.Y > y2) y2 = pN.Y;
if (pN.X < x1) x1 = pN.X;
if (pN.X > x2) x2 = pN.X;
}
// Bytes per color from bit per pixel (bpp) format
int bpcCount = Image.GetPixelFormatSize(sourceImage.PixelFormat) / 8;
Rectangle sourceRectangle = new Rectangle(0, 0, sourceImage.Width, sourceImage.Height);
int srcLength = sourceImage.Width * sourceImage.Height * bpcCount;
// Gets sourceImage byte array as srcpixels
BitmapData srcBitmapData = sourceImage.LockBits(sourceRectangle, ImageLockMode.ReadOnly, sourceImage.PixelFormat);
byte[] srcPixels = new byte[srcLength];
Marshal.Copy(srcBitmapData.Scan0, srcPixels, 0, srcLength);
sourceImage.UnlockBits(srcBitmapData);
srcBitmapData = null;
// Destination byte array preparation as dstPixels
byte[] dstPixels = new byte[srcLength];
int dstIndex = 0;
// Filler color preparation
Color fillColor = fillerColor ?? Color.Transparent;
if (!autoCrop)
{
if (bpcCount <= 4) // Depth > 32bpp may not work as expected, filler color's not applied for bit safety reason
do
{
dstPixels[dstIndex++] = fillColor.B;
if (bpcCount > 1)
{
dstPixels[dstIndex++] = fillColor.G;
dstPixels[dstIndex++] = fillColor.R;
if (bpcCount > 3)
dstPixels[dstIndex++] = fillColor.A; // a
}
} while (dstIndex < srcLength);
}
// Byte-to-byte copy (incl. Point transformation)
int index = 0, srcBpcLength = srcLength - bpcCount;
do
{
int comp = index / bpcCount; // comp yields the current "pixel" position
int y = comp / sourceImage.Width; // Each line is sourceImage.Width bytes wide
int x = comp - (y * sourceImage.Width); // Remaining (comp - lines) bytes is target column (ranges from 0 to width - 1)
// Destination "pixel"
RePoint pt = NewPoint(new RePoint(x, y), sourceImage.Width, sourceImage.Height, amp, inverse);
dstIndex = (((int)pt.Y * sourceImage.Width) + (int)pt.X) * bpcCount; // dstIndex++ overflows when |amp| >= 2
if (dstIndex >= 0 && dstIndex <= srcBpcLength)
for (int i = 0; i++ < bpcCount;)
dstPixels[dstIndex++] = srcPixels[index++];
else
index += bpcCount;
} while (index < srcLength);
srcPixels = null;
// Destination bytes application
BitmapData dstBitmapData = sourceImage.LockBits(sourceRectangle, ImageLockMode.WriteOnly, sourceImage.PixelFormat);
Marshal.Copy(dstPixels, 0, dstBitmapData.Scan0, srcLength);
sourceImage.UnlockBits(dstBitmapData);
dstBitmapData = null;
dstPixels = null;
// Final Image area
Rectangle cropRect = new Rectangle((int)Ceiling(x1), (int)Ceiling(y1), (int)Ceiling(x2 - x1), (int)Ceiling(y2 - y1));
Rectangle destRectangle = autoCrop ? cropRect : sourceRectangle;
// Final image preparation
Bitmap FinalImage = new Bitmap(destRectangle.Width, destRectangle.Height, sourceImage.PixelFormat);
FinalImage.SetResolution(sourceImage.HorizontalResolution, sourceImage.VerticalResolution);
Graphics g1 = Graphics.FromImage(FinalImage);
g1.DrawImage(sourceImage, -destRectangle.X, -destRectangle.Y);
// Previsualization rectangle
if (previewRectangleWidth > 0)
g1.DrawRectangle(new Pen(Color.Red, previewRectangleWidth), cropRect.X - 1, cropRect.Y - 1, cropRect.Width + previewRectangleWidth, cropRect.Height + previewRectangleWidth);
g1.Dispose();
g1 = null;
return FinalImage;
}
private static RePoint NewPoint(RePoint aP, double Width, double Height, double Amplitude, bool inverse)
{
double h = aP.Y / (Height - 1);
double w = aP.X / (Width - 1);
// Works ok for [0/2] to [1/2]
// Floating point error(s) here, in the range of ]1/2] to [2/2] (No workaround found)
double sinX = Round(Sin(PI * w), 15); // Range of [0] to [1] * PI ; result ranges from 0 (far from center) to 1 (at center)
double sinY = Round(Sin(PI * h), 15);
double caX = Amplitude * (1 - 2 * w);
double caY = Amplitude * (1 - 2 * h);
double aY = 0, aX = 0;
if (inverse)
{
aX = -caX;
aY = -caY;
}
double pY = aP.Y + aY + caY * sinX;
double pX = aP.X + aX + caX * sinY;
return new RePoint(pX, pY);
}
private struct RePoint
{
public double X;
public double Y;
public RePoint(double x, double y)
{
X = x;
Y = y;
}
}
}
}

print two images on single page c#

I want to print two images on single page.
I have tried below code, but it is printing all images on different pages.
public void PD_PrintPage(object sender, PrintPageEventArgs e)
{
float W = e.MarginBounds.Width;
float H = e.MarginBounds.Height;
for (; FileCounter >= 0; FileCounter--)
{
try
{
Bitmap Bmp = new Bitmap(BmpFiles[FileCounter]);
if (Bmp.Width / W < Bmp.Height / H)
W = Bmp.Width * H / Bmp.Height;
else
H = Bmp.Height * W / Bmp.Width;
e.Graphics.DrawImage(Bmp, 0, 0, W, H);
break;
}
catch
{
}
}
FileCounter -= 1;
if (FileCounter > 0)
{
e.HasMorePages = true;
}
else
{
FileCounter = BmpFiles.Length - 1;
}
}
this will print all images in different page
I want some functionality that will print one image ,leave some space and again prine other image in same page if space is remaining.
In your code you're printig just one image per page because you leave the loop with the break-statement at the end of try. Instead of using break without a condition you should leave the loop dynamically based on the decision if it is possible to print only one image (not nough space for second image) ore two images (you achieved what you wanted).
//for-loop for printing maximum two images as long as there are files to print
for (int i = 0; i < 2 && FileCounter >= 0; i++)
{
//here comes your printing code just indicated with the draw-call
e.Graphics.DrawImage(Bmp, 0, 0, W, H);
//after a image was printed decrement your filecounter
FileCounter --;
//after a image was drawn check if there is enough space for the next image
//if there is not enough space leave the loop with break
if(condition)
break;
}
At the moment I don't have enough reputation for commenting something on this page... so: never use 'goto' as "Sayka" proposes in his answer. That is really bad style & coding
The working of printDocument is like this:
First the program reaches the printPage function, reads all the code and at the end of the function, if there exists a line e.hasMorePages = true; , then the program re-enters the function from the begining and read again the codes to print it to the next page and continues until it reads a line e.hasMorepages = false .. So u dont have to put a loop inside the function. What you have to do is to make variables inside the class, and incre or decrement the variables to make a condition that satisfies e.hasMorePages = false after your printing job has finished..
public void PD_PrintPage(object sender, PrintPageEventArgs e)
{
float W = e.MarginBounds.Width;
// if you are calculating the whole page margin, then split it to carry 2 images
float H = e.MarginBounds.Height / 2;
// for space btwn images
H -= 5.0;
// First Image
try
{
Bitmap Bmp = new Bitmap(BmpFiles[FileCounter]);
if (Bmp.Width / W < Bmp.Height / H)
W = Bmp.Width * H / Bmp.Height;
else
H = Bmp.Height * W / Bmp.Width;
e.Graphics.DrawImage(Bmp, 0, 0, W, H);
break;
}
catch
{
}
FileCounter --;
if (FileCounter < 0) goto goDaddy;
//Second Img
try
{
Bitmap Bmp = new Bitmap(BmpFiles[FileCounter]);
if (Bmp.Width / W < Bmp.Height / H)
W = Bmp.Width * H / Bmp.Height;
else
H = Bmp.Height * W / Bmp.Width;
e.Graphics.DrawImage(Bmp, 0, H + 2.5, W, H);
break;
}
catch
{
}
FileCounter --;
goDaddy:;
e.HasMorePages = (FileCounter >= 0)
}
I haven't checked the code but just tryin to show you the concept..
I found this to work extremely well, doesn't lose any image quality I experienced using memory streams.
private void printBothGraphs_Click(object sender, EventArgs e)
{
PrintPreviewDialog custom = new PrintPreviewDialog();
custom.ClientSize = new Size(1000, 750);
System.Drawing.Printing.PrintDocument pd = new System.Drawing.Printing.PrintDocument();
pd.DefaultPageSettings.Color = true;
pd.PrintPage += new PrintPageEventHandler(pd_PrintPageBoth);
custom.Document = pd;
custom.ShowDialog();
}
private void pd_PrintPageBoth(object sender, PrintPageEventArgs ev)
{
// Create and initialize print font
System.Drawing.Font printFont = new System.Drawing.Font("Arial", 10);
ev.PageSettings.Color = true;
// Create Rectangle structure, used to set the position of the chart Rectangle
Rectangle myRec = new System.Drawing.Rectangle(ev.MarginBounds.X, ev.MarginBounds.Y, ev.MarginBounds.Width, ev.MarginBounds.Height / 2);
Rectangle myRec2 = new System.Drawing.Rectangle(ev.MarginBounds.X, ev.MarginBounds.Height / 2 + 90, ev.MarginBounds.Width, ev.MarginBounds.Height / 2);
//dataChart and outputGraph are two mscharts in my form
dataChart.Printing.PrintPaint(ev.Graphics, myRec);
outputGraph.Printing.PrintPaint(ev.Graphics, myRec2);
}

Calculating image sizes

Basically, I have an image which could be large (couple of thousand pixels in both height and width), and can vary quite a bit in width's and height's.
What I need to do is display these images at approximately 500 pixels in height and width - but I want to keep the aspect ratio of these intact, so the values would just be fairly close. But I'm a bit confused on what calculations I could use on the original width's and heights to get them to the right figures.
Any ideas?
Try this:
Size original = new Size(5000,4000);
double ratio = (double)original.Height/original.Width;
int resizePercent = 50;
int newWidth = Convert.ToInt32(original.Width*resizePercent/100);
int newHeight = Convert.ToInt32(newWidth * ratio);
Size resized = new Size(newWidth,newHeight);
Of course simply set original variable to your images size and determine your resize percentage. You could calculate the resize percentage if you had a target width like this:
int targetWidth = 500;
int resizePercent = Convert.ToInt32((double)original.Width/targetWidth);
I ran into this problem a couple weeks ago. Check out this article https://web.archive.org/web/20200508200042/http://www.4guysfromrolla.com:80/articles/012203-1.2.aspx
You create a Generic Handler (.ashx) that finds the image, resizes it, and returns it
Here is what I ended up using (you can resize on the fly):
public void ProcessRequest (HttpContext context) {
TimeSpan refresh = new TimeSpan(168, 0, 0);
HttpContext.Current.Response.Cache.SetExpires(DateTime.Now.Add(refresh));
HttpContext.Current.Response.Cache.SetMaxAge(refresh);
HttpContext.Current.Response.Cache.SetCacheability(HttpCacheability.Public);
HttpContext.Current.Response.Cache.SetValidUntilExpires(true);
string dir = HttpContext.Current.Request.QueryString["dir"];
string img = HttpContext.Current.Request.QueryString["img"];
bool force = HttpContext.Current.Request.QueryString["force"] == "yes";
double w = 0;
double h = 0;
string path = null;
Image orig = null;
Image thumb = null;
//make sure we have a directory and image filename
if (string.IsNullOrEmpty(dir))
return;
//make sure that we allow access to that directory, could do some extra sneaky security stuff here if we had to... (only accept a DirectoryID so the user doesn't know the actual path)
switch (dir)
{
case "user":
dir = "assets/images/users";
if (string.IsNullOrEmpty(img) || img == "undefined") img = "0.jpg";
break;
break;
case "icon":
dir = "assets/images/icons";
break;
default:
dir = "assets/images";
break;
}
//make sure that the image filename is just a filename
if (img.IndexOf("/") > -1 | img.IndexOf("\\") > -1)
return;
//make sure the image exists
path = HttpContext.Current.Server.MapPath("~/" + dir + "/" + img);
if (System.IO.File.Exists(path))
{
orig = Image.FromFile(path);
}
else
{
return;
}
//if there is a max width or height
if (double.TryParse(HttpContext.Current.Request.QueryString["w"], out w) | double.TryParse(HttpContext.Current.Request.QueryString["h"], out h))
{
//thumb is the resized image
double s = 1;
if (w > 0 & h > 0)
{
double ratio = h / w;
if (orig.Height / (double)orig.Width > ratio)
{
if (orig.Height > h)
{
if (force)
s = w / (double)orig.Width;
else
s = h / (double)orig.Height;
}
}
else
{
if (orig.Width > w)
{
if (force)
s = h / (double)orig.Height;
else
s = w / (double)orig.Width;
}
}
}
else if (w > 0)
{
if (orig.Width > w)
{
s = w / (double)orig.Width;
}
}
else if (h > 0)
{
if (orig.Height > h)
{
s = h / (double)orig.Height;
}
}
//double flip gets it to lose the embedded thumbnail
//https://web.archive.org/web/20200508200042/http://www.4guysfromrolla.com:80/articles/012203-1.2.aspx
orig.RotateFlip(RotateFlipType.Rotate180FlipNone);
orig.RotateFlip(RotateFlipType.Rotate180FlipNone);
thumb = orig.GetThumbnailImage(Convert.ToInt32(orig.Width * s), Convert.ToInt32(orig.Height * s), null, IntPtr.Zero);
if (force && w > 0 & h > 0)
thumb = cropImage(thumb, (int)w, (int)h);
}
else
{
//thumb is the image at it's original size
thumb = orig;
}
HttpContext.Current.Response.ContentType = "image/jpeg";
thumb.Save(HttpContext.Current.Response.OutputStream, System.Drawing.Imaging.ImageFormat.Jpeg);
}
private Image cropImage(Image img, int w, int h)
{
if (w > img.Width && h > img.Height)
return img;
Rectangle cropArea = new Rectangle((img.Width - w) / 2, (img.Height - h) / 2, w, h);
Bitmap bmpImage = new Bitmap(img);
Bitmap bmpCrop = bmpImage.Clone(cropArea, bmpImage.PixelFormat);
return (Image)(bmpCrop);
}
public bool IsReusable {
get {
return false;
}
}

Categories