2011-02-01 23 views
6

Tengo una cámara y estoy leyendo las imágenes en tiempo real en una matriz. Estoy aplicando algún algoritmo a la imagen y mostrándola. Luego obtengo la siguiente imagen y la visualizo también. Así que estoy transmitiendo imágenes de la cámara a la pantalla. Sin embargo, también quiero guardar imágenes en el disco duro una vez que las haya mostrado. Intenté usar el hilo principal pero todo se ralentizó demasiado. Luego intenté usar ThreadPool (ver código a continuación). Esto no ralentiza la visualización, pero he encontrado que las imágenes no se están guardando correctamente. Parece que no están en el orden esperado y después de que se han guardado unas 50 imágenes, los datos de imagen subsiguientes parecen distorsionados. Supongo que se están iniciando demasiados hilos.¿Cómo guardo los archivos en el disco duro en un hilo separado?

¿Hay una mejor manera de hacerlo? Creo que solo necesito un hilo para guardar las imágenes. Tal vez algún tipo de cola que guarda cada imagen secuencialmente. Siempre que se haga en segundo plano y no ralentice la pantalla. Si alguien pudiera publicar un fragmento de código sería fantástico.

short[] image1 = new short[20000]; 
while(streaming) 
{ 
    ReadImageFromCamera(ref image1) 
    ImageData data;  

    data.fileName = imageNumber; 
    data.image = image1; 

    ThreadPool.QueueUserWorkItem(WriteImageToFile, data); // Send the writes to the queue 
} 


private void WriteImageToFile(object imageData) { 

    try { 
     ImageData data = (ImageData)imageData; 
     System.Runtime.Serialization.Formatters.Binary.BinaryFormatter bf = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter(); 

     string fName = myDirectory + @"/" + Convert.ToString(data.fileName) + @".spe"; 

     using (Stream myStream = new FileStream(fName, FileMode.Create)) { 
      bf.Serialize(myStream, data.image); 
     } 
    } 
    catch (Exception) { } 
} 
+0

corto [] imagen1 = nuevo corto [20000]; Estoy gessing, estás seguro de que tu imagen no toma más de esos 40000 bytes. –

+0

¿'ReadImageFromCamera' copia los datos en la matriz que pasa, o crea una nueva matriz que está asignada a' image1'? –

+0

Sí, lo siento, solo por ilustración. Las imágenes varían en tamaño dependiendo de la configuración del usuario (pero no una vez que la transmisión ha comenzado). Estoy leyendo todos los datos de la imagen, así que no hay problemas allí. – Doug

Respuesta

0

Al tratar con hilos, el orden ya no está bajo su control. El grupo de subprocesos puede elegir programar los subprocesos en el orden que desee. Si necesita que las cosas sucedan secuencialmente en un orden específico, el enhebrado no tiene mucho sentido de todos modos.

En cuanto a las imágenes dañadas, parece que se está pasando la instancia short[] image1. No está claro qué sucede dentro de ReadImageFromCamera, pero dado que pasa una matriz preinicializada, es probable que el método use esa matriz y simplemente copie datos en ella (aunque la palabra clave ref indica que podría crear una matriz nueva) instancia y asignar eso en su lugar). Luego pasa esa instancia de matriz al WriteImageToFile en un hilo separado.

Mientras tanto, en paralelo, obtienes la siguiente imagen. Ahora tiene un escenario donde ReadImageFromCamera puede escribir datos en la matriz al mismo tiempo que WriteImageToFile almacena los datos en el disco. Ahí tienes tu imagen corrupta. Esto se puede evitar mediante el paso de una nueva instancia de matriz a WriteImageToFile:

ReadImageFromCamera(ref image1) 
ImageData data;  

data.fileName = imageNumber; 
data.image = (short[])image1.Clone(); // create a new array instance, so that 
             // the next call to ReadImageFromCamera 
             // will not corrupt the data 

ThreadPool.QueueUserWorkItem(WriteImageToFile, data); 

embargo, as has been mentioned by Al Kepp, ya que sólo tiene un disco duro, el lanzamiento de muchos hilos puede no ser la mejor opción en este caso. Podría considerar tener un hilo separado de larga ejecución para almacenar datos en el disco y colocar las imágenes en algún tipo de cola del que el hilo de almacenamiento toma datos y escribe en el disco. Esto viene con su propio conjunto de problemas relacionados con la concurrencia, lo que limita el tamaño de la cola y lo que no.

+0

brlliant gracias por eso. – Doug

+0

Preferiría hacer una nueva matriz corta [] en lugar de llamar a Clone(). Me refiero a crear un nuevo corto [20000] justo antes de llamar a ReadImageFromCamera(). ¿No es Clone() innecesariamente lento para este escenario? ¿Por qué tendríamos que hacer un clon cuando vamos a tirar el original justo después de eso? –

6

Creo que debe evitar el inicio de un nuevo hilo para cada imagen en particular. Como tiene un solo disco duro y almacena todos los archivos en el único directorio, debe usar solo un hilo de escritor de disco. Entonces, recomendaría utilizar una cola simultánea para transferir trabajos desde el hilo de la cámara al hilo del escritor del disco. No muestro el "fragmento de código" porque no se puede escribir con buena calidad en unas líneas de código.

También definitivamente debe colocar en algún lugar 'nuevo corto [20000]' para cada imagen, de lo contrario, se sobrescribe con la imagen siguiente antes de guardarlo en el disco.

Además, esperaría que sea suficiente para escribir archivos en el hilo principal, porque Windows usa técnicas simultáneas (principalmente caché de disco) automáticamente cuando escribe datos en el disco. ¿Estás seguro de que tu hardware es lo suficientemente rápido como para escribir todos esos datos en tiempo real?

