This is nothing.AlgorithmStunt. Not worth mentioning.
In fact, this is a logic of the layout of medical image film exposure.
The third part of the DICOM standard mainly refers to the description of the IOD definition on the 166th page:
Table C.13.5-1 image box pixel description component
Attribute name tag description
Image Position (2020,0010) the image position of the film based on the Image Display format (2010,0010.
See c.13.5.1 specifications.
What does this so-called "Image Position of Film Based on Image Display format ()" mean? Take an instance as usual:
20 20 10 00 ..............
00000010 02 00 00 00 02 00
20 20 10 00 ..............
00000010 02 00 00 00 03 00
The structure of DICOM data elements is not much said. For details, see DICOM standard. Only the last two digits show that the so-called "Image Position of films based on the Image Display format (2010,0010)" is the sorting from top to bottom. When a film exposure task is sent from a Device workstation based on DICOM protocol image transmission, it is sent in this "sequence" mode.
Let's look at the third part of the DICOM standard section, page 1:
C.13.5.1 Image Position
The position of the image in the film; the encoding of the Image Position sequence is based on the selected Image Display format
(2010,0010 ). The image position sequence increments by 1.
The image position (2020,0010) is defined as follows:
-Standard Display format: the sequence of the image box is the main line sequence (from left to right or from top to bottom); the position of the left image is equal to 1.
-Row display format: the sequence of the image box is the main line sequence (from left to right or from top to bottom); the position of the left image is equal to 1.
-Column Display format: the sequence of the image box is the main column sequence (from left to right or from top to bottom); the position of the left image is equal to 1.
-Slide display format: the sequence of the image box is the main line sequence (from left to right or from top to bottom); the position of the left image is equal to 1.
-Superslide display format: the sequence of the image box is the main line sequence (from left to right or from top to bottom); the position of the left image is equal to 1.
For details about several imgbox methods, we can skip the first method, because more than 90% of the results I encountered were "Standard Display format". When createfilmbox was created when the film was exposed to DIMSE, a parameter command element 0x20100010 was received, the value is similar to the standard \ Standard, which indicates that this is a "Standard Display format" separated by commas (,). The first is the number of columns, and the second is the number of rows. The standard display format is: (arrange the image from left to right in a grid with a specified number of rows and columns)
In the end, what we need to achieve is to splice the sequentially numbered image into a whole mesh Image Based on DICOM standards so that it can print out what you see when reading a piece with the workstation, you can obtain the specified image by number. You can specify the rows and columns of the Image Based on the number. You can obtain the number of the rows and columns of the row based on the number. This is simple.
For simple analysis, we can use standard \ colnum and rownum to determine the number of rows and columns. This is easy.
He is a "continuous stack process"
The first line is full of heap and the second line is continuously heap from left to right.
In column X, column Y indicates that the row starts from 1, and num indicates that the number starts from 1. The following conclusion can be drawn:
For (1 to num)
{
If (Num % colnum! = 0) // do not divide
{
Y = num/colnun + 1;
X = num % colnum;
Else
{
Y = num/colnun;
X = colnum;
}
}
Can you understand this process? What can I do? You can verify it:
Num = 7
7% 3! = 0
{
Y = 7/3 + 1 => 3
X = 7% 3 => 1
}
Num = 6
6% 3 = 0
{
Y = 6/3 => 2
X = 3 => 3
}
Num = 5
5% 3! = 0
{
Y = 5/3 + 1 => 3
X = 5% 3 => 2
}
Why do we need to determine X and Y, because we need to locate a single image in the output, that is, X and Y are available in the upper left corner. An array of images can be used to store the Received images. They are arranged in ascending order of numbers.
Solved a problem
There is another problem with image scaling.
If I assume that the output area is width = 297 Height = 420. The film is divided into four rows and three columns. Therefore, the size of a single image is width = 297/3 Height = 420/4, but the size of the image in each shard is usually not fixed, just like a common image-watching software has a display ratio called "Scaling to the display area". What we need to do is to use this process to describe it:
Pre-set the image to have two proportional zooming size and height alignment (to make the height equal to the visible area) width alignment (to make the width equal to the visible area ).
Here is a simple proportional formula before and after scaling
Width alignment:
Display area width (scaled width)/source image width = scaled height/source Image Height
In case of high alignment:
Display area height (scaled height)/source Image Height = scaled width/source Image Width
Obtain the length of the other side in two cases.
Display area width (scaled width) * source Image Height/source image width = scaled height
Display area height (scaled height) * Source image width/source Image Height = scaled width
Then, the two conditions are determined. If the scaled image area exceeds the display range, the image is rejected.
For example, the blue box indicates the image, and the black box indicates the display area:
In this way, proportional scaling is achieved to adapt to the display range. Simple.
How can we integrate the above ideas and implement them using code? First, we need to define a class named paper, which is equivalent to a film and is in the area to be printed.
Then, variables such as the number of rows and columns to display the area height and width are required, and an image array is also used to store the images with sequential numbers.
1 Public Class Paper 2 { 3 Int Row, Col; // Number of rows and columns 4 Int Width, height; // Film width and height 5 Ilist <image> images; // Sequential number of images 6 7 Public Paper ( Int _ Width,Int _ Height, Int _ Row, Int _ Col) 8 { 9 Width = _ width; Height = _ height; ROW = _ row; Col = _ Col; 10 Images = New List <image> (row * Height ); 11 } 12 // Initialize the display area 13 Public Paper ( Int _ Row, Int _ Col) 14 { 15 // Width = 345*9; Height = 420*9; ROW = _ row; Col = _ Col; // 14inx17in 16 Width =297 * 9 ; Height = 420 * 9 ; ROW = _ row; Col = _ Col; // A3 17 Images = New List <image> (row * Height ); 18 } 19 // Add Image 20 Public Void Addimg (image IMG) 21 { 22 If (Images. Count> = row * COL) 23 Return ; 24 Else 25 { 26 Images. Add (IMG ); 27 } 28 } 29 }
This is equivalent to defining a data model framework. Initializing an instance means outputting a new film. What a wonderful object-oriented analysis design.
The materials are available. The following function is the most important function. It is used to implement all the logic of the first part of analysis [Film layout and image scaling]. Please refer to the description in the first part:
1 // Output after Layout 2 // The layout is from top to bottom from left to right 3 Public Image Layout () 4 { 5 Image layouted = New Bitmap (width, height ); 6 7 Graphics G = Graphics. fromimage (layouted ); 8 G. Clear (color. Green ); 9 For ( Int I = 0 ; I <images. Count; I ++ ) 10 { 11 Int _ Row, _ Col; 12 If (I + 1 ) % Col! = 0 ) 13 { 14 _ ROW = (I + 1 )/COL +1 - 1 ; // _ ROW = (I + 1)/COL + 1-1; 15 _ Col = (I + 1 ) % Col- 1 ; // _ Col = (I + 1)/col-1; 16 } 17 Else 18 { 19 _ ROW = (I + 1 )/Col- 1 ; 20 _ Col = col- 1 ; 21 } 22 If (_ Col < 0 ) 23 { 24 _ Col = 0 ; 25 } 26 If (_ Row < 0 ) 27 { 28 _ ROW = 0 ; 29 } 30 Graphicsunit u = Graphicsunit. pixel; 31 Rectanglef recsrc = images [I]. getbounds ( Ref U ); // Original Image Size 32 Rectanglef recdst; // Resize and adjust the offset 33 34 // Width Ratio width = the side with a longer aspect needs to be fixed to narrow down to adjust to the field of view. 35 Float Rech = (width/COL) * recsrc. Height )/ Recsrc. Width, 36 Recw = (height/row) * recsrc. width )/ Recsrc. height; 37 38 If (Recw> (width/COL )) // Adjust the height when the width exceeds the fixed width 39 Recdst = New Rectanglef (width/COL) * _ Col + 0 , 40 (Height/row) * _ row + (height/row)-rech )/ 2 , 41 (Width/ COL), rech ); 42 Else // Adjust the width when the height exceeds the fixed height 43 Recdst = New Rectanglef (width/COL) * _ Col + (width/COL)-recw )/ 2 , 44 (Height/row) * _ row +0 , 45 Recw, (height/ Row )); 46 47 G. drawimage (images [I], recdst, images [I]. getbounds ( Ref U), U ); 48 // If (I = 4) 49 Images [I]. Save (I + " Out.jpg " , System. Drawing. imaging. imageformat. JPEG ); 50 } 51 52 // Layouted. Save ("layout.jpg", system. Drawing. imaging. imageformat. JPEG ); 53 G. Dispose (); 54 55 Return Layouted; 56 }
Call:
1 Paper Film = New Paper ( 3 , 2 ); 2 Film. addimg (image. fromfile ( " 1. jpg " )); 3 Film. addimg (image. fromfile ( " 2. jpg " )); 4 Film. addimg (image. fromfile (" 3. jpg " )); 5 Film. addimg (image. fromfile ( " 4. jpg " )); 6 Film. addimg (image. fromfile ( " 5. jpg " )); 7 Film. addimg (image. fromfile ( " 6. jpg " )); 8 9 Film. layout ();
I keep saying that I want to talk about the communication of DICOM protocol. Next time, I will try again.
Download source code and test files. Click here.