Skip to content

Commit 5e4f530

Browse files
committed
修正图片左右和上下翻转的问题,并增加对应测试用例。
1 parent ba8f0b0 commit 5e4f530

File tree

9 files changed

+525
-12
lines changed

9 files changed

+525
-12
lines changed

data/img001.bmp

174 KB
Binary file not shown.

src/TensorFlowNET.Core/APIs/tf.image.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -339,6 +339,13 @@ public Tensor decode_image(Tensor contents, int channels = 0, TF_DataType dtype
339339
=> image_ops_impl.decode_image(contents, channels: channels, dtype: dtype,
340340
name: name, expand_animations: expand_animations);
341341

342+
public Tensor encode_png(Tensor contents, string name = null)
343+
=> image_ops_impl.encode_png(contents, name: name);
344+
345+
public Tensor encode_jpeg(Tensor contents, string name = null)
346+
=> image_ops_impl.encode_jpeg(contents, name: name);
347+
348+
342349
/// <summary>
343350
/// Convenience function to check if the 'contents' encodes a JPEG image.
344351
/// </summary>

src/TensorFlowNET.Core/APIs/tf.io.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ limitations under the License.
1616

1717
using System.Collections.Generic;
1818
using Tensorflow.IO;
19+
using Tensorflow.Operations;
1920

2021
namespace Tensorflow
2122
{
@@ -46,6 +47,12 @@ public Operation save_v2(Tensor prefix, string[] tensor_names,
4647
public Tensor[] restore_v2(Tensor prefix, string[] tensor_names,
4748
string[] shape_and_slices, TF_DataType[] dtypes, string name = null)
4849
=> ops.restore_v2(prefix, tensor_names, shape_and_slices, dtypes, name: name);
50+
51+
public Operation write_file(string filename, Tensor conentes, string name = null)
52+
=> write_file(Tensorflow.ops.convert_to_tensor(filename, TF_DataType.TF_STRING), conentes, name);
53+
54+
public Operation write_file(Tensor filename, Tensor conentes, string name = null)
55+
=> gen_ops.write_file(filename, conentes, name);
4956
}
5057

5158
public GFile gfile = new GFile();

src/TensorFlowNET.Core/Keras/Layers/ILayersApi.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,12 @@ public ILayer Conv1D(int filters,
5555
string kernel_initializer = "glorot_uniform",
5656
string bias_initializer = "zeros");
5757

58+
public ILayer Conv2D(int filters,
59+
Shape kernel_size = null,
60+
Shape strides = null,
61+
string padding = "valid"
62+
);
63+
5864
public ILayer Conv2D(int filters,
5965
Shape kernel_size = null,
6066
Shape strides = null,

src/TensorFlowNET.Core/Operations/image_ops_impl.cs

Lines changed: 32 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,10 @@ internal static Operation[] _CheckAtLeast3DImage(Tensor image, bool require_stat
102102
{
103103
throw new ValueError("\'image\' must be fully defined.");
104104
}
105-
var dims = image_shape["-3:"];
105+
var dims = new Shape(new[] {
106+
image_shape.dims[image_shape.dims.Length - 3],
107+
image_shape.dims[image_shape.dims.Length - 2],
108+
image_shape.dims[image_shape.dims.Length - 1]});
106109
foreach (var dim in dims.dims)
107110
{
108111
if (dim == 0)
@@ -112,16 +115,18 @@ internal static Operation[] _CheckAtLeast3DImage(Tensor image, bool require_stat
112115
}
113116

114117
var image_shape_last_three_elements = new Shape(new[] {
115-
image_shape.dims[image_shape.dims.Length - 1],
118+
image_shape.dims[image_shape.dims.Length - 3],
116119
image_shape.dims[image_shape.dims.Length - 2],
117-
image_shape.dims[image_shape.dims.Length - 3]});
120+
image_shape.dims[image_shape.dims.Length - 1]});
118121
if (!image_shape_last_three_elements.IsFullyDefined)
119122
{
120123
Tensor image_shape_ = array_ops.shape(image);
121-
var image_shape_return = tf.constant(new[] {
122-
image_shape_.dims[image_shape.dims.Length - 1],
123-
image_shape_.dims[image_shape.dims.Length - 2],
124-
image_shape_.dims[image_shape.dims.Length - 3]});
124+
var image_shape_return = tf.slice(image_shape_, new[] { Math.Max(image_shape.dims.Length - 3, 0) }, new[] { 3 });
125+
126+
//var image_shape_return = tf.constant(new[] {
127+
// image_shape_.dims[image_shape_.dims.Length - 3],
128+
// image_shape_.dims[image_shape_.dims.Length - 2],
129+
// image_shape_.dims[image_shape_.dims.Length - 1]});
125130

126131
return new Operation[] {
127132
check_ops.assert_positive(
@@ -209,10 +214,10 @@ internal static Tensor _random_flip(Tensor image, int flip_index, int seed, stri
209214
}
210215

211216
public static Tensor flip_left_right(Tensor image)
212-
=> _flip(image, 0, "flip_left_right");
217+
=> _flip(image, 1, "flip_left_right");
213218

214219
public static Tensor flip_up_down(Tensor image)
215-
=> _flip(image, 1, "flip_up_down");
220+
=> _flip(image, 0, "flip_up_down");
216221

217222
internal static Tensor _flip(Tensor image, int flip_index, string scope_name)
218223
{
@@ -223,11 +228,11 @@ internal static Tensor _flip(Tensor image, int flip_index, string scope_name)
223228
Shape shape = image.shape;
224229
if (shape.ndim == 3 || shape.ndim == Unknown)
225230
{
226-
return fix_image_flip_shape(image, gen_array_ops.reverse(image, ops.convert_to_tensor(new int[] { flip_index })));
231+
return fix_image_flip_shape(image, gen_array_ops.reverse_v2(image, ops.convert_to_tensor(new int[] { flip_index })));
227232
}
228233
else if (shape.ndim == 4)
229234
{
230-
return gen_array_ops.reverse_v2(image, ops.convert_to_tensor(new[] { (flip_index + 1) % 2 }));
235+
return gen_array_ops.reverse_v2(image, ops.convert_to_tensor(new[] { flip_index + 1 }));
231236
}
232237
else
233238
{
@@ -2047,6 +2052,22 @@ internal static (Tensor, Tensor) non_max_suppression_padded_v1(Tensor boxes, Ten
20472052
});
20482053
}
20492054

2055+
public static Tensor encode_jpeg(Tensor contents, string name = null)
2056+
{
2057+
return tf_with(ops.name_scope(name, "encode_jpeg"), scope =>
2058+
{
2059+
return gen_ops.encode_jpeg(contents, name:name);
2060+
});
2061+
}
2062+
2063+
public static Tensor encode_png(Tensor contents, string name = null)
2064+
{
2065+
return tf_with(ops.name_scope(name, "encode_png"), scope =>
2066+
{
2067+
return gen_ops.encode_png(contents, name: name);
2068+
});
2069+
}
2070+
20502071
public static Tensor is_jpeg(Tensor contents, string name = null)
20512072
{
20522073
return tf_with(ops.name_scope(name, "is_jpeg"), scope =>

src/TensorFlowNET.Keras/Layers/LayersApi.cs

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,28 @@ public ILayer Conv1D(int filters,
112112
KernelInitializer = GetInitializerByName(kernel_initializer),
113113
BiasInitializer = GetInitializerByName(bias_initializer)
114114
});
115-
115+
public ILayer Conv2D(int filters,
116+
Shape kernel_size = null,
117+
Shape strides = null,
118+
string padding = "valid")
119+
=> new Conv2D(new Conv2DArgs
120+
{
121+
Rank = 2,
122+
Filters = filters,
123+
KernelSize = (kernel_size == null) ? (5, 5) : kernel_size,
124+
Strides = strides == null ? (1, 1) : strides,
125+
Padding = padding,
126+
DataFormat = null,
127+
DilationRate = (1, 1),
128+
Groups = 1,
129+
UseBias = false,
130+
KernelRegularizer = null,
131+
KernelInitializer =tf.glorot_uniform_initializer,
132+
BiasInitializer = tf.zeros_initializer,
133+
BiasRegularizer = null,
134+
ActivityRegularizer = null,
135+
Activation = keras.activations.Linear,
136+
});
116137
/// <summary>
117138
/// 2D convolution layer (e.g. spatial convolution over images).
118139
/// This layer creates a convolution kernel that is convolved with the layer input to produce a tensor of outputs.

test/TensorFlowNET.Graph.UnitTest/ImageTest.cs

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using Tensorflow;
55
using static Tensorflow.Binding;
66
using System;
7+
using System.IO;
78

89
namespace TensorFlowNET.UnitTest
910
{
@@ -164,5 +165,94 @@ public void TestCropAndResize()
164165
Assert.AreEqual(result.size, 16ul);
165166
Assert.AreEqual(result[0, 0, 0, 0], 12f);
166167
}
168+
169+
[TestMethod]
170+
public void ImageSaveTest()
171+
{
172+
var imgPath = TestHelper.GetFullPathFromDataDir("img001.bmp");
173+
var jpegImgPath = TestHelper.GetFullPathFromDataDir("img001.jpeg");
174+
var pngImgPath = TestHelper.GetFullPathFromDataDir("img001.png");
175+
176+
File.Delete(jpegImgPath);
177+
File.Delete(pngImgPath);
178+
179+
var contents = tf.io.read_file(imgPath);
180+
var bmp = tf.image.decode_image(contents);
181+
Assert.AreEqual(bmp.name, "decode_image/DecodeImage:0");
182+
183+
var jpeg = tf.image.encode_jpeg(bmp);
184+
var op1 = tf.io.write_file(jpegImgPath, jpeg);
185+
186+
var png = tf.image.encode_png(bmp);
187+
var op2 = tf.io.write_file(pngImgPath, png);
188+
189+
this.session().run(op1);
190+
this.session().run(op2);
191+
192+
Assert.IsTrue(File.Exists(jpegImgPath), "not find file:" + jpegImgPath);
193+
Assert.IsTrue(File.Exists(pngImgPath), "not find file:" + pngImgPath);
194+
195+
// 如果要测试图片正确性,需要注释下面两行代码
196+
File.Delete(jpegImgPath);
197+
File.Delete(pngImgPath);
198+
}
199+
200+
[TestMethod]
201+
public void ImageFlipTest()
202+
{
203+
var imgPath = TestHelper.GetFullPathFromDataDir("img001.bmp");
204+
205+
var contents = tf.io.read_file(imgPath);
206+
var bmp = tf.image.decode_image(contents);
207+
208+
// 左右翻转
209+
var lrImgPath = TestHelper.GetFullPathFromDataDir("img001_lr.png");
210+
File.Delete(lrImgPath);
211+
212+
var lr = tf.image.flip_left_right(bmp);
213+
var png = tf.image.encode_png(lr);
214+
var op = tf.io.write_file(lrImgPath, png);
215+
this.session().run(op);
216+
217+
Assert.IsTrue(File.Exists(lrImgPath), "not find file:" + lrImgPath);
218+
219+
// 上下翻转
220+
var updownImgPath = TestHelper.GetFullPathFromDataDir("img001_updown.png");
221+
File.Delete(updownImgPath);
222+
223+
var updown = tf.image.flip_up_down(bmp);
224+
var pngupdown = tf.image.encode_png(updown);
225+
var op2 = tf.io.write_file(updownImgPath, pngupdown);
226+
this.session().run(op2);
227+
Assert.IsTrue(File.Exists(updownImgPath));
228+
229+
230+
// 暂时先人工观测图片是否翻转,观测时需要删除下面这两行代码
231+
File.Delete(lrImgPath);
232+
File.Delete(updownImgPath);
233+
234+
// 多图翻转
235+
// 目前直接通过 bmp 拿到 shape ,这里先用默认定义图片大小来构建了
236+
var mImg = tf.stack(new[] { bmp, lr }, axis:0);
237+
print(mImg.shape);
238+
239+
var up2 = tf.image.flip_up_down(mImg);
240+
241+
var updownImgPath_m1 = TestHelper.GetFullPathFromDataDir("img001_m_ud.png"); // 直接上下翻转
242+
File.Delete(updownImgPath_m1);
243+
244+
var img001_updown_m2 = TestHelper.GetFullPathFromDataDir("img001_m_lr_ud.png"); // 先左右再上下
245+
File.Delete(img001_updown_m2);
246+
247+
var png2 = tf.image.encode_png(up2[0]);
248+
tf.io.write_file(updownImgPath_m1, png2);
249+
250+
png2 = tf.image.encode_png(up2[1]);
251+
tf.io.write_file(img001_updown_m2, png2);
252+
253+
// 如果要测试图片正确性,需要注释下面两行代码
254+
File.Delete(updownImgPath_m1);
255+
File.Delete(img001_updown_m2);
256+
}
167257
}
168258
}

0 commit comments

Comments
 (0)