I know there's a lot of Fibonacci questions and answers on Stack overflow and the web in general, but this is a problem that's been vexing me for a while now, and i can't seem to crack it or find a solution.
Creating Fibonacci algorithms are easy enough, there's plenty of them, but i'm trying to create the boxes in a spiral formation graphically using C#. This is not for Uni or anything, it's just a problem that i've spent way too much time on that i now need to find a solution for, if you know what i mean?
Here's what i've got so far, now i did have a better configuration, but with countless hours of revising the code, this is what i have at the moment:
public partial class Form1 : Form
{
public const int FIBNUM = 6;
public const int CENTRE = 10;
public const int SIZE = 10;
public const int OFFSET = 100;
public Form1()
{
InitializeComponent();
drawSpiral();
}
private int fib(int n)
{
switch (n)
{
case 0:
return 0;
case 1:
return 1;
default:
return fib(n - 1) + fib(n - 2);
}
}
private void drawSpiral()
{
if (pictureBox1.Image == null)
{
pictureBox1.Image = new Bitmap(pictureBox1.Width, pictureBox1.Height);
}
using (Graphics g = Graphics.FromImage(pictureBox1.Image))
{
Rectangle r = new Rectangle(0, 0, 0, 0);
int fibnum = 0;
int centre = 0;
int size = 0;
int cnt = 0;
for (int n = 1; n <= FIBNUM; n++)
{
fibnum = fib(n);
centre = fibnum * CENTRE;
size = fibnum * SIZE;
++cnt;
if (cnt == 1)
{
if (n == 1)
{
r = new Rectangle(fibnum + OFFSET, fibnum + OFFSET, size, size);
g.DrawRectangle(Pens.Red, r);
r = new Rectangle((fibnum + size) + OFFSET, fibnum + OFFSET, size, size);
g.DrawRectangle(Pens.Purple, r);
n++;
}
else
{
r = new Rectangle((centre - size) + OFFSET, (centre - size) + OFFSET, size, size);
g.DrawRectangle(Pens.Black, r);
}
continue;
}
if(cnt == 2)
{
r = new Rectangle((fibnum) + OFFSET, (fibnum - size) + OFFSET, size, size);
g.DrawRectangle(Pens.Blue, r);
continue;
}
if (cnt == 3)
{
r = new Rectangle((fibnum - size) + OFFSET, (fibnum - size) + OFFSET, size, size);
g.DrawRectangle(Pens.Green, r);
continue;
}
if (cnt == 4)
{
r = new Rectangle((fibnum - size / 2) + OFFSET, (fibnum - size) + OFFSET, size, size);
g.DrawRectangle(Pens.Gray, r);
}
cnt = 0;
}
}
pictureBox1.Invalidate();
}
I've taken an image from wikipedia of what it is i'm trying to create graphically :
Thanks in advance.
I think your code is too complicated, because you try to do a lot at once. Consider the following code where the spiral is drawn around the origin, without scaling and translating:
// the current fibonacci numbers
int current = 1;
int previous = 0;
// the current bounding box
int left = 0;
int right = 1;
int top = 0;
int bottom = 0;
// the number of boxes you want to draw
const int N = 10;
for (int i = 0; i < N; i++) {
switch (i % 4) {
case 0: // attach to bottom of current rectangle
drawRectangle(g, left, right, bottom, bottom + current);
bottom += current;
break;
case 1: // attach to right of current rectangle
drawRectangle(g, right, right + current, top, bottom);
right += current;
break;
case 2: // attach to top of current rectangle
drawRectangle(g, left, right, top - current, top);
top -= current;
break;
case 3: // attach to left of current rectangle
drawRectangle(g, left - current, left, top, bottom);
left -= current;
break;
}
// update fibonacci number
int temp = current;
current += previous;
previous = temp;
}
You can then deal with the actual drawing part in the separate method drawRectangle (I left out all details about the actual graphics objects, but you can probably do that yourself).
const int SCALE = 5;
const int OFFSET = 150;
private void drawRectangle(Graphics g, int left, int right, int top, int bottom)
{
g.DrawRectangle(Pens.Red, new Rectangle(SCALE * left + OFFSET,
SCALE * top + OFFSET,
SCALE * (right - left),
SCALE * (bottom - top)));
}
Output:
Here you go :
public partial class Form1 : Form
{
public const int FIBNUM = 8;
public const int CENTERX = 300;
public const int CENTERY = 300;
public const int ZOOM = 10;
public Form1()
{
InitializeComponent();
drawSpiral();
}
private int fib(int n, int p = 0, int q = 1)
{
switch (n)
{
case 0: return 0;
case 1: return q;
default: return fib(n - 1, q, p + q);
}
}
private void drawSpiral()
{
if (pictureBox1.Image == null)
{
pictureBox1.Image = new Bitmap(pictureBox1.Width, pictureBox1.Height);
}
using (Graphics g = Graphics.FromImage(pictureBox1.Image))
{
Rectangle r = new Rectangle(0, 0, 0, 0);
int x = CENTERX;
int y = CENTERY;
for (int n = 1; n <= FIBNUM; n++)
{
int fibnum = fib(n)*ZOOM;
r = new Rectangle(x, y, fibnum, fibnum);
g.DrawRectangle(Pens.Red, r);
switch (n % 4)
{
case 0:
{
y += fibnum;
break;
}
case 1:
{
x += fibnum;
y -= fib(n - 1) * ZOOM;
break;
}
case 2:
{
x -= fib(n - 1)*ZOOM;
y -= fib(n + 1)*ZOOM;
break;
}
case 3:
{
x -= fib(n + 1) * ZOOM;
break;
}
}
}
pictureBox1.Invalidate();
}
}
}
Note that i have changed your fibonacci function with a better performing one ; in particular, mine calculates the next fibonacci number in linear time.
With some MatheMagic ( sorry for the joke :) ) you can make it even smarter, and obtain
private int move(int n, int a, int currentFib)
{
switch (a)
{
case 1: return currentFib;
case 2: return -fib(n - 1) * ZOOM;
case 3: return -fib(n + 1) * ZOOM;
default: return 0;
}
}
private void drawSpiral()
{
if (pictureBox1.Image == null)
{
pictureBox1.Image = new Bitmap(pictureBox1.Width, pictureBox1.Height);
}
using (Graphics g = Graphics.FromImage(pictureBox1.Image))
{
Rectangle r = new Rectangle(0, 0, 0, 0);
int x = CENTERX;
int y = CENTERY;
for (int n = 1; n <= FIBNUM; n++)
{
int fibnum = fib(n)*ZOOM;
r = new Rectangle(x, y, fibnum, fibnum);
g.DrawRectangle(Pens.Red, r);
x += move(n, n % 4, fibnum);
y += move(n, (n + 1) % 4, fibnum);
}
pictureBox1.Invalidate();
}
}
private int fib(int n)
{
switch (n)
{
case 0:
return 0;
case 1:
return 1;
default:
return fib(n - 1) + fib(n - 2);
}
}
That is enough slow,maybe it doesn't matter in this task but isn't it better to use an array fib[1..n] (fib[0]=0 fib[1]=1 for(i = 2;i<=n;i++)fib[i]=fib[i-1]+fib[i-2]) that will work in O(n) not in O(n*(1.7^n))
Related
Im doing school project. My task is to write a small Winform application that represents the Bezier Curve, but with some constraints.
I did almost everything, just one more step is ahead of me.
The whole program starts with an empty canvas, then the user can click on it, and a circle is drawn. After every 4th click, the bezier curve appears to that polygon. Now comes my problem.
What I am stuck with is that I have to controll somehow where the 5th click is going to be. It must be on a line that comes from 2 points: the 3rd and 4th points.
Can anybody help me with this? I have really no idea how to even start.
So far, this is my code.
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace grafika_beadando_kettesert
{
public partial class MainForm : Form
{
Graphics g;
int counter = 0;
Pen PenBlack = Pens.Black; //ezzel a tollal rajzolom a vonalat
Pen PenCurve = new Pen(Color.Blue, 3f); //ezzel a tollal rajzolom a görbét
Brush PenPoint; //Ezzel töltöm ki a pontot
int size = 4; // a lerakott pont mérete
int found = -1;
List<PointF> Points = new List<PointF>(); //ebbe a listába tárolom a pontokat
PointF p0, p1;
public MainForm()
{
InitializeComponent();
PenPoint = new SolidBrush(canvas.BackColor);
this.DoubleBuffered = true;
}
private void canvas_Paint(object sender, PaintEventArgs e)
{
g = e.Graphics;
g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
for (int i = 0; i < Points.Count - 1; i++) // mindig meg kell rajzolni az eddig meghúzott vonalakat a polygonból újra
g.DrawLine(PenBlack, Points[i], Points[i + 1]);
if (counter == 4)
{
DrawBeziergorbe();
counter = 0;
}
for (int i = 0; i < Points.Count; i++) // ezzel rajzolom meg az eddig felrakott pontokat újra
{
g.FillEllipse(PenPoint, Points[i].X - size, Points[i].Y - size, 2 * size, 2 * size);
g.DrawEllipse(PenBlack, Points[i].X - size, Points[i].Y - size, 2 * size, 2 * size);
}
}
private void canvas_MouseUp(object sender, MouseEventArgs e)
{
found = -1;
}
private void canvas_MouseMove(object sender, MouseEventArgs e)
{
if (found != -1)
{
Points[found] = e.Location;
canvas.Invalidate();
}
}
private void canvas_MouseDown(object sender, MouseEventArgs e)
{
for (int i = 0; i < Points.Count; i++)
{
if (Math.Abs(Points[i].X - e.X) <= size && Math.Abs(Points[i].Y - e.Y) <= size)
{
found = i;
break;
}
}
if (found == -1)
{
Points.Add(e.Location); //ha nincs túl közel a lerakott pont egy jelenlegihez, akkor hozzáadja a
//"Points" listához, hogy innen kiolvasva újra belehessen rajzolni
found = Points.Count - 1;
counter++;
canvas.Invalidate();
}
}
private void DrawBeziergorbe() //Mivel n-ed fokú bezier görbe kell, ezért használom a binomiálisos megoldást
{
int n = Points.Count - 1;
double t = 0;
double h = 1.0 / 500.0;
double b = 0.0;
p0 = new PointF(0, 0);
for (int i = 0; i <= n; i++)
{
b = B(n, i, t);
p0.X += (float)(b * Points[i].X);
p0.Y += (float)(b * Points[i].Y);
}
while (t < 1)
{
t += h;
p1 = new PointF(0, 0);
for (int i = 0; i <= n; i++)
{
b = B(n, i, t);
p1.X += (float)(b * Points[i].X);
p1.Y += (float)(b * Points[i].Y);
}
g.DrawLine(PenCurve, p0, p1);
p0 = p1;
}
}
private double B(int n, int i, double t)
{
return Binom(n, i) * (Math.Pow(1 - t, n - i) * Math.Pow(t, i));
}
private uint Binom(int n, int k)
{
if (n == 0) return 0;
else if (k == 0 || k == n) return 1;
else return Binom(n - 1, k - 1) + Binom(n - 1, k);
}
}
}
You can simply project the click position on the desired line.
If c is the click position and A and B are the two last control points, then the projected position p is:
d = B - A
p = A + dot(c - A, d) / dot(d, d) * d
I am working on my Project in Windows Forms and I have a Problem. In this Project I have to Compare Screenshot with Images from File. I have good Method for Comparison in Internet found and it seems to be working. For example: I have cuted Facebook Logo from site and saved it on Desktop. When I am comparing this Logo with itself(I am making a screenshot of a logo and than compare this screenshot with Logo) this method works correctly(it says that screenshot contains Logo) but when I am making a Screenshot on it's Site and than compare it with Logo, This Method says that Screenshot donn't contains Logo.
I am using this Compare Method:
public static Rectangle searchBitmap(Bitmap smallBmp, Bitmap bigBmp, double tolerance)
{
BitmapData smallData =
smallBmp.LockBits(new Rectangle(0, 0, smallBmp.Width, smallBmp.Height),
System.Drawing.Imaging.ImageLockMode.ReadOnly,
System.Drawing.Imaging.PixelFormat.Format24bppRgb);
BitmapData bigData =
bigBmp.LockBits(new Rectangle(0, 0, bigBmp.Width, bigBmp.Height),
System.Drawing.Imaging.ImageLockMode.ReadOnly,
System.Drawing.Imaging.PixelFormat.Format24bppRgb);
int smallStride = smallData.Stride;
int bigStride = bigData.Stride;
int bigWidth = bigBmp.Width;
int bigHeight = bigBmp.Height - smallBmp.Height + 1;
int smallWidth = smallBmp.Width * 3;
int smallHeight = smallBmp.Height;
Rectangle location = Rectangle.Empty;
int margin = Convert.ToInt32(255.0 * tolerance);
unsafe
{
byte* pSmall = (byte*)(void*)smallData.Scan0;
byte* pBig = (byte*)(void*)bigData.Scan0;
int smallOffset = smallStride - smallBmp.Width * 3;
int bigOffset = bigStride - bigBmp.Width * 3;
bool matchFound = true;
for (int y = 0; y < bigHeight; y++)
{
for (int x = 0; x < bigWidth; x++)
{
byte* pBigBackup = pBig;
byte* pSmallBackup = pSmall;
//Look for the small picture.
for (int i = 0; i < smallHeight; i++)
{
int j = 0;
matchFound = true;
for (j = 0; j < smallWidth; j++)
{
//With tolerance: pSmall value should be between margins.
int inf = pBig[0] - margin;
int sup = pBig[0] + margin;
if (sup < pSmall[0] || inf > pSmall[0])
{
matchFound = false;
break;
}
pBig++;
pSmall++;
}
if (!matchFound) break;
//We restore the pointers.
pSmall = pSmallBackup;
pBig = pBigBackup;
//Next rows of the small and big pictures.
pSmall += smallStride * (1 + i);
pBig += bigStride * (1 + i);
}
//If match found, we return.
if (matchFound)
{
location.X = x;
location.Y = y;
location.Width = smallBmp.Width;
location.Height = smallBmp.Height;
break;
}
//If no match found, we restore the pointers and continue.
else
{
pBig = pBigBackup;
pSmall = pSmallBackup;
pBig += 3;
}
}
if (matchFound) break;
pBig += bigOffset;
}
}
bigBmp.UnlockBits(bigData);
smallBmp.UnlockBits(smallData);
return location;
}
This method returns a Rectangle "location". If (location.Width == 0 || location.height == 0), it means that Screenshot doesn't contain Image.
What's the problem?
If the screenshot contains any blank space around the border, it will most likely not match with the image (unless the screenshot is the whole screen).
I can't get RGB values from surfaceView. I'm working with xamarin, C#.
tried to use getBitmap, but this method not for SurfaceView..
I need to get rgb from camera live stream by touching some place at surfaceView.
Maybe i need to use something else instead of surfaceView?
`
public class MainActivity : Activity, ISurfaceHolderCallback, Camera.IPreviewCallback, View.IOnTouchListener
{
Camera _camera;
SurfaceView _surfaceview;
int redValue,blueValue,greenValue;
int pixel;
Bitmap bitmap;
TextView _textview;
protected override void OnCreate (Bundle savedInstanceState)
{
base.OnCreate (savedInstanceState);
SetContentView (Resource.Layout.Main);
_surfaceview = (SurfaceView)FindViewById (Resource.Id.surfaceView1);
_surfaceview.SetOnTouchListener (this);
var holder = _surfaceview.Holder;
holder.AddCallback (this);
holder.SetType (Android.Views.SurfaceType.PushBuffers);
TabHost tabhost = FindViewById<TabHost> (Resource.Id.myTab);
tabhost.Setup ();
TabHost.TabSpec tabhost1 = tabhost.NewTabSpec ("Tab1");
tabhost1.SetContent (Resource.Id.tab1);
tabhost1.SetIndicator ("CAMERA");
tabhost.AddTab (tabhost1);
TabHost.TabSpec tabhost2 = tabhost.NewTabSpec ("Tab2");
tabhost2.SetContent (Resource.Id.tab2);
tabhost2.SetIndicator ("RGB");
tabhost.AddTab (tabhost2);
_textview = (TextView)FindViewById (Resource.Id.textView1);
}
public bool OnTouch(View v, MotionEvent e)
{
switch (e.Action)
{
case MotionEventActions.Down:
break;
case MotionEventActions.Up:
pixel = bitmap.GetPixel ((int)e.GetX (), (int)e.GetY ());
var _color = new Color (pixel);
redValue = _color.R;
blueValue = _color.G;
greenValue = _color.B;
_textview.Text = "Hello";
break;
}
return true;
}
public void SurfaceCreated(ISurfaceHolder holder)
{
try{
_camera = Android.Hardware.Camera.Open();
Android.Hardware.Camera.Parameters p = _camera.GetParameters();
p.PictureFormat = Android.Graphics.ImageFormatType.Jpeg;
_camera.SetParameters(p);
_camera.SetPreviewCallback(this);
_camera.Lock();
_camera.SetDisplayOrientation(90);
_camera.SetPreviewDisplay(holder);
_camera.StartPreview();
}
catch(System.IO.IOException e){
}
}
public void SurfaceDestroyed(ISurfaceHolder holder){
_camera.Unlock ();
_camera.StopPreview ();
_camera.Release ();
}
public void SurfaceChanged(ISurfaceHolder holder,Android.Graphics.Format f,int i, int j)
{
}
void Camera.IPreviewCallback.OnPreviewFrame(byte[] b, Android.Hardware.Camera c)
{
}
}}
`
in PreviewCallBack:
Camera.Parameters parameters = camera.getParameters();
Camera.Size size = parameters.getPreviewSize();
Camera.CameraInfo info = new Camera.CameraInfo();
Camera.getCameraInfo(0, info);
if(mBitmapWidth == 0 || mBitmapHeight == 0) {
mBitmapWidth = size.width;
mBitmapHeight = size.height;
}
mCurrentImageRGB = new int[mBitmapWidth*mBitmapHeight];
Recognize.decodeYUV420SP2(mCurrentImageRGB, data, mBitmapWidth, mBitmapHeight);
in CameraPreview:
mCamera.getParameters().setPreviewFormat(ImageFormat.NV21);
in surfaceChanged:
setCameraDisplayOrientation((Activity)mContext,0,mCamera);
and setCameraDisplayOrientation (this method i added in CameraPreview class):
public static void setCameraDisplayOrientation(Activity activity, int cameraId, android.hardware.Camera camera) {
android.hardware.Camera.CameraInfo info = new android.hardware.Camera.CameraInfo();
android.hardware.Camera.getCameraInfo(cameraId, info);
int rotation = activity.getWindowManager().getDefaultDisplay().getRotation();
int degrees = 0;
switch (rotation) {
case Surface.ROTATION_0:
degrees = 0;
break;
case Surface.ROTATION_90:
degrees = 90;
break;
case Surface.ROTATION_180:
degrees = 180;
break;
case Surface.ROTATION_270:
degrees = 270;
break;
}
int result;
if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
result = (info.orientation + degrees) % 360;
result = (360 - result) % 360;
} else {
result = (info.orientation - degrees + 360) % 360;
}
camera.setDisplayOrientation(result);
}
hope this will help )
in mCurrentImageRGB will be your int array with RGB integers for this image, you can them do with it whatever you want )
int middlePixel = mCurrentImageRGB[mBitmapWidth/2 + mBitmapHeight/2]; // this is your rgb pixel somewhere in center of image :)
I forget, decoder:
public static void decodeYUV420SP2(int[] rgb, byte[] yuv420sp, int width, int height) {
final int frameSize = width * height;
for (int j = 0, yp = 0; j < height; j++) {
int uvp = frameSize + (j >> 1) * width, u = 0, v = 0;
for (int i = 0; i < width; i++, yp++) {
int y = (0xff & ((int) yuv420sp[yp])) - 16;
if (y < 0)
y = 0;
if ((i & 1) == 0) {
v = (0xff & yuv420sp[uvp++]) - 128;
u = (0xff & yuv420sp[uvp++]) - 128;
}
int y1192 = 1192 * y;
int r = (y1192 + 1634 * v);
int g = (y1192 - 833 * v - 400 * u);
int b = (y1192 + 2066 * u);
if (r < 0)
r = 0;
else if (r > 262143)
r = 262143;
if (g < 0)
g = 0;
else if (g > 262143)
g = 262143;
if (b < 0)
b = 0;
else if (b > 262143)
b = 262143;
rgb[yp] = 0xff000000 | ((r << 6) & 0xff0000) | ((g >> 2) & 0xff00) | ((b >> 10) & 0xff);
}
}
}
I'm working on a screen sharing app, which runs a loop and grab fast screenshots using GDI methods . example here
Of course I also use a flood fill algorithm to find the changes areas between 2 images (previous screenshot and current).
I use another small trick - I downscale the snapshot resolution in 10, because processing 1920*1080=2073600 pixels very constantly is not very efficient.
However when I find the rectangle bounds - I apply it on the original full size bitmap and I just multiply by 10 the dimension (including top, left, width, height).
This is the scanning code:
unsafe bool ArePixelsEqual(byte* p1, byte* p2, int bytesPerPixel)
{
for (int i = 0; i < bytesPerPixel; ++i)
if (p1[i] != p2[i])
return false;
return true;
}
private unsafe List<Rectangle> CodeImage(Bitmap bmp, Bitmap bmp2)
{
List<Rectangle> rec = new List<Rectangle>();
var bmData1 = bmp.LockBits(new System.Drawing.Rectangle(0, 0, bmp.Width, bmp.Height), System.Drawing.Imaging.ImageLockMode.ReadOnly, bmp.PixelFormat);
var bmData2 = bmp2.LockBits(new System.Drawing.Rectangle(0, 0, bmp.Width, bmp.Height), System.Drawing.Imaging.ImageLockMode.ReadOnly, bmp2.PixelFormat);
int bytesPerPixel = 4;
IntPtr scan01 = bmData1.Scan0;
IntPtr scan02 = bmData2.Scan0;
int stride1 = bmData1.Stride;
int stride2 = bmData2.Stride;
int nWidth = bmp.Width;
int nHeight = bmp.Height;
bool[] visited = new bool[nWidth * nHeight];
byte* base1 = (byte*)scan01.ToPointer();
byte* base2 = (byte*)scan02.ToPointer();
for (int y = 0; y < nHeight; y ++)
{
byte* p1 = base1;
byte* p2 = base2;
for (int x = 0; x < nWidth; ++x)
{
if (!ArePixelsEqual(p1, p2, bytesPerPixel) && !(visited[x + nWidth * y]))
{
// fill the different area
int minX = x;
int maxX = x;
int minY = y;
int maxY = y;
var pt = new Point(x, y);
Stack<Point> toBeProcessed = new Stack<Point>();
visited[x + nWidth * y] = true;
toBeProcessed.Push(pt);
while (toBeProcessed.Count > 0)
{
var process = toBeProcessed.Pop();
var ptr1 = (byte*)scan01.ToPointer() + process.Y * stride1 + process.X * bytesPerPixel;
var ptr2 = (byte*)scan02.ToPointer() + process.Y * stride2 + process.X * bytesPerPixel;
//Check pixel equality
if (ArePixelsEqual(ptr1, ptr2, bytesPerPixel))
continue;
//This pixel is different
//Update the rectangle
if (process.X < minX) minX = process.X;
if (process.X > maxX) maxX = process.X;
if (process.Y < minY) minY = process.Y;
if (process.Y > maxY) maxY = process.Y;
Point n; int idx;
//Put neighbors in stack
if (process.X - 1 >= 0)
{
n = new Point(process.X - 1, process.Y); idx = n.X + nWidth * n.Y;
if (!visited[idx]) { visited[idx] = true; toBeProcessed.Push(n); }
}
if (process.X + 1 < nWidth)
{
n = new Point(process.X + 1, process.Y); idx = n.X + nWidth * n.Y;
if (!visited[idx]) { visited[idx] = true; toBeProcessed.Push(n); }
}
if (process.Y - 1 >= 0)
{
n = new Point(process.X, process.Y - 1); idx = n.X + nWidth * n.Y;
if (!visited[idx]) { visited[idx] = true; toBeProcessed.Push(n); }
}
if (process.Y + 1 < nHeight)
{
n = new Point(process.X, process.Y + 1); idx = n.X + nWidth * n.Y;
if (!visited[idx]) { visited[idx] = true; toBeProcessed.Push(n); }
}
}
//finaly set a rectangle.
Rectangle r = new Rectangle(minX * 10, minY * 10, (maxX - minX + 1) * 10, (maxY - minY + 1) * 10);
rec.Add(r);
//got the rectangle now i'll do whatever i want with that.
//notify i scaled everything by x10 becuse i want to apply the changes on the originl 1920x1080 image.
}
p1 += bytesPerPixel;
p2 += bytesPerPixel;
}
base1 += stride1;
base2 += stride2;
}
bmp.UnlockBits(bmData1);
bmp2.UnlockBits(bmData2);
return rec;
}
This is my call:
private void Start()
{
full1 = GetDesktopImage();//the first,intial screen.
while (true)
{
full2 = GetDesktopImage();
a = new Bitmap(full1, 192, 108);//resizing for faster processing the images.
b = new Bitmap(full2, 192, 108); // resizing for faster processing the images.
CodeImage(a, b);
count++; // counter for the performance.
full1 = full2; // assign old to current bitmap.
}
}
However, after all the tricks and techniques I used, the algorithm runs quite slow... on my machine - Intel i5 4670k 3.4ghz - it runs only 20 times (at the maximum! It might get lower)! It maybe sounds fast (don't forget I have to send each changed area over the network after), but I'm looking to achieve more processed image per second. I think the main bottleneck is in the resizing of the 2 images - but I just thought it would be even faster after resizing - because it would have to loop through less pixels... 192*108=200,000 only..
I would appreciate any help, any improvement. Thanks.
I have the request that crop image white space in C#, and I search some methods from the forum, but it could not satisfy my request.
There is the original image,
This is the result I expect,
Any help are appreciate.
You can try to get first image data(There is an image), and draw the data into a new image. Try this method. Hope it can help you.
private static Bitmap ImageTrim(Bitmap img)
{
//get image data
BitmapData bd= img.LockBits(new Rectangle(Point.Empty, img.Size),
ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);
int[] rgbValues = new int[img.Height * img.Width];
Marshal.Copy(bd.Scan0, rgbValues, 0, rgbValues.Length);
img.UnlockBits(bd);
#region determine bounds
int left = bd.Width;
int top = bd.Height;
int right = 0;
int bottom = 0;
//determine top
for (int i = 0; i < rgbValues.Length; i++)
{
int color = rgbValues[i] & 0xffffff;
if (color != 0xffffff)
{
int r = i / bd.Width;
int c = i % bd.Width;
if (left > c)
{
left = c;
}
if (right < c)
{
right = c;
}
bottom = r;
top = r;
break;
}
}
//determine bottom
for (int i = rgbValues.Length - 1; i >= 0; i--)
{
int color = rgbValues[i] & 0xffffff;
if (color != 0xffffff)
{
int r = i / bd.Width;
int c = i % bd.Width;
if (left > c)
{
left = c;
}
if (right < c)
{
right = c;
}
bottom = r;
break;
}
}
if (bottom > top)
{
for (int r = top + 1; r < bottom; r++)
{
//determine left
for (int c = 0; c < left; c++)
{
int color = rgbValues[r * bd.Width + c] & 0xffffff;
if (color != 0xffffff)
{
if (left > c)
{
left = c;
break;
}
}
}
//determine right
for (int c = bd.Width - 1; c > right; c--)
{
int color = rgbValues[r * bd.Width + c] & 0xffffff;
if (color != 0xffffff)
{
if (right < c)
{
right = c;
break;
}
}
}
}
}
int width = right - left + 1;
int height = bottom - top + 1;
#endregion
//copy image data
int[] imgData = new int[width * height];
for (int r = top; r <= bottom; r++)
{
Array.Copy(rgbValues, r * bd.Width + left, imgData, (r - top) * width, width);
}
//create new image
Bitmap newImage = new Bitmap(width, height, PixelFormat.Format32bppArgb);
BitmapData nbd
= newImage.LockBits(new Rectangle(0, 0, width, height),
ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb);
Marshal.Copy(imgData, 0, nbd.Scan0, imgData.Length);
newImage.UnlockBits(nbd);
return newImage;
}
If your image only has 2 colors (white and black), you can iterate through your image and find the top left pixel set and the bottom right pixel set, then you can crop it:
(pseudo code, depends on what you use to get your image pixels)
int minX = int.MaxValue, maxX = 0, minY = int.MaxValue, maxY = 0;
for (x = 0; x < image.Width, x++)
{
for (y = 0; y < image.Height; y++)
{
if (image[x, y] == 1)
{
if (x < minX) minX = x;
else if (x > maxX) maxX = x;
if (y < minY) minY = y;
else if (y > maxY) maxY = y;
}
}
}
then you'll have the coordinates that will let you crop the image
I'm sure this could be optimized but that's the general idea