+0

Intenté escribir datos en el disco en el hilo principal pero ralentizó notablemente la pantalla. La solución de enhebrado no ralentiza la pantalla – Doug

0

Debe crear un búfer distinto para el hilo desde el que leer datos, de lo contrario, el hilo principal lo sobreescribirá al volcarlo en un archivo. La forma en que lo hace parece copiar solo referencias (en particular).

Así:

ThreadPool.QueueUserWorkItem(WriteImageToFile, data); 

en lugar de los datos que usted envíe una copia profunda de data. Como parece que ya lo está haciendo, pero en el hilo de trabajo, solo necesita mover la copia antes de enviándola.

HTH

+2

Una copia superficial será suficiente. Como los elementos son de un tipo de valor inmutable, no hay forma de que se modifiquen de forma tal que comprometa una referencia de matriz clonada. –

+0

@Fredrik: copia superficial es suficiente. +1 Pero la razón no es la inmutabilidad de los elementos, porque la matriz en su conjunto no es inmutable. Simplemente se deja intacto en este escenario: solo se muestra y se guarda en el disco, por lo que no hay razón para hacer una copia en profundidad. –

+0

@Fredrik si una copia superficial es suficiente, ¿por qué la clona en su respuesta? – Simone

0

Usted tiene que comprobar antes de pensar en las discusiones si la velocidad de un disco normal será suficiente para su tarea, ya que puede crear imágenes más rápido que escribir en el disco. Si la creación de imágenes es más rápida que la escritura, lo vería con un disco de memoria, pero luego debe calcular si el tamaño es suficiente hasta que detenga la cámara, de modo que pueda escribir en el disco normal durante la noche.
Si usa .NET 4.0, le sugiero que use Concurrent queue junto con un hilo normal (ya que el hilo se ejecutará hasta que el programa finalice).

0

Puede hacer lo siguiente.

public class AsyncFileWriter 
    { 
     private readonly FileStream fs; 
     private readonly AsyncCallback callback; 
     public Action FinishedCallback; 
     private IAsyncResult result; 
     private class AsyncState 
     { 
      public FileStream Fs; 

     } 

     private void WriteCore(IAsyncResult ar) 
     { 
      if (result != null) 
      { 
       FileStream stream = ((AsyncState)ar.AsyncState).Fs; 
       stream.EndWrite(result); 
       if (this.FinishedCallback != null) 
       { 
        FinishedCallback(); 
       } 
      } 
     } 

     public AsyncFileWriter(FileStream fs, Action finishNotification) 
     { 
      this.fs = fs; 
      callback = new AsyncCallback(WriteCore); 
      this.FinishedCallback = finishNotification; 
     } 

     public AsyncFileWriter(FileStream fs) 
      : this(fs, null) 
     { 

     } 


     public void Write(Byte[] data) 
     { 
      result = fs.BeginWrite(data, 0, data.Length, callback, new AsyncState() { Fs = fs }); 
     } 
    } 

Posteriormente puede consumirlo como.

static void Main(string[] args) 
     { 
      FileStream fs = File.Create("D:\\ror.txt"); 
      ManualResetEvent evt = new ManualResetEvent(false); 
      AsyncFileWriter writer = new AsyncFileWriter(fs,() => 
                   { 
                    Console.Write("Write Finished"); 
                    evt.Set(); 
                   } 

       ); 
      byte[] bytes = File.ReadAllBytes("D:\\test.xml");//Getting some random bytes 

      writer.Write(bytes); 
      evt.WaitOne(); 
      Console.Write("Write Done"); 
     } 
0

manera rápida y sucia está empezando nuevo hilo individual y el trabajo con los miembros de clase mundial - el nuevo hilo debe ser capaz de acceder a ellos, mientras que el hilo principal actualizará ellos.

En primer lugar, tienen estas líneas fuera de cualquier función:

private List<ImageData> arrGlobalData = new List<ImageData>(); 
private bool keepWritingImages = true; 

Ahora cambiar el código en el hilo "principal" a esto:

short[] image1 = new short[20000]; 
ThreadPool.QueueUserWorkItem(WriteImageToFile, null); 
while(streaming) 
{ 
    ReadImageFromCamera(ref image1) 
    ImageData data = new ImageData(); 
    data.fileName = imageNumber; 
    data.image = image1; 
    arrGlobalData.Add(data); 
} 
keepWritingImages = false; 

Y, por último dispone de esta función para la nuevo hilo:

private void WriteImageToFile(object imageData) 
{ 
    while (keepWritingImages) 
    { 
     if (arrGlobalData.Count > 0) 
     { 
      ImageData data = arrGlobalData[0]; 
      try 
      { 
       System.Runtime.Serialization.Formatters.Binary.BinaryFormatter bf = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter(); 
       string fName = myDirectory + @"/" + Convert.ToString(data.fileName) + @".spe"; 
       using (Stream myStream = new FileStream(fName, FileMode.Create)) 
       { 
        bf.Serialize(myStream, data.image); 
       } 
      } 
      catch 
      { 
      } 
      finally 
      { 
       arrGlobalData.Remove(data); 
      } 
     } 

     Thread.Sleep(10); 
    } 
} 
+1

No puedo creer lo bueno que es este sitio. Es fantástico, gracias por todas las respuestas. – Doug

+0

No puedo creer lo bueno que es este sitio. Es fantástico, gracias por todas las respuestas. La respuesta de Frederick Mork parece haber resuelto todo. data.image = (short []) image1.Clone(); – Doug

+0

Shadow Wizard Voy a probar tu código ya que estoy un poco preocupado QueueUserWorkItem usará demasiados hilos en algún momento. – Doug

Cuestiones relacionadas